/*
 *  Copyright (C) 2002-2019  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */


#ifndef DOSBOX_DOS_INC_H
#define DOSBOX_DOS_INC_H

#ifndef DOSBOX_DOS_SYSTEM_H
#include "dos_system.h"
#endif
#ifndef DOSBOX_MEM_H
#include "mem.h"
#endif

#include <stddef.h> //for offsetof

#ifdef _MSC_VER
#pragma pack (1)
#endif
struct CommandTail{
  Bit8u count;				/* number of bytes returned */
  char buffer[127];			 /* the buffer itself */
} GCC_ATTRIBUTE(packed);
#ifdef _MSC_VER
#pragma pack ()
#endif

struct DOS_Date {
	Bit16u year;
	Bit8u month;
	Bit8u day;
};

struct DOS_Version {
	Bit8u major,minor,revision;
};


#ifdef _MSC_VER
#pragma pack (1)
#endif
union bootSector {
	struct entries {
		Bit8u jump[3];
		Bit8u oem_name[8];
		Bit16u bytesect;
		Bit8u sectclust;
		Bit16u reserve_sect;
		Bit8u misc[496];
	} bootdata;
	Bit8u rawdata[512];
} GCC_ATTRIBUTE(packed);
#ifdef _MSC_VER
#pragma pack ()
#endif


enum { MCB_FREE=0x0000,MCB_DOS=0x0008 };
enum { RETURN_EXIT=0,RETURN_CTRLC=1,RETURN_ABORT=2,RETURN_TSR=3};

#define DOS_FILES 127
#define DOS_DRIVES 26
#define DOS_DEVICES 10


// dos swappable area is 0x320 bytes beyond the sysvars table
// device driver chain is inside sysvars
#define DOS_INFOBLOCK_SEG 0x80	// sysvars (list of lists)
#define DOS_CONDRV_SEG 0xa0
#define DOS_CONSTRING_SEG 0xa8
#define DOS_SDA_SEG 0xb2		// dos swappable area
#define DOS_SDA_OFS 0
#define DOS_CDS_SEG 0x108
#define DOS_FIRST_SHELL 0x118
#define DOS_MEM_START 0x16f		//First Segment that DOS can use

#define DOS_PRIVATE_SEGMENT 0xc800
#define DOS_PRIVATE_SEGMENT_END 0xd000

/* internal Dos Tables */

extern DOS_File * Files[DOS_FILES];
extern DOS_Drive * Drives[DOS_DRIVES];
extern DOS_Device * Devices[DOS_DEVICES];

extern Bit8u dos_copybuf[0x10000];


void DOS_SetError(Bit16u code);

/* File Handling Routines */

enum { STDIN=0,STDOUT=1,STDERR=2,STDAUX=3,STDPRN=4};
enum { HAND_NONE=0,HAND_FILE,HAND_DEVICE};

/* Routines for File Class */
void DOS_SetupFiles (void);
bool DOS_ReadFile(Bit16u handle,Bit8u * data,Bit16u * amount, bool fcb = false);
bool DOS_WriteFile(Bit16u handle,Bit8u * data,Bit16u * amount,bool fcb = false);
bool DOS_SeekFile(Bit16u handle,Bit32u * pos,Bit32u type,bool fcb = false);
bool DOS_CloseFile(Bit16u handle,bool fcb = false);
bool DOS_FlushFile(Bit16u handle);
bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry);
bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry);
bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate);

/* Routines for Drive Class */
bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry,bool fcb = false);
bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status);
bool DOS_CreateFile(char const * name,Bit16u attribute,Bit16u * entry, bool fcb = false);
bool DOS_UnlinkFile(char const * const name);
bool DOS_FindFirst(char *search,Bit16u attr,bool fcb_findfirst=false);
bool DOS_FindNext(void);
bool DOS_Canonicalize(char const * const name,char * const big);
bool DOS_CreateTempFile(char * const name,Bit16u * entry);
bool DOS_FileExists(char const * const name);

/* Helper Functions */
bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive);
/* Drive Handing Routines */
Bit8u DOS_GetDefaultDrive(void);
void DOS_SetDefaultDrive(Bit8u drive);
bool DOS_SetDrive(Bit8u drive);
bool DOS_GetCurrentDir(Bit8u drive,char * const buffer);
bool DOS_ChangeDir(char const * const dir);
bool DOS_MakeDir(char const * const dir);
bool DOS_RemoveDir(char const * const dir);
bool DOS_Rename(char const * const oldname,char const * const newname);
bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free);
bool DOS_GetFileAttr(char const * const name,Bit16u * attr);
bool DOS_SetFileAttr(char const * const name,Bit16u attr);

/* IOCTL Stuff */
bool DOS_IOCTL(void);
bool DOS_GetSTDINStatus();
Bit8u DOS_FindDevice(char const * name);
void DOS_SetupDevices(void);

/* Execute and new process creation */
bool DOS_NewPSP(Bit16u pspseg,Bit16u size);
bool DOS_ChildPSP(Bit16u pspseg,Bit16u size);
bool DOS_Execute(char * name,PhysPt block,Bit8u flags);
void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode);

/* Memory Handling Routines */
void DOS_SetupMemory(void);
bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks);
bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks);
bool DOS_FreeMemory(Bit16u segment);
void DOS_FreeProcessMemory(Bit16u pspseg);
Bit16u DOS_GetMemory(Bit16u pages);
bool DOS_SetMemAllocStrategy(Bit16u strat);
Bit16u DOS_GetMemAllocStrategy(void);
void DOS_BuildUMBChain(bool umb_active,bool ems_active);
bool DOS_LinkUMBsToMemChain(Bit16u linkstate);

/* FCB stuff */
bool DOS_FCBOpen(Bit16u seg,Bit16u offset);
bool DOS_FCBCreate(Bit16u seg,Bit16u offset);
bool DOS_FCBClose(Bit16u seg,Bit16u offset);
bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset);
bool DOS_FCBFindNext(Bit16u seg,Bit16u offset);
Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset, Bit16u numBlocks);
Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u numBlocks);
Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore);
Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore);
bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset);
bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset);
bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset);
void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset);
Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change);
bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters);

/* Extra DOS Interrupts */
void DOS_SetupMisc(void);

/* The DOS Tables */
void DOS_SetupTables(void);

/* Internal DOS Setup Programs */
void DOS_SetupPrograms(void);

/* Initialize Keyboard Layout */
void DOS_KeyboardLayout_Init(Section* sec);

bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3);

enum {
	KEYB_NOERROR=0,
	KEYB_FILENOTFOUND,
	KEYB_INVALIDFILE,
	KEYB_LAYOUTNOTFOUND,
	KEYB_INVALIDCPFILE
};


static INLINE Bit16u long2para(Bit32u size) {
	if (size>0xFFFF0) return 0xffff;
	if (size&0xf) return (Bit16u)((size>>4)+1);
	else return (Bit16u)(size>>4);
}


static INLINE Bit16u DOS_PackTime(Bit16u hour,Bit16u min,Bit16u sec) {
	return (hour&0x1f)<<11 | (min&0x3f) << 5 | ((sec/2)&0x1f);
}

static INLINE Bit16u DOS_PackDate(Bit16u year,Bit16u mon,Bit16u day) {
	return ((year-1980)&0x7f)<<9 | (mon&0x3f) << 5 | (day&0x1f);
}

/* Dos Error Codes */
#define DOSERR_NONE 0
#define DOSERR_FUNCTION_NUMBER_INVALID 1
#define DOSERR_FILE_NOT_FOUND 2
#define DOSERR_PATH_NOT_FOUND 3
#define DOSERR_TOO_MANY_OPEN_FILES 4
#define DOSERR_ACCESS_DENIED 5
#define DOSERR_INVALID_HANDLE 6
#define DOSERR_MCB_DESTROYED 7
#define DOSERR_INSUFFICIENT_MEMORY 8
#define DOSERR_MB_ADDRESS_INVALID 9
#define DOSERR_ENVIRONMENT_INVALID 10
#define DOSERR_FORMAT_INVALID 11
#define DOSERR_ACCESS_CODE_INVALID 12
#define DOSERR_DATA_INVALID 13
#define DOSERR_RESERVED 14
#define DOSERR_FIXUP_OVERFLOW 14
#define DOSERR_INVALID_DRIVE 15
#define DOSERR_REMOVE_CURRENT_DIRECTORY 16
#define DOSERR_NOT_SAME_DEVICE 17
#define DOSERR_NO_MORE_FILES 18
#define DOSERR_FILE_ALREADY_EXISTS 80


/* Remains some classes used to access certain things */
#define sOffset(s,m) ((char*)&(((s*)NULL)->m)-(char*)NULL)
#define sGet(s,m) GetIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m))
#define sSave(s,m,val) SaveIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m),val)

class MemStruct {
public:
	Bitu GetIt(Bitu size,PhysPt addr) {
		switch (size) {
		case 1:return mem_readb(pt+addr);
		case 2:return mem_readw(pt+addr);
		case 4:return mem_readd(pt+addr);
		}
		return 0;
	}
	void SaveIt(Bitu size,PhysPt addr,Bitu val) {
		switch (size) {
		case 1:mem_writeb(pt+addr,(Bit8u)val);break;
		case 2:mem_writew(pt+addr,(Bit16u)val);break;
		case 4:mem_writed(pt+addr,(Bit32u)val);break;
		}
	}
	void SetPt(Bit16u seg) { pt=PhysMake(seg,0);}
	void SetPt(Bit16u seg,Bit16u off) { pt=PhysMake(seg,off);}
	void SetPt(RealPt addr) { pt=Real2Phys(addr);}
protected:
	PhysPt pt;
};

class DOS_PSP :public MemStruct {
public:
	DOS_PSP(Bit16u segment)
		: seg(0)
	{
		SetPt(segment);
		seg = segment;
	}
	void	MakeNew				(Bit16u memSize);
	void	CopyFileTable		(DOS_PSP* srcpsp,bool createchildpsp);
	Bit16u	FindFreeFileEntry	(void);
	void	CloseFiles			(void);

	void	SaveVectors			(void);
	void	RestoreVectors		(void);
	void	SetSize				(Bit16u size)			{ sSave(sPSP,next_seg,size);		};
	Bit16u	GetSize				(void)					{ return (Bit16u)sGet(sPSP,next_seg);		};
	void	SetEnvironment		(Bit16u envseg)			{ sSave(sPSP,environment,envseg);	};
	Bit16u	GetEnvironment		(void)					{ return (Bit16u)sGet(sPSP,environment);	};
	Bit16u	GetSegment			(void)					{ return seg;						};
	void	SetFileHandle		(Bit16u index, Bit8u handle);
	Bit8u	GetFileHandle		(Bit16u index);
	void	SetParent			(Bit16u parent)			{ sSave(sPSP,psp_parent,parent);	};
	Bit16u	GetParent			(void)					{ return (Bit16u)sGet(sPSP,psp_parent);		};
	void	SetStack			(RealPt stackpt)		{ sSave(sPSP,stack,stackpt);		};
	RealPt	GetStack			(void)					{ return sGet(sPSP,stack);			};
	void	SetInt22			(RealPt int22pt)		{ sSave(sPSP,int_22,int22pt);		};
	RealPt	GetInt22			(void)					{ return sGet(sPSP,int_22);			};
	void	SetFCB1				(RealPt src);
	void	SetFCB2				(RealPt src);
	void	SetCommandTail		(RealPt src);	
	bool	SetNumFiles			(Bit16u fileNum);
	Bit16u	FindEntryByHandle	(Bit8u handle);
			
private:
	#ifdef _MSC_VER
	#pragma pack(1)
	#endif
	struct sPSP {
		Bit8u   exit[2];     /* CP/M-like exit poimt */
		Bit16u  next_seg;    /* Segment of first byte beyond memory allocated or program */
		Bit8u   fill_1;      /* single char fill */
		Bit8u   far_call;    /* far call opcode */
		RealPt  cpm_entry;   /* CPM Service Request address*/
		RealPt  int_22;      /* Terminate Address */
		RealPt  int_23;      /* Break Address */
		RealPt  int_24;      /* Critical Error Address */
		Bit16u  psp_parent;  /* Parent PSP Segment */
		Bit8u   files[20];   /* File Table - 0xff is unused */
		Bit16u  environment; /* Segment of evironment table */
		RealPt  stack;       /* SS:SP Save point for int 0x21 calls */
		Bit16u  max_files;   /* Maximum open files */
		RealPt  file_table;  /* Pointer to File Table PSP:0x18 */
		RealPt  prev_psp;    /* Pointer to previous PSP */
		Bit8u   interim_flag;
		Bit8u   truename_flag;
		Bit16u  nn_flags;
		Bit16u  dos_version;
		Bit8u   fill_2[14];  /* Lot's of unused stuff i can't care aboue */
		Bit8u   service[3];  /* INT 0x21 Service call int 0x21;retf; */
		Bit8u   fill_3[9];   /* This has some blocks with FCB info */
		Bit8u   fcb1[16];    /* first FCB */
		Bit8u   fcb2[16];    /* second FCB */
		Bit8u   fill_4[4];   /* unused */
		CommandTail cmdtail;
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack()
	#endif
	Bit16u	seg;
public:
	static	Bit16u rootpsp;
};

class DOS_ParamBlock:public MemStruct {
public:
	DOS_ParamBlock(PhysPt addr)
		: exec{0, 0, 0, 0, 0, 0},
		  overlay{0, 0}
	{
		pt = addr;
	}
	void Clear(void);
	void LoadData(void);
	void SaveData(void);		/* Save it as an exec block */
	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct sOverlay {
		Bit16u loadseg;
		Bit16u relocation;
	} GCC_ATTRIBUTE(packed);
	struct sExec {
		Bit16u envseg;
		RealPt cmdtail;
		RealPt fcb1;
		RealPt fcb2;
		RealPt initsssp;
		RealPt initcsip;
	}GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack()
	#endif
	sExec exec;
	sOverlay overlay;
};

class DOS_InfoBlock:public MemStruct {
public:
	DOS_InfoBlock()
		: seg(0)
	{}
	void SetLocation(Bit16u  seg);
	void SetFirstMCB(Bit16u _first_mcb);
	void SetBuffers(Bit16u x,Bit16u y);
	void SetCurDirStruct(Bit32u _curdirstruct);
	void SetFCBTable(Bit32u _fcbtable);
	void SetDeviceChainStart(Bit32u _devchain);
	void SetDiskBufferHeadPt(Bit32u _dbheadpt);
	void SetStartOfUMBChain(Bit16u _umbstartseg);
	void SetUMBChainState(Bit8u _umbchaining);
	void SetBlockDevices(Bit8u _count);
	Bit16u	GetStartOfUMBChain(void);
	Bit8u	GetUMBChainState(void);
	RealPt	GetPointer(void);
	Bit32u GetDeviceChain(void);

	#ifdef _MSC_VER
	#pragma pack(1)
	#endif
	struct sDIB {		
		Bit8u	unknown1[4];
		Bit16u	magicWord;			// -0x22 needs to be 1
		Bit8u	unknown2[8];
		Bit16u	regCXfrom5e;		// -0x18 CX from last int21/ah=5e
		Bit16u	countLRUcache;		// -0x16 LRU counter for FCB caching
		Bit16u	countLRUopens;		// -0x14 LRU counter for FCB openings
		Bit8u	stuff[6];		// -0x12 some stuff, hopefully never used....
		Bit16u	sharingCount;		// -0x0c sharing retry count
		Bit16u	sharingDelay;		// -0x0a sharing retry delay
		RealPt	diskBufPtr;		// -0x08 pointer to disk buffer
		Bit16u	ptrCONinput;		// -0x04 pointer to con input
		Bit16u	firstMCB;		// -0x02 first memory control block
		RealPt	firstDPB;		//  0x00 first drive parameter block
		RealPt	firstFileTable;		//  0x04 first system file table
		RealPt	activeClock;		//  0x08 active clock device header
		RealPt	activeCon;		//  0x0c active console device header
		Bit16u	maxSectorLength;	//  0x10 maximum bytes per sector of any block device;
		RealPt	diskInfoBuffer;		//  0x12 pointer to disk info buffer
		RealPt  curDirStructure;	//  0x16 pointer to current array of directory structure
		RealPt	fcbTable;		//  0x1a pointer to system FCB table
		Bit16u	protFCBs;		//  0x1e protected fcbs
		Bit8u	blockDevices;		//  0x20 installed block devices
		Bit8u	lastdrive;		//  0x21 lastdrive
		Bit32u	nulNextDriver;	//  0x22 NUL driver next pointer
		Bit16u	nulAttributes;	//  0x26 NUL driver aattributes
		Bit32u	nulStrategy;	//  0x28 NUL driver strategy routine
		Bit8u	nulString[8];	//  0x2c NUL driver name string
		Bit8u	joindedDrives;		//  0x34 joined drives
		Bit16u	specialCodeSeg;		//  0x35 special code segment
		RealPt  setverPtr;		//  0x37 pointer to setver
		Bit16u  a20FixOfs;		//  0x3b a20 fix routine offset
		Bit16u  pspLastIfHMA;		//  0x3d psp of last program (if dos in hma)
		Bit16u	buffers_x;		//  0x3f x in BUFFERS x,y
		Bit16u	buffers_y;		//  0x41 y in BUFFERS x,y
		Bit8u	bootDrive;		//  0x43 boot drive
		Bit8u	useDwordMov;		//  0x44 use dword moves
		Bit16u	extendedSize;		//  0x45 size of extended memory
		Bit32u	diskBufferHeadPt;	//  0x47 pointer to least-recently used buffer header
		Bit16u	dirtyDiskBuffers;	//  0x4b number of dirty disk buffers
		Bit32u	lookaheadBufPt;		//  0x4d pointer to lookahead buffer
		Bit16u	lookaheadBufNumber;		//  0x51 number of lookahead buffers
		Bit8u	bufferLocation;			//  0x53 workspace buffer location
		Bit32u	workspaceBuffer;		//  0x54 pointer to workspace buffer
		Bit8u	unknown3[11];			//  0x58
		Bit8u	chainingUMB;			//  0x63 bit0: UMB chain linked to MCB chain
		Bit16u	minMemForExec;			//  0x64 minimum paragraphs needed for current program
		Bit16u	startOfUMBChain;		//  0x66 segment of first UMB-MCB
		Bit16u	memAllocScanStart;		//  0x68 start paragraph for memory allocation
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack ()
	#endif
	Bit16u	seg;
};

class DOS_DTA:public MemStruct{
public:
	DOS_DTA(RealPt addr) { SetPt(addr); }

	void SetupSearch(Bit8u _sdrive,Bit8u _sattr,char * _pattern);
	void SetResult(const char * _name,Bit32u _size,Bit16u _date,Bit16u _time,Bit8u _attr);
	
	Bit8u GetSearchDrive(void);
	void GetSearchParams(Bit8u & _sattr,char * _spattern);
	void GetResult(char * _name,Bit32u & _size,Bit16u & _date,Bit16u & _time,Bit8u & _attr);

	void	SetDirID(Bit16u entry)			{ sSave(sDTA,dirID,entry); };
	void	SetDirIDCluster(Bit16u entry)	{ sSave(sDTA,dirCluster,entry); };
	Bit16u	GetDirID(void)				{ return (Bit16u)sGet(sDTA,dirID); };
	Bit16u	GetDirIDCluster(void)		{ return (Bit16u)sGet(sDTA,dirCluster); };
private:
	#ifdef _MSC_VER
	#pragma pack(1)
	#endif
	struct sDTA {
		Bit8u sdrive;						/* The Drive the search is taking place */
		Bit8u sname[8];						/* The Search pattern for the filename */		
		Bit8u sext[3];						/* The Search pattern for the extenstion */
		Bit8u sattr;						/* The Attributes that need to be found */
		Bit16u dirID;						/* custom: dir-search ID for multiple searches at the same time */
		Bit16u dirCluster;					/* custom (drive_fat only): cluster number for multiple searches at the same time */
		Bit8u fill[4];
		Bit8u attr;
		Bit16u time;
		Bit16u date;
		Bit32u size;
		char name[DOS_NAMELENGTH_ASCII];
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack()
	#endif
};

class DOS_FCB: public MemStruct {
public:
	DOS_FCB(Bit16u seg,Bit16u off,bool allow_extended=true);
	void Create(bool _extended);
	void SetName(Bit8u _drive,char * _fname,char * _ext);
	void SetSizeDateTime(Bit32u _size,Bit16u _date,Bit16u _time);
	void GetSizeDateTime(Bit32u & _size,Bit16u & _date,Bit16u & _time);
	void GetName(char * fillname);
	void FileOpen(Bit8u _fhandle);
	void FileClose(Bit8u & _fhandle);
	void GetRecord(Bit16u & _cur_block,Bit8u & _cur_rec);
	void SetRecord(Bit16u _cur_block,Bit8u _cur_rec);
	void GetSeqData(Bit8u & _fhandle,Bit16u & _rec_size);
	void SetSeqData(Bit8u _fhandle,Bit16u _rec_size);
	void GetRandom(Bit32u & _random);
	void SetRandom(Bit32u  _random);
	Bit8u GetDrive(void);
	bool Extended(void);
	void GetAttr(Bit8u & attr);
	void SetAttr(Bit8u attr);
	void SetResult(Bit32u size,Bit16u date,Bit16u time,Bit8u attr);
	bool Valid(void);
	void ClearBlockRecsize(void);
private:
	bool extended;
	PhysPt real_pt;
	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct sFCB {
		Bit8u drive;			/* Drive number 0=default, 1=A, etc */
		Bit8u filename[8];		/* Space padded name */
		Bit8u ext[3];			/* Space padded extension */
		Bit16u cur_block;		/* Current Block */
		Bit16u rec_size;		/* Logical record size */
		Bit32u filesize;		/* File Size */
		Bit16u date;
		Bit16u time;
		/* Reserved Block should be 8 bytes */
		Bit8u sft_entries;
		Bit8u share_attributes;
		Bit8u extra_info;
		/* Maybe swap file_handle and sft_entries now that fcbs 
		 * aren't stored in the psp filetable anymore */
		Bit8u file_handle;
		Bit8u reserved[4];
		/* end */
		Bit8u  cur_rec;			/* Current record in current block */
		Bit32u rndm;			/* Current relative record number */
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack ()
	#endif
};

class DOS_MCB : public MemStruct{
public:
	DOS_MCB(Bit16u seg) { SetPt(seg); }
	void SetFileName(char const * const _name) { MEM_BlockWrite(pt+offsetof(sMCB,filename),_name,8); }
	void GetFileName(char * const _name) { MEM_BlockRead(pt+offsetof(sMCB,filename),_name,8);_name[8]=0;}
	void SetType(Bit8u _type) { sSave(sMCB,type,_type);}
	void SetSize(Bit16u _size) { sSave(sMCB,size,_size);}
	void SetPSPSeg(Bit16u _pspseg) { sSave(sMCB,psp_segment,_pspseg);}
	Bit8u GetType(void) { return (Bit8u)sGet(sMCB,type);}
	Bit16u GetSize(void) { return (Bit16u)sGet(sMCB,size);}
	Bit16u GetPSPSeg(void) { return (Bit16u)sGet(sMCB,psp_segment);}
private:
	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct sMCB {
		Bit8u type;
		Bit16u psp_segment;
		Bit16u size;	
		Bit8u unused[3];
		Bit8u filename[8];
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack ()
	#endif
};

class DOS_SDA : public MemStruct {
public:
	DOS_SDA(Bit16u _seg,Bit16u _offs) { SetPt(_seg,_offs); }
	void Init();   
	void SetDrive(Bit8u _drive) { sSave(sSDA,current_drive, _drive); }
	void SetDTA(Bit32u _dta) { sSave(sSDA,current_dta, _dta); }
	void SetPSP(Bit16u _psp) { sSave(sSDA,current_psp, _psp); }
	Bit8u GetDrive(void) { return (Bit8u)sGet(sSDA,current_drive); }
	Bit16u GetPSP(void) { return (Bit16u)sGet(sSDA,current_psp); }
	Bit32u GetDTA(void) { return (Bit32u)sGet(sSDA,current_dta); }
	
	
private:
	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct sSDA {
		Bit8u crit_error_flag;		/* 0x00 Critical Error Flag */
		Bit8u inDOS_flag;		/* 0x01 InDOS flag (count of active INT 21 calls) */
		Bit8u drive_crit_error;		/* 0x02 Drive on which current critical error occurred or FFh */
		Bit8u locus_of_last_error;	/* 0x03 locus of last error */
		Bit16u extended_error_code;	/* 0x04 extended error code of last error */
		Bit8u suggested_action;		/* 0x06 suggested action for last error */
		Bit8u error_class;		/* 0x07 class of last error*/
		Bit32u last_error_pointer; 	/* 0x08 ES:DI pointer for last error */
		Bit32u current_dta;		/* 0x0C current DTA (Disk Transfer Address) */
		Bit16u current_psp; 		/* 0x10 current PSP */
		Bit16u sp_int_23;		/* 0x12 stores SP across an INT 23 */
		Bit16u return_code;		/* 0x14 return code from last process termination (zerod after reading with AH=4Dh) */
		Bit8u current_drive;		/* 0x16 current drive */
		Bit8u extended_break_flag; 	/* 0x17 extended break flag */
		Bit8u fill[2];			/* 0x18 flag: code page switching || flag: copy of previous byte in case of INT 24 Abort*/
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack()
	#endif
};
extern DOS_InfoBlock dos_infoblock;

struct DOS_Block {
	DOS_Date date;
	DOS_Version version;
	Bit16u firstMCB;
	Bit16u errorcode;
	Bit16u psp(){return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetPSP();};
	void psp(Bit16u _seg){ DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetPSP(_seg);};
	Bit16u env;
	RealPt cpmentry;
	RealPt dta(){return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDTA();};
	void dta(RealPt _dta){DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDTA(_dta);};
	Bit8u return_code,return_mode;
	
	Bit8u current_drive;
	bool verify;
	bool breakcheck;
	bool echo;          // if set to true dev_con::read will echo input 
	bool direct_output;
	bool internal_output;
	struct  {
		RealPt mediaid;
		RealPt tempdta;
		RealPt tempdta_fcbdelete;
		RealPt dbcs;
		RealPt filenamechar;
		RealPt collatingseq;
		RealPt upcase;
		Bit8u* country;//Will be copied to dos memory. resides in real mem
		Bit16u dpb; //Fake Disk parameter system using only the first entry so the drive letter matches
	} tables;
	Bit16u loaded_codepage;
};

extern DOS_Block dos;

static INLINE Bit8u RealHandle(Bit16u handle) {
	DOS_PSP psp(dos.psp());	
	return psp.GetFileHandle(handle);
}

#endif
