913 lines
29 KiB
C++
913 lines
29 KiB
C++
/*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*
|
|
* Copyright (C) 2020-2023 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 <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
#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 */
|
|
static constexpr size_t MaxCmdtailBufferSize = 126;
|
|
char buffer[MaxCmdtailBufferSize + 1] = {}; /* 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 std::array<DOS_Drive*, DOS_DRIVES> 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 = nullptr);
|
|
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);
|
|
|
|
uint16_t DOS_GetBiosTimePacked();
|
|
uint16_t DOS_GetBiosDatePacked();
|
|
|
|
// 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<uint16_t>(packed);
|
|
}
|
|
|
|
constexpr uint16_t DOS_PackTime(const struct tm &datetime) noexcept
|
|
{
|
|
return DOS_PackTime(static_cast<uint16_t>(datetime.tm_hour),
|
|
static_cast<uint16_t>(datetime.tm_min),
|
|
static_cast<uint16_t>(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<uint16_t>(
|
|
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<uint16_t>(packed);
|
|
}
|
|
|
|
constexpr uint16_t DOS_PackDate(const struct tm &datetime) noexcept
|
|
{
|
|
return DOS_PackDate(static_cast<uint16_t>(datetime.tm_year + 1900),
|
|
static_cast<uint16_t>(datetime.tm_mon + 1),
|
|
static_cast<uint16_t>(datetime.tm_mday));
|
|
}
|
|
|
|
/* Routines for Drive Class */
|
|
bool DOS_OpenFile(const char* name, uint8_t flags, uint16_t* entry, bool fcb = false);
|
|
bool DOS_OpenFileExtended(const char* name, uint16_t flags,
|
|
FatAttributeFlags createAttr, uint16_t action,
|
|
uint16_t* entry, uint16_t* status);
|
|
bool DOS_CreateFile(const char* name, FatAttributeFlags attribute,
|
|
uint16_t* entry, bool fcb = false);
|
|
bool DOS_UnlinkFile(const char* const name);
|
|
bool DOS_FindFirst(const char* search, FatAttributeFlags attr,
|
|
bool fcb_findfirst = false);
|
|
bool DOS_FindNext(void);
|
|
bool DOS_Canonicalize(const char* const name, char* const canonicalized);
|
|
std::string DOS_Canonicalize(const char* const name);
|
|
bool DOS_CreateTempFile(char* const name, uint16_t* entry);
|
|
bool DOS_FileExists(const char* const name);
|
|
|
|
/* Helper Functions */
|
|
bool DOS_MakeName(const char* 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(const char* const dir);
|
|
bool DOS_MakeDir(const char* const dir);
|
|
bool DOS_RemoveDir(const char* const dir);
|
|
bool DOS_Rename(const char* const oldname, const char* const newname);
|
|
bool DOS_GetFreeDiskSpace(uint8_t drive,uint16_t * bytes,uint8_t * sectors,uint16_t * clusters,uint16_t * free);
|
|
bool DOS_GetFileAttr(const char* const name, FatAttributeFlags* attr);
|
|
bool DOS_SetFileAttr(const char* const name, FatAttributeFlags attr);
|
|
|
|
/* IOCTL Stuff */
|
|
bool DOS_IOCTL(void);
|
|
bool DOS_GetSTDINStatus();
|
|
uint8_t DOS_FindDevice(const char* 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,
|
|
const 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 */
|
|
bool DOS_IsCancelRequest();
|
|
|
|
/* 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))
|
|
|
|
class MemStruct {
|
|
public:
|
|
MemStruct() = default;
|
|
MemStruct(uint16_t seg, uint16_t off) : pt(PhysicalMake(seg, off)) {}
|
|
MemStruct(RealPt addr) : pt(RealToPhysical(addr)) {}
|
|
|
|
void SetPt(uint16_t seg) { pt = PhysicalMake(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); }
|
|
|
|
void SetVersion(const uint8_t major, const uint8_t minor)
|
|
{
|
|
SSET_BYTE(sPSP, dos_version_major, major);
|
|
SSET_BYTE(sPSP, dos_version_minor, minor);
|
|
}
|
|
uint8_t GetVersionMajor() const
|
|
{
|
|
return SGET_BYTE(sPSP, dos_version_major);
|
|
}
|
|
uint8_t GetVersionMinor() const
|
|
{
|
|
return SGET_BYTE(sPSP, dos_version_minor);
|
|
}
|
|
|
|
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;
|
|
uint8_t dos_version_major;
|
|
uint8_t dos_version_minor;
|
|
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.
|
|
*/
|
|
|
|
#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
|
|
|
|
class DOS_DTA final : public MemStruct {
|
|
public:
|
|
DOS_DTA(RealPt addr) : MemStruct(addr) {}
|
|
|
|
void SetupSearch(uint8_t drive, FatAttributeFlags attr, char* pattern);
|
|
uint8_t GetSearchDrive() const { return SGET_BYTE(sDTA, sdrive); }
|
|
void GetSearchParams(FatAttributeFlags& attr, char* pattern) const;
|
|
|
|
struct Result {
|
|
std::string name = {};
|
|
|
|
uint32_t size = 0;
|
|
uint16_t date = 0;
|
|
uint16_t time = 0;
|
|
|
|
FatAttributeFlags attr = {};
|
|
|
|
std::string GetExtension() const;
|
|
std::string GetBareName() const; // name without extension
|
|
|
|
bool IsFile() const
|
|
{
|
|
return !attr.directory && !attr.volume && !attr.device;
|
|
}
|
|
|
|
bool IsDirectory() const
|
|
{
|
|
return attr.directory;
|
|
}
|
|
|
|
bool IsDummyDirectory() const
|
|
{
|
|
return attr.directory && (name == "." || name == "..");
|
|
}
|
|
|
|
bool IsDevice() const
|
|
{
|
|
return attr.device;
|
|
}
|
|
|
|
bool IsReadOnly() const
|
|
{
|
|
return attr.read_only;
|
|
}
|
|
};
|
|
|
|
void SetResult(const char* name, uint32_t size, uint16_t date,
|
|
uint16_t time, FatAttributeFlags attr);
|
|
void GetResult(Result& result) 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); }
|
|
};
|
|
|
|
enum class ResultGrouping {
|
|
None,
|
|
FilesFirst,
|
|
NonFilesFirst,
|
|
};
|
|
|
|
enum class ResultSorting {
|
|
None,
|
|
ByName,
|
|
ByExtension,
|
|
BySize,
|
|
ByDateTime,
|
|
};
|
|
|
|
void DOS_Sort(std::vector<DOS_DTA::Result>& list, const ResultSorting sorting,
|
|
const bool reverse_order = false,
|
|
const ResultGrouping grouping = ResultGrouping::None);
|
|
|
|
/* 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(FatAttributeFlags attr);
|
|
void GetAttr(FatAttributeFlags& attr) const;
|
|
|
|
void SetResult(uint32_t size, uint16_t date, uint16_t time,
|
|
FatAttributeFlags 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(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 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);
|
|
}
|
|
|
|
/* Locale information */
|
|
|
|
enum class DosDateFormat : uint8_t {
|
|
MonthDayYear = 0,
|
|
DayMonthYear = 1,
|
|
YearMonthDay = 2,
|
|
};
|
|
|
|
enum class DosTimeFormat : uint8_t {
|
|
Time12H = 0, // AM/PM
|
|
Time24H = 1,
|
|
};
|
|
|
|
enum class DosCurrencyFormat : uint8_t {
|
|
SymbolAmount = 0,
|
|
AmountSymbol = 1,
|
|
SymbolSpaceAmount = 2,
|
|
AmountSpaceSymbol = 3,
|
|
|
|
// Some sources claim that bit 2 set means currency symbol should
|
|
// replace decimal point; so far it is unknown which (if any)
|
|
// COUNTRY.SYS uses this bit, most likely no DOS software uses it.
|
|
};
|
|
|
|
DosDateFormat DOS_GetLocaleDateFormat();
|
|
DosTimeFormat DOS_GetLocaleTimeFormat();
|
|
char DOS_GetLocaleDateSeparator();
|
|
char DOS_GetLocaleTimeSeparator();
|
|
char DOS_GetLocaleThousandsSeparator();
|
|
char DOS_GetLocaleDecimalSeparator();
|
|
char DOS_GetLocaleListSeparator();
|
|
|
|
#endif
|