/* * SPDX-License-Identifier: GPL-2.0-or-later * * Copyright (C) 2020-2022 The DOSBox Staging Team * 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 #include "dosbox.h" #include #include #include #include "dos_system.h" #include "mem.h" #define EXT_DEVICE_BIT 0x0200 #ifdef _MSC_VER #pragma pack (1) #endif struct CommandTail{ uint8_t count = 0; /* number of bytes returned */ char buffer[127] = {}; /* the buffer itself */ } GCC_ATTRIBUTE(packed); #ifdef _MSC_VER #pragma pack () #endif struct DOS_Date { uint16_t year; uint8_t month; uint8_t day; }; struct DOS_Version { uint8_t major,minor,revision; }; #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; uint8_t misc[496]; } bootdata; uint8_t 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 255 #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 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}; /* Routines for File Class */ void DOS_SetupFiles (void); bool DOS_ReadFile(uint16_t handle,uint8_t * data,uint16_t * amount, bool fcb = false); bool DOS_WriteFile(uint16_t handle,uint8_t * data,uint16_t * amount,bool fcb = false); bool DOS_SeekFile(uint16_t handle,uint32_t * pos,uint32_t type,bool fcb = false); bool DOS_CloseFile(uint16_t handle,bool fcb = false,uint8_t * refcnt = NULL); bool DOS_FlushFile(uint16_t handle); 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); // Date and Time Conversion constexpr uint16_t DOS_PackTime(const uint16_t hour, const uint16_t min, const uint16_t sec) noexcept { const auto h_bits = 0b1111100000000000 & (hour << 11); const auto m_bits = 0b0000011111100000 & (min << 5); const auto s_bits = 0b0000000000011111 & (sec / 2); const auto packed = h_bits | m_bits | s_bits; return static_cast(packed); } constexpr uint16_t DOS_PackTime(const struct tm &datetime) noexcept { return DOS_PackTime(static_cast(datetime.tm_hour), static_cast(datetime.tm_min), static_cast(datetime.tm_sec)); } constexpr uint16_t DOS_PackDate(const uint16_t year, const uint16_t mon, const uint16_t day) noexcept { const int delta_year = year - 1980; constexpr int delta_year_min = 0; constexpr int delta_year_max = INT8_MAX; const auto years_after_1980 = static_cast( std::clamp(delta_year, delta_year_min, delta_year_max)); const auto y_bits = 0b1111111000000000 & (years_after_1980 << 9); const auto m_bits = 0b0000000111100000 & (mon << 5); const auto d_bits = 0b0000000000011111 & day; const auto packed = y_bits | m_bits | d_bits; return static_cast(packed); } constexpr uint16_t DOS_PackDate(const struct tm &datetime) noexcept { return DOS_PackDate(static_cast(datetime.tm_year + 1900), static_cast(datetime.tm_mon + 1), static_cast(datetime.tm_mday)); } /* Routines for Drive Class */ bool DOS_OpenFile(char const * name,uint8_t flags,uint16_t * entry,bool fcb = false); 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 attribute,uint16_t * entry, bool fcb = false); bool DOS_UnlinkFile(char const * const name); 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 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_GetFileAttr(char const * const name,uint16_t * attr); bool DOS_SetFileAttr(char const * const name,uint16_t attr); /* IOCTL Stuff */ bool DOS_IOCTL(void); bool DOS_GetSTDINStatus(); uint8_t DOS_FindDevice(char const * name); void DOS_SetupDevices(); void DOS_ShutDownDevices(); /* Execute and new process creation */ bool DOS_NewPSP(uint16_t pspseg,uint16_t size); bool DOS_ChildPSP(uint16_t pspseg,uint16_t size); bool DOS_Execute(char * name,PhysPt block,uint8_t flags); void DOS_Terminate(uint16_t pspseg,bool tsr,uint8_t exitcode); /* Memory Handling Routines */ void DOS_SetupMemory(void); 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); bool DOS_SetMemAllocStrategy(uint16_t strat); void DOS_SetMcbFaultStrategy(const char *mcb_fault_strategy_pref); 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 numBlocks); uint8_t DOS_FCBWrite(uint16_t seg,uint16_t offset,uint16_t numBlocks); 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(const uint8_t key, const uint8_t flags1, const uint8_t flags2, const uint8_t flags3); DOS_Version DOS_ParseVersion(const char *word, const char *args); enum KeyboardErrorCode : uint8_t { KEYB_NOERROR = 0, KEYB_FILENOTFOUND, KEYB_INVALIDFILE, KEYB_LAYOUTNOTFOUND, KEYB_INVALIDCPFILE }; 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); } /* 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 /* Wait/check user input */ enum class UserDecision { Cancel, Continue, Next }; bool DOS_IsCancelRequest(); UserDecision DOS_WaitForCancelContinue(); UserDecision DOS_WaitForCancelContinueNext(); /* 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 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::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() #define VERIFY_SGET_ARGS(n, s, f) \ assert_macro_args_ok() #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)) class MemStruct { public: MemStruct() = default; MemStruct(uint16_t seg, uint16_t off) : pt(PhysMake(seg, off)) {} MemStruct(RealPt addr) : pt(Real2Phys(addr)) {} void SetPt(uint16_t seg) { pt = PhysMake(seg, 0); } protected: PhysPt pt = 0; }; /* Program Segment Prefix */ class DOS_PSP final : public MemStruct { public: DOS_PSP(uint16_t segment) : seg(segment) { SetPt(seg); } void MakeNew(uint16_t mem_size); void CopyFileTable(DOS_PSP *srcpsp, bool createchildpsp); void CloseFiles(); uint16_t GetSegment() const { return seg; } void SaveVectors(); void RestoreVectors(); void SetFileHandle(uint16_t index, uint8_t handle); uint8_t GetFileHandle(uint16_t index) const; uint16_t FindFreeFileEntry() const; uint16_t FindEntryByHandle(uint8_t handle) const; void SetSize(uint16_t size) { SSET_WORD(sPSP, next_seg, size); } uint16_t GetSize() const { return SGET_WORD(sPSP, next_seg); } void SetInt22(RealPt int22pt) { SSET_DWORD(sPSP, int_22, int22pt); } RealPt GetInt22() const { return SGET_DWORD(sPSP, int_22); } void SetParent(uint16_t parent) { SSET_WORD(sPSP, psp_parent, parent); } uint16_t GetParent() const { return SGET_WORD(sPSP, psp_parent); } void SetEnvironment(uint16_t env) { SSET_WORD(sPSP, environment, env); } uint16_t GetEnvironment() const { return SGET_WORD(sPSP, environment); } void SetStack(RealPt stackpt) { SSET_DWORD(sPSP, stack, stackpt); } RealPt GetStack() const { return SGET_DWORD(sPSP, stack); } bool SetNumFiles(uint16_t file_num); void SetFCB1(RealPt src); void SetFCB2(RealPt src); void SetCommandTail(RealPt src); private: #ifdef _MSC_VER #pragma pack(1) #endif struct sPSP { uint8_t exit[2]; /* CP/M-like exit poimt */ 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 evironment 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 aboue */ 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 final : public MemStruct { public: DOS_ParamBlock(PhysPt addr) : exec{0, 0, 0, 0, 0, 0}, overlay{0, 0} { pt = addr; } void Clear(); void LoadData(); void SaveData(); // 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 final : public MemStruct { public: DOS_InfoBlock() : seg(0) {} void SetLocation(uint16_t segment); void SetBuffers(uint16_t x, uint16_t y); RealPt GetPointer() const { return RealMake(seg, offsetof(sDIB, firstDPB)); } void SetDeviceChainStart(uint32_t chain) { SSET_DWORD(sDIB, nulNextDriver, chain); } uint32_t GetDeviceChain() const { return SGET_DWORD(sDIB, nulNextDriver); } void SetUMBChainState(uint8_t state) { SSET_BYTE(sDIB, chainingUMB, state); } uint8_t GetUMBChainState() const { return SGET_BYTE(sDIB, chainingUMB); } void SetStartOfUMBChain(uint16_t start_seg) { SSET_WORD(sDIB, startOfUMBChain, start_seg); } uint16_t GetStartOfUMBChain() const { return SGET_WORD(sDIB, startOfUMBChain); } void SetDiskBufferHeadPt(uint32_t db) { SSET_DWORD(sDIB, diskBufferHeadPt, db); } void SetFirstMCB(uint16_t mcb) { SSET_WORD(sDIB, firstMCB, mcb); } void SetCurDirStruct(uint32_t cds) { SSET_DWORD(sDIB, curDirStructure, cds); } void SetFCBTable(uint32_t tab) { SSET_DWORD(sDIB, fcbTable, tab); } void SetBlockDevices(uint8_t num) { SSET_BYTE(sDIB, blockDevices, num); } #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 uint32_t nulStrategy; // 0x28 NUL driver strategy 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; }; /* Disk Transfer Address * * Some documents refer to it also as Data Transfer Address or Disk Transfer Area. */ class DOS_DTA final : public MemStruct { public: DOS_DTA(RealPt addr) : MemStruct(addr) {} void SetupSearch(uint8_t drive, uint8_t attr, char *pattern); uint8_t GetSearchDrive() const { return SGET_BYTE(sDTA, sdrive); } void GetSearchParams(uint8_t &attr, char *pattern) const; void SetResult(const char *name, uint32_t size, uint16_t date, uint16_t time, uint8_t attr); void GetResult(char *name, uint32_t &size, uint16_t &date, uint16_t &time, uint8_t &attr) const; void SetDirID(uint16_t id) { SSET_WORD(sDTA, dirID, id); } uint16_t GetDirID() const { return SGET_WORD(sDTA, dirID); } void SetDirIDCluster(uint16_t cl) { SSET_WORD(sDTA, dirCluster, cl); } uint16_t GetDirIDCluster() const { return SGET_WORD(sDTA, dirCluster); } private: #ifdef _MSC_VER #pragma pack(1) #endif struct sDTA { uint8_t sdrive; /* The Drive the search is taking place */ uint8_t sname[8]; /* The Search pattern for the filename */ uint8_t sext[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 */ uint16_t dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */ uint8_t fill[4]; uint8_t attr; uint16_t time; uint16_t date; uint32_t size; char name[DOS_NAMELENGTH_ASCII]; } GCC_ATTRIBUTE(packed); #ifdef _MSC_VER #pragma pack() #endif }; /* File Control Block */ class DOS_FCB final : public MemStruct { public: DOS_FCB(uint16_t seg, uint16_t off, bool allow_extended = true); void Create(bool _extended); bool Extended() const { return extended; } void SetName(uint8_t drive, const char *fname, const char *ext); void GetName(char * fillname); void SetSizeDateTime(uint32_t size, uint16_t mod_date, uint16_t mod_time); void GetSizeDateTime(uint32_t &size, uint16_t &mod_date, uint16_t &mod_time) const; void FileOpen(uint8_t fhandle); void FileClose(uint8_t &fhandle); void SetRecord(uint16_t cur_block, uint8_t cur_rec); void GetRecord(uint16_t &cur_block, uint8_t &cur_rec) const; void SetSeqData(uint8_t fhandle, uint16_t rec_size); void GetSeqData(uint8_t &fhandle, uint16_t &rec_size) const; void SetRandom(uint32_t random) { SSET_DWORD(sFCB, rndm, random); } uint32_t GetRandom() const { return SGET_DWORD(sFCB, rndm); } void SetAttr(uint8_t attr); void GetAttr(uint8_t &attr) const; void SetResult(uint32_t size,uint16_t date,uint16_t time,uint8_t attr); uint8_t GetDrive() const; bool Valid() const; void ClearBlockRecsize(); private: bool extended; 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; // Date of last modification uint16_t time; // Time of last modification /* 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 }; /* Memory Control Block */ class DOS_MCB final : public MemStruct { public: DOS_MCB(uint16_t seg) : MemStruct(seg, 0) {} 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(uint8_t mcb_type) { SSET_BYTE(sMCB, type, mcb_type); } uint8_t GetType() const { return SGET_BYTE(sMCB, type); } void SetSize(uint16_t size_paras) { SSET_WORD(sMCB, size, size_paras); } uint16_t GetSize() const { return SGET_WORD(sMCB, size); } void SetPSPSeg(uint16_t psp) { SSET_WORD(sMCB, psp_segment, psp); } uint16_t GetPSPSeg() const { return SGET_WORD(sMCB, psp_segment); } private: #ifdef _MSC_VER #pragma pack (1) #endif struct sMCB { uint8_t type; uint16_t psp_segment; uint16_t size; // Allocation size in 16-byte paragraphs uint8_t unused[3]; uint8_t filename[8]; } GCC_ATTRIBUTE(packed); #ifdef _MSC_VER #pragma pack () #endif }; class DOS_SDA final : public MemStruct { public: DOS_SDA(uint16_t seg, uint16_t off) : MemStruct(seg, off) {} void Init(); void SetDrive(uint8_t drive) { SSET_BYTE(sSDA, current_drive, drive); } uint8_t GetDrive() const { return SGET_BYTE(sSDA, current_drive); } void SetDTA(uint32_t dta) { SSET_DWORD(sSDA, current_dta, dta); } uint32_t GetDTA() const { return SGET_DWORD(sSDA, current_dta); } void SetPSP(uint16_t psp) { SSET_WORD(sSDA, current_psp, psp); } uint16_t GetPSP() const { return SGET_WORD(sSDA, current_psp); } 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 }; extern DOS_InfoBlock dos_infoblock; struct DOS_Block { DOS_Date date; DOS_Version version; uint16_t firstMCB; uint16_t errorcode; uint16_t psp() { return DOS_SDA(DOS_SDA_SEG, DOS_SDA_OFS).GetPSP(); } void psp(uint16_t seg) { DOS_SDA(DOS_SDA_SEG, DOS_SDA_OFS).SetPSP(seg); } uint16_t env; RealPt cpmentry; RealPt dta() { return DOS_SDA(DOS_SDA_SEG, DOS_SDA_OFS).GetDTA(); } void dta(RealPt dtap) { DOS_SDA(DOS_SDA_SEG, DOS_SDA_OFS).SetDTA(dtap); } uint8_t return_code,return_mode; uint8_t 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; uint8_t* country;//Will be copied to dos memory. resides in real mem uint16_t dpb; //Fake Disk parameter system using only the first entry so the drive letter matches } tables; uint16_t loaded_codepage; uint16_t dcp; }; extern DOS_Block dos; static inline uint8_t RealHandle(uint16_t handle) { DOS_PSP psp(dos.psp()); return psp.GetFileHandle(handle); } #define DOS_DATE_FORMAT_OFS 0 #define DOS_DATE_SEPARATOR_OFS 11 #define DOS_TIME_FORMAT_OFS 17 #define DOS_TIME_SEPARATOR_OFS 13 #define DOS_THOUSANDS_SEPARATOR_OFS 7 #define DOS_DECIMAL_SEPARATOR_OFS 9 enum class Country : uint16_t { 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, }; #endif