/*
 *  Copyright (C) 2002-2021  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

#define CTBUF 127

#include "dos_system.h"

#include <list>
#include <stddef.h> //for offsetof

 /* Macros SSET_* and SGET_* are used to safely access fields in memory-mapped
  * DOS structures represented via classes inheriting from MemStruct class.
  *
  * All of these macros depend on 'pt' base pointer from MemStruct base class;
  * all DOS-specific fields are accessed by reading memory relative to that
  * pointer.
  *
  * Example usage:
  *
  *   SSET_WORD(dos-structure-name, field-name, value);
  *   uint16_t x = SGET_WORD(dos-structure-name, field-name);
  */
template <size_t N, typename S, typename T1, typename T2 = T1>
constexpr PhysPt assert_macro_args_ok()
{
    static_assert(sizeof(T1) == N, "Requested struct field has unexpected size");
    static_assert(sizeof(T2) == N, "Type used to save value has unexpected size");
    static_assert(std::is_standard_layout<S>::value,
        "Struct needs to have standard layout for offsetof calculation");
    // returning 0, so compiler can optimize-out no-op "0 +" expression
    return 0;
}

#define VERIFY_SSET_ARGS(n, s, f, v)                                           \
	assert_macro_args_ok<n, s, decltype(s::f), decltype(v)>()
#define VERIFY_SGET_ARGS(n, s, f)                                              \
	assert_macro_args_ok<n, s, decltype(s::f)>()

#define SSET_BYTE(s, f, v)                                                     \
	mem_writeb(VERIFY_SSET_ARGS(1, s, f, v) + pt + offsetof(s, f), v)
#define SSET_WORD(s, f, v)                                                     \
	mem_writew(VERIFY_SSET_ARGS(2, s, f, v) + pt + offsetof(s, f), v)
#define SSET_DWORD(s, f, v)                                                    \
	mem_writed(VERIFY_SSET_ARGS(4, s, f, v) + pt + offsetof(s, f), v)

#define SGET_BYTE(s, f)                                                        \
	mem_readb(VERIFY_SGET_ARGS(1, s, f) + pt + offsetof(s, f))
#define SGET_WORD(s, f)                                                        \
	mem_readw(VERIFY_SGET_ARGS(2, s, f) + pt + offsetof(s, f))
#define SGET_DWORD(s, f)                                                       \
	mem_readd(VERIFY_SGET_ARGS(4, s, f) + pt + offsetof(s, f))

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

extern bool dos_kernel_disabled;

#if !defined(OSFREE)
#define IS_DOS_JAPANESE (!dos_kernel_disabled && mem_readb(Real2Phys(dos.tables.dbcs) + 0x02) == 0x81 && mem_readb(Real2Phys(dos.tables.dbcs) + 0x03) == 0x9F)
#define IS_DOS_CJK (!dos_kernel_disabled && ((mem_readb(Real2Phys(dos.tables.dbcs) + 0x02) == 0x81 || mem_readb(Real2Phys(dos.tables.dbcs) + 0x02) == 0xA1) && (mem_readb(Real2Phys(dos.tables.dbcs) + 0x03) == 0x9F || mem_readb(Real2Phys(dos.tables.dbcs) + 0x03) == 0xFE)))
#define IS_DOSV (dos.set_jdosv_enabled || dos.set_kdosv_enabled || dos.set_pdosv_enabled || dos.set_tdosv_enabled)
#define IS_JDOSV (dos.set_jdosv_enabled)
#define IS_KDOSV (dos.set_kdosv_enabled)
#define IS_PDOSV (dos.set_pdosv_enabled)
#define IS_TDOSV (dos.set_tdosv_enabled)
#define IS_J3100 (dos.set_j3100_enabled)
#else
/* OSFREE: You don't get any of this! */
#define IS_DOS_JAPANESE (0)
#define IS_DOS_CJK (0)
#define IS_DOSV (0)
#define IS_JDOSV (0)
#define IS_KDOSV (0)
#define IS_PDOSV (0)
#define IS_TDOSV (0)
#define IS_J3100 (0)
#endif

extern uint16_t first_umb_seg;
extern uint16_t first_umb_size;

bool MEM_unmap_physmem(Bitu start,Bitu end);
bool MEM_map_RAM_physmem(Bitu start,Bitu end);
bool MEM_map_ROM_physmem(Bitu start,Bitu end);

struct BuiltinFileBlob {
	const char		*recommended_file_name;
	const unsigned char	*data;
	size_t			length;
};

struct DOS_Date {
	uint16_t year;
	uint8_t month;
	uint8_t day;
};

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

#ifndef MACOSX
#if defined (__APPLE__)
#define MACOSX 1
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef signed char         INT8, *PINT8;
typedef signed short        INT16, *PINT16;
typedef signed int          INT32, *PINT32;
//typedef signed __int64      INT64, *PINT64;
typedef unsigned char       UINT8, *PUINT8;
typedef unsigned short      UINT16, *PUINT16;
typedef unsigned int        UINT32, *PUINT32;
//typedef unsigned __int64    UINT64, *PUINT64;
#ifdef __cplusplus
}
#endif

#define SECTOR_SIZE_MAX     2048

#ifdef _MSC_VER
#pragma pack (1)
#endif
union bootSector {
	struct entries {
		uint8_t jump[3];
		uint8_t oem_name[8];
		uint16_t bytesect;
		uint8_t sectclust;
		uint16_t reserve_sect;
	} bootdata;
	uint8_t rawdata[SECTOR_SIZE_MAX];
} 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};

extern Bitu DOS_FILES;

#define DOS_DRIVES 26
#define DOS_DEVICES 45


#if 0 /* ORIGINAL DEFINES FOR REFERENCE */
// 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_MEM_START 0x158	 // regression to r3437 fixes nascar 2 colors
//#define DOS_MEM_START 0x16f		//First Segment that DOS can use 

#define DOS_PRIVATE_SEGMENT 0xc800
#define DOS_PRIVATE_SEGMENT_END 0xd000
#endif

// dos swappable area is 0x320 bytes beyond the sysvars table
// device driver chain is inside sysvars
extern uint16_t DOS_INFOBLOCK_SEG;// 0x80	// sysvars (list of lists)
extern uint16_t DOS_CONDRV_SEG;// 0xa0
extern uint16_t DOS_CONSTRING_SEG;// 0xa8
extern uint16_t DOS_SDA_SEG;// 0xb2		// dos swappable area
extern uint16_t DOS_SDA_SEG_SIZE;
extern uint16_t DOS_SDA_OFS;// 0
extern uint16_t DOS_CDS_SEG;// 0x108
extern uint16_t DOS_MEM_START;// 0x158	 // regression to r3437 fixes nascar 2 colors

extern uint16_t DOS_PRIVATE_SEGMENT;// 0xc800
extern uint16_t DOS_PRIVATE_SEGMENT_END;// 0xd000

constexpr int SftHeaderSize = 6;
constexpr int SftEntrySize = 59;
constexpr int SftNumEntries = 16;

/* internal Dos Tables */

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

extern uint8_t ZDRIVE_NUM;

extern uint8_t dos_copybuf[0x10000];


void DOS_SetError(uint16_t code);

/* File Handling Routines */

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

namespace DeviceInfoFlags
{
	// Device flags
	constexpr uint16_t StdIn            = 1<<0;
	constexpr uint16_t StdOut           = 1<<1;
	constexpr uint16_t Nul              = 1<<2;
	constexpr uint16_t Clock            = 1<<3;
	constexpr uint16_t Special          = 1<<4;
	constexpr uint16_t Binary           = 1<<5;
	constexpr uint16_t EofOnInput       = 1<<6;
	constexpr uint16_t Device           = 1<<7;
	constexpr uint16_t ExternalDevice   = 1<<9;
	constexpr uint16_t OpenCloseSupport = 1<<11;
	constexpr uint16_t OutputUntilBusy  = 1<<13;
	constexpr uint16_t IoctlSupport     = 1<<14;

	// File flags
	constexpr uint16_t NotWritten       = 1<<6;
	constexpr uint16_t NotRemovable     = 1<<11;
	constexpr uint16_t NoTimeUpdate     = 1<<14;
	constexpr uint16_t Remote           = 1<<15;
}
namespace DeviceAttributeFlags
{
	constexpr uint16_t CurrentStdIn      = 1<<0;
	constexpr uint16_t CurrentStdOut     = 1<<1;
	constexpr uint16_t CurrentNul        = 1<<2;
	constexpr uint16_t CurrentClock      = 1<<3;
	constexpr uint16_t SupportsRemovable = 1<<11;
	constexpr uint16_t NonIBM            = 1<<13;
	constexpr uint16_t SupportsIoctl     = 1<<14;
	constexpr uint16_t CharacterDevice   = 1<<15;

	constexpr uint16_t CoreDevicesMask   = CurrentStdIn | CurrentStdOut | CurrentNul | CurrentClock;
}

/* Routines for File Class */
void DOS_SetupFiles (void);
bool DOS_ReadFile(uint16_t entry,uint8_t * data,uint16_t * amount, bool fcb = false);
bool DOS_WriteFile(uint16_t entry,const uint8_t * data,uint16_t * amount,bool fcb = false);
bool DOS_SeekFile(uint16_t entry,uint32_t * pos,uint32_t type,bool fcb = false);
/* ert, 20100711: Locking extensions */
bool DOS_LockFile(uint16_t entry,uint8_t mode,uint32_t pos,uint32_t size);
bool DOS_CloseFile(uint16_t entry,bool fcb = false,uint8_t * refcnt = NULL);
bool DOS_FlushFile(uint16_t entry);
bool DOS_DuplicateEntry(uint16_t entry,uint16_t * newentry);
bool DOS_ForceDuplicateEntry(uint16_t entry,uint16_t newentry);
bool DOS_GetFileDate(uint16_t entry, uint16_t* otime, uint16_t* odate);
bool DOS_SetFileDate(uint16_t entry, uint16_t ntime, uint16_t ndate);

/* Routines for Drive Class */
bool DOS_OpenFile(char const * name,uint8_t flags,uint16_t * entry,bool fcb = false);
bool DOS_OpenExistingSFTEntry(uint16_t jft_handle,int sft_handle);
bool DOS_OpenFileExtended(char const * name, uint16_t flags, uint16_t createAttr, uint16_t action, uint16_t *entry, uint16_t* status);
bool DOS_CreateFile(char const * name,uint16_t attributes,uint16_t * entry, bool fcb = false);
bool DOS_UnlinkFile(char const * const name);
bool DOS_GetSFNPath(char const * const path, char *SFNpath, bool LFN);
bool DOS_FindFirst(const char *search,uint16_t 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,uint16_t * entry);
bool DOS_FileExists(char const * const name);

/* Helper Functions */
bool DOS_MakeName(char const * const name,char * const fullname,uint8_t * drive);
/* Drive Handing Routines */
uint8_t DOS_GetDefaultDrive(void);
void DOS_SetDefaultDrive(uint8_t drive);
bool DOS_SetDrive(uint8_t drive);
bool DOS_GetCurrentDir(uint8_t drive,char * const buffer, bool LFN);
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(uint8_t drive,uint16_t * bytes,uint8_t * sectors,uint16_t * clusters,uint16_t * free);
bool DOS_GetFreeDiskSpace32(uint8_t drive,uint32_t * bytes,uint32_t * sectors,uint32_t * clusters,uint32_t * free);
bool DOS_GetFileAttr(char const * const name,uint16_t * attr);
bool DOS_SetFileAttr(char const * const name,uint16_t attr);
bool DOS_GetFileAttrEx(char const* const name, struct stat *status, uint8_t hdrive=-1);
unsigned long DOS_GetCompressedFileSize(char const* const name);
#if defined (WIN32)
HANDLE DOS_CreateOpenFile(char const* const name);
#endif

/* IOCTL Stuff */
bool DOS_IOCTL(void);
bool DOS_GetSTDINStatus();
uint8_t DOS_FindDevice(char const * name);
void DOS_SetupDevices(void);
void DOS_ClearKeyMap(void);
void DOS_SetConKey(uint16_t src, uint16_t dst);
uint32_t DOS_CheckExtDevice(const char *name, bool already_flag);

/* Execute and new process creation */
bool DOS_NewPSP(uint16_t segment,uint16_t size);
bool DOS_ChildPSP(uint16_t segment,uint16_t size);
bool DOS_Execute(const char* name, PhysPt block_pt, uint16_t flags);
#define DOSEXEC_DEVICEDRIVER 0x0100 /* special flags param for DOS_Execute() that DOS programs cannot pass through INT 21h, block_pt becomes direct segment */
void DOS_Terminate(uint16_t pspseg,bool tsr,uint8_t exitcode);

/* Memory Handling Routines */
void DOS_SetupMemory(void);
uint16_t DOS_GetMaximumFreeSize(uint16_t minBlocks);
bool DOS_AllocateMemory(uint16_t * segment,uint16_t * blocks);
bool DOS_ResizeMemory(uint16_t segment,uint16_t * blocks);
bool DOS_FreeMemory(uint16_t segment);
void DOS_FreeProcessMemory(uint16_t pspseg);
uint16_t DOS_GetMemory(uint16_t pages,const char *who=NULL);
void DOS_Private_UMB_Lock(const bool lock);
void DOS_FreeTableMemory();
bool DOS_SetMemAllocStrategy(uint16_t strat);
uint16_t DOS_GetMemAllocStrategy(void);
void DOS_BuildUMBChain(bool umb_active,bool ems_active);
bool DOS_LinkUMBsToMemChain(uint16_t linkstate);

/* FCB stuff */
bool DOS_FCBOpen(uint16_t seg,uint16_t offset);
bool DOS_FCBCreate(uint16_t seg,uint16_t offset);
bool DOS_FCBClose(uint16_t seg,uint16_t offset);
bool DOS_FCBFindFirst(uint16_t seg,uint16_t offset);
bool DOS_FCBFindNext(uint16_t seg,uint16_t offset);
uint8_t DOS_FCBRead(uint16_t seg,uint16_t offset, uint16_t recno);
uint8_t DOS_FCBWrite(uint16_t seg,uint16_t offset,uint16_t recno);
uint8_t DOS_FCBRandomRead(uint16_t seg,uint16_t offset,uint16_t * numRec,bool restore);
uint8_t DOS_FCBRandomWrite(uint16_t seg,uint16_t offset,uint16_t * numRec,bool restore);
bool DOS_FCBGetFileSize(uint16_t seg,uint16_t offset);
bool DOS_FCBDeleteFile(uint16_t seg,uint16_t offset);
bool DOS_FCBRenameFile(uint16_t seg, uint16_t offset);
void DOS_FCBSetRandomRecord(uint16_t seg, uint16_t offset);
uint8_t FCB_Parsename(uint16_t seg,uint16_t offset,uint8_t parser ,char *string, uint8_t *change);
bool DOS_GetAllocationInfo(uint8_t drive,uint16_t * _bytes_sector,uint8_t * _sectors_cluster,uint16_t * _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, uint8_t flags1, uint8_t flags2, uint8_t flags3);

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


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


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

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

/* fopen64, ftello64, fseeko64 */
#if defined(__linux__)
 #define fseek_ofs_t long
#elif defined (_MSC_VER)
 #define fopen64 fopen
 #if (_MSC_VER >= 1400)
  #define ftello64 _ftelli64
  #define fseeko64 _fseeki64
  #define fseek_ofs_t __int64
 #else
  #define ftello64 ftell
  #define fseeko64 fseek
  #define fseek_ofs_t long
 #endif
#elif defined (__MINGW64_VERSION_MAJOR)
 #define fopen64 fopen
 #define ftello64 _ftelli64
 #define fseeko64 _fseeki64
 #define fseek_ofs_t __int64
#else
 #define fopen64 fopen
 #define ftello64 ftell
 #define fseeko64 fseek
 #define fseek_ofs_t off_t
#endif

/* 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_WRITE_PROTECTED 19
#define DOSERR_DRIVE_NOT_READY 21
#define DOSERR_FILE_ALREADY_EXISTS 80


/* Remains some classes used to access certain things */
#define sOffset(s,m) offsetof(s,m)
#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:
    inline uint32_t GetIt(const uint32_t size, const 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;
	}
	inline void SaveIt(const uint32_t size, const PhysPt addr, const uint32_t val) {
		switch (size) {
		case 1:mem_writeb(pt+addr,(uint8_t)val);break;
		case 2:mem_writew(pt+addr,(uint16_t)val);break;
		case 4:mem_writed(pt+addr,val);break;
		}
	}
    inline void SetPt(const uint16_t seg) { pt=PhysMake(seg,0);}
    inline void SetPt(const uint16_t seg, const uint16_t off) { pt=PhysMake(seg,off);}
    inline void SetPt(const RealPt addr) { pt=Real2Phys(addr);}
    inline PhysPt GetPtPhys(void) const { return pt; }
    inline void SetPtPhys(const PhysPt _pt) { pt=_pt; }
protected:
	PhysPt pt;
};

class DOS_PSP :public MemStruct {
public:
	DOS_PSP						(uint16_t segment):seg(segment)		{ SetPt(segment);};
	void	MakeNew				(uint16_t mem_size);
	void	CopyFileTable		(DOS_PSP* srcpsp,bool createchildpsp);
	uint16_t	FindFreeFileEntry	(void);
	void	CloseFile			(const char *name);
	void	CloseFiles			(void);

	void	SaveVectors			(void);
	void	RestoreVectors		(void);
	void	SetSize				(uint16_t size)			{ sSave(sPSP,next_seg,size);		};
	uint16_t	GetSize				(void)					{ return (uint16_t)sGet(sPSP,next_seg);		};
	void	SetEnvironment		(uint16_t envseg)			{ sSave(sPSP,environment,envseg);	};
	uint16_t	GetEnvironment		(void)					{ return (uint16_t)sGet(sPSP,environment);	};
	uint16_t	GetSegment			(void)					{ return seg;						};
	void	SetFileHandle		(uint16_t index, uint8_t handle);
	uint8_t	GetFileHandle		(uint16_t index);
	void	SetParent			(uint16_t parent)			{ sSave(sPSP,psp_parent,parent);	};
	uint16_t	GetParent			(void)					{ return (uint16_t)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);
	void    StoreCommandTail    (void);
	void    RestoreCommandTail  (void);
	bool	SetNumFiles			(uint16_t fileNum);
	uint16_t	FindEntryByHandle	(uint8_t handle);
	uint16_t	GetSegment		(void) const			{ return seg; }
			
private:
	#ifdef _MSC_VER
	#pragma pack(1)
	#endif
	struct sPSP {
		uint8_t	exit[2];			/* CP/M-like exit point */
		uint16_t	next_seg;			/* Segment of first byte beyond memory allocated or program */
		uint8_t	fill_1;				/* single char fill */
		uint8_t	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 */
		uint16_t	psp_parent;			/* Parent PSP Segment */
		uint8_t	files[20];			/* File Table - 0xff is unused */
		uint16_t	environment;		/* Segment of environment table */
		RealPt	stack;				/* SS:SP Save point for int 0x21 calls */
		uint16_t	max_files;			/* Maximum open files */
		RealPt	file_table;			/* Pointer to File Table PSP:0x18 */
		RealPt	prev_psp;			/* Pointer to previous PSP */
		uint8_t interim_flag;
		uint8_t truename_flag;
		uint16_t nn_flags;
		uint16_t dos_version;
		uint8_t	fill_2[14];			/* Lot's of unused stuff i can't care about */
		uint8_t	service[3];			/* INT 0x21 Service call int 0x21;retf; */
		uint8_t	fill_3[9];			/* This has some blocks with FCB info */
		uint8_t	fcb1[16];			/* first FCB */
		uint8_t	fcb2[16];			/* second FCB */
		uint8_t	fill_4[4];			/* unused */
		CommandTail cmdtail;		
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack()
	#endif
	uint16_t	seg;
public:
	static	uint16_t rootpsp;
};

class DOS_ParamBlock:public MemStruct {
public:
	DOS_ParamBlock(PhysPt addr) {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 {
		uint16_t loadseg;
		uint16_t relocation;
	} GCC_ATTRIBUTE(packed);
	struct sExec {
		uint16_t 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() {};
	void SetLocation(uint16_t  segment);
	void SetFirstDPB(uint32_t _first_dpb);
	void SetFirstMCB(uint16_t _firstmcb);
	uint16_t GetFirstMCB(void);
	void SetBuffers(uint16_t x,uint16_t y);
	void SetCurDirStruct(uint32_t _curdirstruct);
	void SetFCBTable(uint32_t _fcbtable);
	void SetDeviceChainStart(uint32_t _devchain);
	void SetDiskBufferHeadPt(uint32_t _dbheadpt);
	void SetStartOfUMBChain(uint16_t _umbstartseg);
	void SetUMBChainState(uint8_t _umbchaining);
	void SetBlockDevices(uint8_t _count);
	uint16_t	GetStartOfUMBChain(void);
	uint8_t	GetUMBChainState(void);
	RealPt	GetPointer(void);
	uint32_t GetDeviceChain(void);
	uint32_t GetStartOfDeviceChain(void);/*this one includes the NUL driver at the very start*/

	void SetBootDrive(uint8_t drv) { sSave(sDIB,bootDrive,drv); }
	uint8_t GetBootDrive(void) { return sGet(sDIB,bootDrive); }

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

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

    int GetFindData(int fmt,char * finddata,int *c);
	
	void SetupSearch(uint8_t _sdrive,uint8_t _sattr,const char * pattern);
	void SetResult(const char * _name,const char * _lname,uint32_t _size,uint32_t _hsize,uint16_t _date,uint16_t _time,uint8_t _attr);
	
	uint8_t GetSearchDrive(void);
	void GetSearchParams(uint8_t & _sattr,char * _spattern,bool lfn);
    void GetResult(char * _name,char * _lname,uint32_t & _size,uint32_t & _hsize,uint16_t & _date,uint16_t & _time,uint8_t & _attr);

	void	SetDirID(uint16_t entry)			{ sSave(sDTA,dirID,entry); };
	void	SetDirIDCluster(uint32_t entry)	{ sSave(sDTA,dirCluster,entry); };
	uint16_t	GetDirID(void)				{ return (uint16_t)sGet(sDTA,dirID); };
	uint32_t	GetDirIDCluster(void)		{ return sGet(sDTA,dirCluster); };
    uint8_t   GetAttr(void)               { return (uint8_t)sGet(sDTA,sattr); }
private:
	#ifdef _MSC_VER
	#pragma pack(1)
	#endif
	struct sDTA {
		uint8_t sdrive;						/* The Drive the search is taking place */
        uint8_t spname[8];                    /* The Search pattern for the filename */              
        uint8_t spext[3];                     /* The Search pattern for the extension */
		uint8_t sattr;						/* The Attributes that need to be found */
		uint16_t dirID;						/* custom: dir-search ID for multiple searches at the same time */
		uint32_t dirCluster;					/* custom (drive_fat only): cluster number for multiple searches at the same time. 32-bit wide on FAT32 aware MS-DOS 7.1 or higher. */
		uint8_t fill[2];
		uint8_t attr;
		uint16_t time;
		uint16_t date;
		uint32_t size;
		char name[DOS_NAMELENGTH_ASCII];
	} GCC_ATTRIBUTE(packed);
	static_assert(offsetof(sDTA,dirID) == 0x0D,"oops");
	static_assert(offsetof(sDTA,dirCluster) == 0x0F,"oops");
	static_assert(offsetof(sDTA,fill) == 0x13,"oops");
	static_assert(offsetof(sDTA,attr) == 0x15,"oops");
	#ifdef _MSC_VER
	#pragma pack()
	#endif
};

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

class DOS_MCB : public MemStruct{
public:
	DOS_MCB(uint16_t seg) { SetPt(seg); }
	void SetFileName(const char * 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(uint8_t _type) { sSave(sMCB,type,_type);}
	void SetSize(uint16_t _size) { sSave(sMCB,size,_size);}
	void SetPSPSeg(uint16_t _pspseg) { sSave(sMCB,psp_segment,_pspseg);}
	uint8_t GetType(void) { return (uint8_t)sGet(sMCB,type);}
	uint16_t GetSize(void) { return (uint16_t)sGet(sMCB,size);}
	uint16_t GetPSPSeg(void) { return (uint16_t)sGet(sMCB,psp_segment);}
	enum class MCBType : uint8_t
	{
		ValidBlock = 'M',
		LastBlock = 'Z'
	};
private:
	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct sMCB {
		uint8_t type;
		uint16_t psp_segment;
		uint16_t size;	
		uint8_t unused[3];
		uint8_t filename[8];
	} GCC_ATTRIBUTE(packed);
	#ifdef _MSC_VER
	#pragma pack ()
	#endif
};

class DOS_SDA : public MemStruct {
public:
	DOS_SDA(uint16_t _seg,uint16_t _offs) { SetPt(_seg,_offs); }
	void Init();   
	void SetDrive(uint8_t _drive) { sSave(sSDA,current_drive, _drive); }
	void SetDTA(uint32_t _dta) { sSave(sSDA,current_dta, _dta); }
	void SetPSP(uint16_t _psp) { sSave(sSDA,current_psp, _psp); }
	uint8_t GetDrive(void) { return (uint8_t)sGet(sSDA,current_drive); }
	uint16_t GetPSP(void) { return (uint16_t)sGet(sSDA,current_psp); }
	uint32_t GetDTA(void) { return sGet(sSDA,current_dta); }
	
	
private:
	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct sSDA {
		uint8_t crit_error_flag;		/* 0x00 Critical Error Flag */
		uint8_t inDOS_flag;		/* 0x01 InDOS flag (count of active INT 21 calls) */
		uint8_t drive_crit_error;		/* 0x02 Drive on which current critical error occurred or FFh */
		uint8_t locus_of_last_error;	/* 0x03 locus of last error */
		uint16_t extended_error_code;	/* 0x04 extended error code of last error */
		uint8_t suggested_action;		/* 0x06 suggested action for last error */
		uint8_t error_class;		/* 0x07 class of last error*/
		uint32_t last_error_pointer; 	/* 0x08 ES:DI pointer for last error */
		uint32_t current_dta;		/* 0x0C current DTA (Disk Transfer Address) */
		uint16_t current_psp; 		/* 0x10 current PSP */
		uint16_t sp_int_23;		/* 0x12 stores SP across an INT 23 */
		uint16_t return_code;		/* 0x14 return code from last process termination (zerod after reading with AH=4Dh) */
		uint8_t current_drive;		/* 0x16 current drive */
		uint8_t extended_break_flag; 	/* 0x17 extended break flag */
		uint8_t 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
};

static constexpr uint32_t NONEXTDEV = 0xFFFFFFFFul;

static constexpr uint16_t DEVATTR_ISCHAR = 1u << 15u;/*which determines which of the following flags apply*/

static constexpr uint16_t DEVATTRCHR_IOCTL_CTLSTRINGS = 1u << 14u;/*MS-DOS 4.0: 1 IF THE DEVICE UNDERSTANDS IOCTL CONTROL STRINGS*/
static constexpr uint16_t DEVATTRCHR_IOCTL_OUTPUT_UNTIL_BUSY = 1u << 13u;/*MS-DOS 4.0: 1 IF THE DEVICE SUPPORTS OUTPUT-UNTIL-BUSY*/
static constexpr uint16_t DEVATTRCHR_OPENCLOSE = 1u << 11u;/*MS-DOS 4.0: 1 IF THE DEVICE UNDERSTANDS OPEN/CLOSE*/
static constexpr uint16_t DEVATTRCHR_INT29 = 1u << 4u;/*MS-DOS 4.0: 1 IF DEVICE IS RECIPIENT OF INT 29H*/
static constexpr uint16_t DEVATTRCHR_CLOCK = 1u << 3u;/*MS-DOS 4.0: 1 IF DEVICE IS CLOCK DEVICE*/
static constexpr uint16_t DEVATTRCHR_NULL = 1u << 2u;/*MS-DOS 4.0: 1 IF DEVICE IS NULL DEVICE*/
static constexpr uint16_t DEVATTRCHR_CONOUT = 1u << 1u;/*MS-DOS 4.0: 1 IF DEVICE IS CONSOLE OUTPUT*/
static constexpr uint16_t DEVATTRCHR_CONIN = 1u << 0u;/*MS-DOS 4.0: 1 IF DEVICE IS CONSOLE INPUT*/

static constexpr uint16_t DEVATTRBLK_IOCTL_CTLSTRINGS = 1u << 14u;/*MS-DOS 4.0: 1 IF THE DEVICE UNDERSTANDS IOCTL CONTROL STRINGS*/
static constexpr uint16_t DEVATTRBLK_IOCTL_MEDIA_FAT_BYTE = 1u << 13u;/*MS-DOS 4.0: 1 IF THE DEVICE DETERMINES MEDIA BY EXAMINING THE FAT ID BYTE*/
static constexpr uint16_t DEVATTRBLK_OPENCLOSEREMOVABLE = 1u << 11u;/*MS-DOS 4.0: 1 IF THE DEVICE UNDERSTANDS OPEN/CLOSE/REMOVABLE MEDIA*/
static constexpr uint16_t DEVATTRBLK_IBM_DRIVE_SHARED = 1u << 9u;/*MS-DOS 4.0: ... IS CURRENTLY USED ON IBM SYSTEMS TO INDICATE "DRIVE IS SHARED" ... THIS USE IS NOT DOCUMENTED ... used by utilities like FORMAT which are supposed to fail on shared drives on server machines */
static constexpr uint16_t DEVATTRBLK_IOCTL_GEN = 1u << 6u;/*MS-DOS 4.0: IF DEVICE HAS SUPPORT FOR GETMAP/SETMAP OF LOGICAL DRIVES / UNDERSTANDS GENERIC IOCTL FUNCTION CALLS*/
static constexpr uint16_t DEVATTRBLK_EXTENDED = 1u << 1u;/*MS-DOS 4.0: Extended block device (>=32MB) aka EXTDRVR*/

enum DEVFUNC {
	DEVFUNC_INIT=0,
	DEVFUNC_MEDIACHECK=1,
	DEVFUNC_GETPBP=2,
	DEVFUNC_IOCTL_READ=3,
	DEVFUNC_READ=4,
	DEVFUNC_NDREAD=5,/*non destructive read*/
	DEVFUNC_INPUT_STATUS=6,
	DEVFUNC_INPUT_FLUSH=7,
	DEVFUNC_WRITE=8,
	DEVFUNC_WRITEVERIFY=9,
	DEVFUNC_OUTPUT_STATUS=10,
	DEVFUNC_OUTPUT_FLUSH=11,
	DEVFUNC_IOCTL_WRITE=12,
	DEVFUNC_OPEN=13,
	DEVFUNC_CLOSE=14,
	DEVFUNC_REMOVABLE_MEDIA=15,
	DEVFUNC_OUTPUT_UNTIL_BUSY=16,/*MS-DOS 4.0*/
	DEVFUNC_GENERAL_IOCTL=19,
	DEVFUNC_GET_OWNER=23,/*MS-DOS 4.0*/
	DEVFUNC_SET_OWNER=24/*MS-DOS 4.0*/
};

enum DOSDEVERR {
	DOSDEVERR_WRITEPROTECT=0,
	DOSDEVERR_UNKNOWNUNIT=1,
	DOSDEVERR_DRIVENOTREADY=2,
	DOSDEVERR_UNKNOWNCOMMAND=3,
	DOSDEVERR_CRC=4,
	DOSDEVERR_BADDRIVEREQUEST=5,
	DOSDEVERR_SEEKERR=6,
	DOSDEVERR_UNKNOWNMEDIA=7,
	DOSDEVERR_SECTORNOTFOUND=8,
	DOSDEVERR_PRINTEROUTOFPAPER=9,
	DOSDEVERR_WRITEFAULT=10,
	DOSDEVERR_READFAULT=11,
	DOSDEVERR_GENERALFAILURE=12
};

class DOS_DEVHDR : public MemStruct{/*device driver header*/
public:
	DOS_DEVHDR(uint16_t seg) { SetPt(seg); }
	uint32_t GetNextDriver(void) { return (uint32_t)sGet(hdr,nextdev); };
 /* NONEXTDEV if end of list */
	void SetNextDriver(const uint32_t p) { sSave(hdr,nextdev,p); };
	uint16_t GetAttributes(void) { return (uint16_t)sGet(hdr,attributes); };
	uint16_t GetStrategyOffset(void) { return (uint16_t)sGet(hdr,strategy_entry); };
	uint16_t GetInterruptOffset(void) { return (uint16_t)sGet(hdr,interrupt_entry); };
	void GetName(char * const _name) { MEM_BlockRead(pt+offsetof(hdr,name),_name,8);_name[8]=0;}

	#ifdef _MSC_VER
	#pragma pack (1)
	#endif
	struct hdr {
		uint32_t nextdev; /* pointer to next device or FFFF:FFFF */
		uint16_t attributes;
		uint16_t strategy_entry;
		uint16_t interrupt_entry;
		uint8_t name[8];
	} GCC_ATTRIBUTE(packed);/*=18 bytes*/

	/* structure used in passing requests to device driver, head structure: "Static Request Header" */
	struct streqhdr {
		uint8_t record_length; /* length of the driver request */
		uint8_t unit_code;
		uint8_t cmd_code;
		uint16_t status; /* status word: 15=ERR 9=BUSY 8=DONE 7..0=ERR CODE */
		uint32_t reserved[2]; /* reserved for internal DOS use, queue links */
	} GCC_ATTRIBUTE(packed);/*=13 bytes*/

	/* init request */
	struct req_init {
		struct streqhdr hdr; /* static request header (13 bytes) DEVFUNC_INIT */
		uint8_t num_of_units; /* number of units */
		uint32_t end_ptr; /* ending address of driver, filled in by INIT */
		uint32_t bpb_ptr; /* in: init arguments  out: BPB array */
		/* MS-DOS 2.0 ends here == 22 bytes */
		uint8_t drive_num; /* driver number */
		uint16_t config_err; /* config.sys error flag */
	} GCC_ATTRIBUTE(packed);/*=25 bytes*/

	/* read/write request */
	struct req_rwio {
		struct streqhdr hdr; /* static request header (13 bytes) DEVFUNC_READ/DEVFUNC_WRITE/DEVFUNC_WRITEVERIFY */
		uint8_t media_dpb; /* from DPB */
		uint32_t xfer_addr; /* transfer address (16:16) */
		uint16_t count; /* byte or sector count */
		uint16_t start_sector; /* starting sector (if block device) */
		/* block device with 16-bit sector stops here (prior to MS-DOS 3.3) == 22 bytes */
		/* the following applies if EXTDRVR */
		uint32_t ptr_volid; /* pointer to volume ID (R/W according to MS-DOS 4.0 source code) */
		uint32_t start_sector32; /* starting sector (if block device) */
		/* EXTDRVR stops here == 30 bytes */
	} GCC_ATTRIBUTE(packed);/*=30 bytes*/

	#ifdef _MSC_VER
	#pragma pack ()
	#endif
};

extern DOS_InfoBlock dos_infoblock;

struct DOS_Block {
    DOS_Date date = {};
    DOS_Version version = {};
    uint16_t firstMCB = 0;
    uint16_t errorcode = 0;
    uint16_t psp() const;//{return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetPSP();};
    void psp(uint16_t _seg) const;//{ DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetPSP(_seg);};
    RealPt dta() const;//{return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDTA();};
    void dta(RealPt _dta) const;//{DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDTA(_dta);};
    uint8_t return_code = 0, return_mode = 0;

    uint8_t current_drive = 0;
    bool verify = false;
    bool breakcheck = false;
    bool echo = false;          // if set to true dev_con::read will echo input
    bool direct_output = false;
    bool internal_output = false;
    struct {
        RealPt mediaid = 0;
        RealPt tempdta = 0;
        RealPt tempdta_fcbdelete = 0;
        RealPt dbcs = 0;
        RealPt filenamechar = 0;
        RealPt collatingseq = 0;
        RealPt upcase = 0;
        uint8_t* country = NULL;//Will be copied to dos memory. resides in real mem
        uint16_t dpb = 0; //Fake Disk parameter system using only the first entry so the drive letter matches
        uint16_t dpb_size = 0x21; // bytes per DPB entry (MS-DOS 4.x-6.x size)
        uint16_t mediaid_offset = 0x17; // media ID offset in DPB (MS-DOS 4.x-6.x case)
    } tables;
    uint16_t loaded_codepage = 0;
#if !defined(OSFREE)
    bool set_jdosv_enabled = false;
    bool set_kdosv_enabled = false;
    bool set_pdosv_enabled = false;
    bool set_tdosv_enabled = false;
    bool set_j3100_enabled = false;
#else
    static constexpr bool set_jdosv_enabled = false;
    static constexpr bool set_kdosv_enabled = false;
    static constexpr bool set_pdosv_enabled = false;
    static constexpr bool set_tdosv_enabled = false;
    static constexpr bool set_j3100_enabled = false;
#endif
    bool im_enable_flag;
    uint16_t dcp;	// Device command packet
    static constexpr uint16_t dcp_size_seg = 3;//allocated size of DCP buffer
};

extern DOS_Block dos;

static INLINE uint8_t RealHandle(uint16_t handle) {
	DOS_PSP psp(dos.psp());	
	return psp.GetFileHandle(handle);
}

struct DOS_GetMemLog_Entry {
    uint16_t      segbase = 0;
    uint16_t      pages = 0;
    std::string who;
};

extern std::list<DOS_GetMemLog_Entry> DOS_GetMemLog;

extern int customcp, altcp;
bool isSupportedCP(int cp);
void SetupDBCSTable(void);
const char* DOS_GetLoadedLayout(void);

enum COUNTRYNO {
	United_States = 1,
	Candian_French = 2,
	Latin_America = 3,
	Russia = 7,
	Greece = 30,
	Netherlands = 31,
	Belgium = 32,
	France = 33,
	Spain = 34,
	Hungary = 36,
	Yugoslavia = 38,
	Italy = 39,
	Romania = 40,
	Switzerland = 41,
	Czech_Slovak = 42,
	Austria = 43,
	United_Kingdom = 44,
	Denmark = 45,
	Sweden = 46,
	Norway = 47,
	Poland = 48,
	Germany = 49,
	Argentina = 54,
	Brazil = 55,
	Malaysia = 60,
	Australia = 61,
	Philippines = 63,
	Singapore = 65,
	Kazakhstan = 77,
	Japan = 81,
	South_Korea = 82,
	Vietnam = 84,
	China = 86,
	Turkey = 90,
	India = 91,
	Niger = 227,
	Benin = 229,
	Nigeria = 234,
	Faeroe_Islands = 298,
	Portugal = 351,
	Iceland = 354,
	Albania = 355,
	Malta = 356,
	Finland = 358,
	Bulgaria = 359,
	Lithuania = 370,
	Latvia = 371,
	Estonia = 372,
	Armenia = 374,
	Belarus = 375,
	Ukraine = 380,
	Serbia = 381,
	Montenegro = 382,
	Croatia = 384,
	Slovenia = 386,
	Bosnia = 387,
	Macedonia = 389,
	Taiwan = 886,
	Arabic = 785,
	Israel = 972,
	Mongolia = 976,
	Tadjikistan = 992,
	Turkmenistan = 993,
	Azerbaijan = 994,
	Georgia = 995,
	Kyrgyzstan = 996,
	Uzbekistan = 998,
};

const std::map<std::string, int> country_code_map {
	// reference: https://gitlab.com/FreeDOS/base/keyb_lay/-/blob/master/DOC/KEYB/LAYOUTS/LAYOUTS.TXT
	{"ar462",  COUNTRYNO::Arabic         },
	{"ar470",  COUNTRYNO::Arabic         },
	{"az",     COUNTRYNO::Azerbaijan     },
	{"ba",     COUNTRYNO::Bosnia         },
	{"be",     COUNTRYNO::Belgium        },
	{"bg",     COUNTRYNO::Bulgaria       }, // 101-key
	{"bg103",  COUNTRYNO::Bulgaria       }, // 101-key, Phonetic
	{"bg241",  COUNTRYNO::Bulgaria       }, // 102-key
	{"bl",     COUNTRYNO::Belarus        },
	{"bn",     COUNTRYNO::Benin          },
	{"br",     COUNTRYNO::Brazil         }, // ABNT layout
	{"br274",  COUNTRYNO::Brazil         }, // US layout
	{"bx",     COUNTRYNO::Belgium        }, // International
	{"by",     COUNTRYNO::Belarus        },
	{"ca",     COUNTRYNO::Candian_French }, // Standard
	{"ce",     COUNTRYNO::Russia         }, // Chechnya Standard
	{"ce443",  COUNTRYNO::Russia         }, // Chechnya Typewriter
	{"cg",     COUNTRYNO::Montenegro     },
	{"cf",     COUNTRYNO::Candian_French }, // Standard
	{"cf445",  COUNTRYNO::Candian_French }, // Dual-layer
	{"co",     COUNTRYNO::United_States  }, // Colemak
	{"cz",     COUNTRYNO::Czech_Slovak   }, // Czechia, QWERTY
	{"cz243",  COUNTRYNO::Czech_Slovak   }, // Czechia, Standard
	{"cz489",  COUNTRYNO::Czech_Slovak   }, // Czechia, Programmers
	{"de",     COUNTRYNO::Germany        }, // Standard
	{"dk",     COUNTRYNO::Denmark        },
	{"dv",     COUNTRYNO::United_States  }, // Dvorak
	{"ee",     COUNTRYNO::Estonia        },
	{"el",     COUNTRYNO::Greece         }, // 319
	{"es",     COUNTRYNO::Spain          },
	{"et",     COUNTRYNO::Estonia        },
	{"fi",     COUNTRYNO::Finland        },
	{"fo",     COUNTRYNO::Faeroe_Islands },
	{"fr",     COUNTRYNO::France         }, // Standard
	{"fx",     COUNTRYNO::France         }, // International
	{"gk",     COUNTRYNO::Greece         }, // 319
	{"gk220",  COUNTRYNO::Greece         }, // 220
	{"gk459",  COUNTRYNO::Greece         }, // 101-key
	{"gr",     COUNTRYNO::Germany        }, // Standard
	{"gr453",  COUNTRYNO::Germany        }, // Dual-layer
	{"hr",     COUNTRYNO::Croatia        },
	{"hu",     COUNTRYNO::Hungary        }, // 101-key
	{"hu208",  COUNTRYNO::Hungary        }, // 102-key
	{"hy",     COUNTRYNO::Armenia        },
	{"il",     COUNTRYNO::Israel         },
	{"is",     COUNTRYNO::Iceland        }, // 101-key
	{"is161",  COUNTRYNO::Iceland        }, // 102-key
	{"it",     COUNTRYNO::Italy          }, // Standard
	{"it142",  COUNTRYNO::Italy          }, // Comma on Numeric Pad
	{"ix",     COUNTRYNO::Italy          }, // International
	{"jp",     COUNTRYNO::Japan          },
	{"ka",     COUNTRYNO::Georgia        },
	{"kk",     COUNTRYNO::Kazakhstan     },
	{"kk476",  COUNTRYNO::Kazakhstan     },
	{"kx",     COUNTRYNO::United_Kingdom }, // International
	{"ky",     COUNTRYNO::Kyrgyzstan     },
	{"la",     COUNTRYNO::Latin_America  },
	{"lh",     COUNTRYNO::United_States  }, // Left-Hand Dvorak
	{"lt",     COUNTRYNO::Lithuania      }, // Baltic
	{"lt210",  COUNTRYNO::Lithuania      }, // 101-key, Programmers
	{"lt211",  COUNTRYNO::Lithuania      }, // AZERTY
	{"lt221",  COUNTRYNO::Lithuania      }, // Standard
	{"lt456",  COUNTRYNO::Lithuania      }, // Dual-layout
	{"lv",     COUNTRYNO::Latvia         }, // Standard
	{"lv455",  COUNTRYNO::Latvia         }, // Dual-layout
	{"ml",     COUNTRYNO::Malta          }, // UK-based
	{"mk",     COUNTRYNO::Macedonia      },
	{"mn",     COUNTRYNO::Mongolia       },
	{"mo",     COUNTRYNO::Mongolia       },
	{"mt",     COUNTRYNO::Malta          }, // UK-based
	{"mt103",  COUNTRYNO::Malta          }, // US-based
	{"ne",     COUNTRYNO::Niger          },
	{"ng",     COUNTRYNO::Nigeria        },
	{"nl",     COUNTRYNO::Netherlands    }, // 102-key
	{"no",     COUNTRYNO::Norway         },
	{"ph",     COUNTRYNO::Philippines    },
	{"pl",     COUNTRYNO::Poland         }, // 101-key, Programmers
	{"pl214",  COUNTRYNO::Poland         }, // 102-key
	{"po",     COUNTRYNO::Portugal       },
	{"px",     COUNTRYNO::Portugal       }, // International
	{"ro",     COUNTRYNO::Romania        }, // Standard
	{"ro446",  COUNTRYNO::Romania        }, // QWERTY
	{"rh",     COUNTRYNO::United_States  }, // Right-Hand Dvorak
	{"ru",     COUNTRYNO::Russia         }, // Standard
	{"ru443",  COUNTRYNO::Russia         }, // Typewriter
	{"rx",     COUNTRYNO::Russia         }, // Extended Standard
	{"rx443",  COUNTRYNO::Russia         }, // Extended Typewriter
	{"sd",     COUNTRYNO::Switzerland    }, // German
	{"sf",     COUNTRYNO::Switzerland    }, // French
	{"sg",     COUNTRYNO::Switzerland    }, // German
	{"si",     COUNTRYNO::Slovenia       },
	{"sk",     COUNTRYNO::Czech_Slovak   }, // Slovakia
	{"sp",     COUNTRYNO::Spain          },
	{"sq",     COUNTRYNO::Albania        }, // No-deadkeys
	{"sq448",  COUNTRYNO::Albania        }, // Deadkeys
	{"sr",     COUNTRYNO::Serbia         }, // Deadkey
	{"su",     COUNTRYNO::Finland        },
	{"sv",     COUNTRYNO::Sweden         },
	{"sx",     COUNTRYNO::Spain          }, // International
	{"tj",     COUNTRYNO::Tadjikistan    },
	{"tm",     COUNTRYNO::Turkmenistan   },
	{"tr",     COUNTRYNO::Turkey         }, // QWERTY
	{"tr440",  COUNTRYNO::Turkey         }, // Non-standard
	{"tt",     COUNTRYNO::Russia         }, // Tatarstan Standard
	{"tt443",  COUNTRYNO::Russia         }, // Tatarstan Typewriter
	{"ua",     COUNTRYNO::Ukraine        }, // 101-key
	{"uk",     COUNTRYNO::United_Kingdom }, // Standard
	{"uk168",  COUNTRYNO::United_Kingdom }, // Alternate
	{"ur",     COUNTRYNO::Ukraine        }, // 101-key
	{"ur465",  COUNTRYNO::Ukraine        }, // 101-key
	{"ur1996", COUNTRYNO::Ukraine        }, // 101-key
	{"ur2001", COUNTRYNO::Ukraine        }, // 102-key
	{"ur2007", COUNTRYNO::Ukraine        }, // 102-key
	{"us",     COUNTRYNO::United_States  }, // Standard
	{"ux",     COUNTRYNO::United_States  }, // International
	{"uz",     COUNTRYNO::Uzbekistan     },
	{"vi",     COUNTRYNO::Vietnam        },
	{"yc",     COUNTRYNO::Serbia         }, // Deadkey
	{"yc450",  COUNTRYNO::Serbia         }, // No-deadkey
	{"yu",     COUNTRYNO::Yugoslavia     },
};

void DOS_FlushSTDIN(void);


extern unsigned char exepack_handling;

enum {
	EXEPACK_NONE,
	EXEPACK_A20OFF,
	EXEPACK_UNPACK
};

#endif
