First CVS upload.
Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@80
This commit is contained in:
parent
7d1ca9bdd4
commit
42e5d0b779
158 changed files with 42940 additions and 0 deletions
7
src/dos/Makefile.am
Normal file
7
src/dos/Makefile.am
Normal file
|
@ -0,0 +1,7 @@
|
|||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
noinst_LIBRARIES = libdos.a
|
||||
libdos_a_SOURCES = dos.cpp dos_devices.cpp dos_execute.cpp dos_files.cpp dos_ioctl.cpp dos_memory.cpp \
|
||||
dos_misc.cpp dos_classes.cpp dos_programs.cpp dos_tables.cpp \
|
||||
drives.cpp drives.h drive_virtual.cpp drive_local.cpp \
|
||||
dev_con.h
|
105
src/dos/dev_con.h
Normal file
105
src/dos/dev_con.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
class device_CON : public DOS_Device {
|
||||
public:
|
||||
device_CON();
|
||||
bool Read(Bit8u * data,Bit16u * size);
|
||||
bool Write(Bit8u * data,Bit16u * size);
|
||||
bool Seek(Bit32u * pos,Bit32u type);
|
||||
bool Close();
|
||||
Bit16u GetInformation(void);
|
||||
private:
|
||||
Bit8u cache;
|
||||
};
|
||||
|
||||
bool device_CON::Read(Bit8u * data,Bit16u * size) {
|
||||
Bit16u oldax=reg_ax;
|
||||
Bit16u count=0;
|
||||
if ((cache) && (*size)) {
|
||||
data[count++]=cache;
|
||||
cache=0;
|
||||
}
|
||||
while (*size>count) {
|
||||
reg_ah=0;
|
||||
CALLBACK_RunRealInt(0x16);
|
||||
switch(reg_al) {
|
||||
case 13:
|
||||
data[count++]=0x0D;
|
||||
// if (*size>count) data[count++]=0x0A;
|
||||
// else cache=0x0A;
|
||||
*size=count;
|
||||
reg_ax=oldax;
|
||||
return true;
|
||||
default:
|
||||
data[count++]=reg_al;
|
||||
break;
|
||||
case 0:
|
||||
data[count++]=reg_al;
|
||||
if (*size>count) data[count++]=reg_ah;
|
||||
else cache=reg_ah;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*size=count;
|
||||
reg_ax=oldax;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern void INT10_TeletypeOutput(Bit8u chr,Bit8u attr,bool showattr, Bit8u page);
|
||||
bool device_CON::Write(Bit8u * data,Bit16u * size) {
|
||||
//TODO Hack a way to call int 0x10
|
||||
Bit16u oldax=reg_ax;Bit16u oldbx=reg_bx;
|
||||
Bit16u count=0;
|
||||
while (*size>count) {
|
||||
/*
|
||||
reg_al=data[count];
|
||||
reg_ah=0x0e;
|
||||
reg_bx=0x0007;
|
||||
CALLBACK_RunRealInt(0x10);
|
||||
*/
|
||||
INT10_TeletypeOutput(data[count],7,false,0);
|
||||
count++;
|
||||
}
|
||||
*size=count;
|
||||
// reg_ax=oldax;reg_bx=oldbx;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_CON::Seek(Bit32u * pos,Bit32u type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_CON::Close() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bit16u device_CON::GetInformation(void) {
|
||||
Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
|
||||
Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
|
||||
|
||||
if ((head==tail) && !cache) return 0x80D3; /* No Key Available */
|
||||
return 0x8093; /* Key Available */
|
||||
};
|
||||
|
||||
|
||||
device_CON::device_CON() {
|
||||
name="CON";
|
||||
cache=0;
|
||||
}
|
||||
|
790
src/dos/dos.cpp
Normal file
790
src/dos/dos.cpp
Normal file
|
@ -0,0 +1,790 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include "dosbox.h"
|
||||
#include "bios.h"
|
||||
#include "mem.h"
|
||||
#include "callback.h"
|
||||
#include "regs.h"
|
||||
#include "dos_inc.h"
|
||||
|
||||
DOS_Block dos;
|
||||
static Bit8u dos_copybuf[0x10000];
|
||||
static Bitu call_20,call_21,call_theend;
|
||||
|
||||
void DOS_SetError(Bit16u code) {
|
||||
dos.errorcode=code;
|
||||
};
|
||||
|
||||
|
||||
#define DOSNAMEBUF 256
|
||||
static Bitu DOS_21Handler(void) {
|
||||
//TODO KEYBOARD Check for break
|
||||
char name1[DOSNAMEBUF+1];
|
||||
char name2[DOSNAMEBUF+1];
|
||||
switch (reg_ah) {
|
||||
case 0x00: /* Terminate Program */
|
||||
E_Exit("DOS:Unhandled call %02X",reg_ah);
|
||||
break;
|
||||
case 0x01: /* Read character from STDIN, with echo */
|
||||
{
|
||||
Bit8u c;Bit16u n=1;
|
||||
DOS_ReadFile(STDIN,&c,&n);
|
||||
reg_al=c;
|
||||
DOS_WriteFile(STDOUT,&c,&n);
|
||||
}
|
||||
break;
|
||||
case 0x02: /* Write character to STDOUT */
|
||||
{
|
||||
Bit8u c=reg_dl;Bit16u n=1;
|
||||
DOS_WriteFile(STDOUT,&c,&n);
|
||||
}
|
||||
break;
|
||||
case 0x03: /* Read character from STDAUX */
|
||||
case 0x04: /* Write Character to STDAUX */
|
||||
case 0x05: /* Write Character to PRINTER */
|
||||
E_Exit("DOS:Unhandled call %02X",reg_ah);
|
||||
break;
|
||||
case 0x06: /* Direct Console Output / Input */
|
||||
switch (reg_dl) {
|
||||
case 0xFF: /* Input */
|
||||
{
|
||||
//TODO Make this better according to standards
|
||||
if (!DOS_GetSTDINStatus()) {
|
||||
CALLBACK_SZF(true);
|
||||
break;
|
||||
}
|
||||
Bit8u c;Bit16u n=1;
|
||||
DOS_ReadFile(STDIN,&c,&n);
|
||||
reg_al=c;
|
||||
CALLBACK_SZF(false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Bit8u c=reg_dl;Bit16u n=1;
|
||||
DOS_WriteFile(STDOUT,&c,&n);
|
||||
}
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case 0x07: /* Character Input, without echo */
|
||||
{
|
||||
Bit8u c;Bit16u n=1;
|
||||
DOS_ReadFile (STDIN,&c,&n);
|
||||
reg_al=c;
|
||||
break;
|
||||
};
|
||||
case 0x08: /* Direct Character Input, without echo */
|
||||
{
|
||||
Bit8u c;Bit16u n=1;
|
||||
DOS_ReadFile (STDIN,&c,&n);
|
||||
reg_al=c;
|
||||
break;
|
||||
};
|
||||
case 0x09: /* Write string to STDOUT */
|
||||
{
|
||||
Bit8u c;Bit16u n=1;
|
||||
PhysPt buf=real_phys(Segs[ds].value,reg_dx);
|
||||
while ((c=mem_readb(buf++))!='$') {
|
||||
DOS_WriteFile(STDOUT,&c,&n);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0a: /* Buffered Input */
|
||||
{
|
||||
//TODO ADD Break checkin in STDIN but can't care that much for it
|
||||
PhysPt data=real_phys(Segs[ds].value,reg_dx);
|
||||
Bit8u free=mem_readb(data);
|
||||
Bit8u read=0;Bit8u c;Bit16u n=1;
|
||||
if (!free) break;
|
||||
while (read<free) {
|
||||
DOS_ReadFile(STDIN,&c,&n);
|
||||
DOS_WriteFile(STDOUT,&c,&n);
|
||||
if (c==13) {
|
||||
DOS_ReadFile(STDIN,&c,&n);
|
||||
break;
|
||||
}
|
||||
mem_writeb(data+read+2,c);
|
||||
read++;
|
||||
};
|
||||
mem_writeb(data+1,read);
|
||||
break;
|
||||
};
|
||||
case 0x0b: /* Get STDIN Status */
|
||||
if (DOS_GetSTDINStatus()) reg_al=0xff;
|
||||
else reg_al=0;
|
||||
break;
|
||||
case 0x0c: /* Flush Buffer and read STDIN call */
|
||||
{
|
||||
switch (reg_al) {
|
||||
case 0x1:
|
||||
case 0x6:
|
||||
case 0x7:
|
||||
case 0x8:
|
||||
case 0xa:
|
||||
{
|
||||
Bit8u oldah=reg_ah;
|
||||
reg_ah=reg_al;
|
||||
DOS_21Handler();
|
||||
reg_ah=oldah;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("DOS:0C:Illegal Flush STDIN Buffer call %d",reg_al);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
//TODO Find out the values for when reg_al!=0
|
||||
//TODO Hope this doesn't do anything special
|
||||
case 0x0d: /* Disk Reset */
|
||||
//Sure let's reset a virtual disk
|
||||
break;
|
||||
case 0x0e: /* Select Default Drive */
|
||||
DOS_SetDefaultDrive(reg_dl);
|
||||
reg_al=26;
|
||||
break;
|
||||
case 0x0f: /* Open File using FCB */
|
||||
case 0x10: /* Close File using FCB */
|
||||
case 0x11: /* Find First Matching File using FCB */
|
||||
case 0x12: /* Find Next Matching File using FCB */
|
||||
case 0x13: /* Delete File using FCB */
|
||||
case 0x14: /* Sequential read from FCB */
|
||||
case 0x15: /* Sequential write to FCB */
|
||||
case 0x16: /* Create or truncate file using FCB */
|
||||
case 0x17: /* Rename file using FCB */
|
||||
case 0x21: /* Read random record from FCB */
|
||||
case 0x22: /* Write random record to FCB */
|
||||
case 0x23: /* Get file size for FCB */
|
||||
case 0x24: /* Set Random Record number for FCB */
|
||||
case 0x27: /* Random block read from FCB */
|
||||
case 0x28: /* Random Block read to FCB */
|
||||
LOG_ERROR("DOS:Unhandled call %02X, FCB Stuff",reg_ah);
|
||||
reg_al=0xff; /* FCB Calls FAIL */
|
||||
CALLBACK_SCF(true);
|
||||
break;
|
||||
case 0x29: /* Parse filename into FCB */
|
||||
//TODO Give errors for unsupported functions
|
||||
{
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_si),name1,DOSNAMEBUF);
|
||||
/* only detect the call program use to detect the existence of a harddisk */
|
||||
if ((strlen((char *)name1)==2) && (name1[1]==':')) {
|
||||
Bit8u drive=toupper(name1[0])-'A';
|
||||
if (Drives[drive]) reg_al=0;
|
||||
else reg_al=0xff;
|
||||
break;
|
||||
}
|
||||
LOG_DEBUG("DOS:29:FCB Parse Filename:%s",name1);
|
||||
};
|
||||
reg_al=0xff; /* FCB Calls FAIL */
|
||||
break;
|
||||
case 0x18: /* NULL Function for CP/M compatibility or Extended rename FCB */
|
||||
case 0x1d: /* NULL Function for CP/M compatibility or Extended rename FCB */
|
||||
case 0x1e: /* NULL Function for CP/M compatibility or Extended rename FCB */
|
||||
case 0x20: /* NULL Function for CP/M compatibility or Extended rename FCB */
|
||||
case 0x6b: /* NULL Function */
|
||||
case 0x61: /* UNUSED */
|
||||
reg_al=0;
|
||||
break;
|
||||
case 0x19: /* Get current default drive */
|
||||
reg_al=DOS_GetDefaultDrive();
|
||||
break;
|
||||
case 0x1a: /* Set Disk Transfer Area Address */
|
||||
//TODO find out what a DTA does
|
||||
dos.dta=RealMake(Segs[ds].value,reg_dx);
|
||||
break;
|
||||
case 0x1c: /* Get allocation info for specific drive */
|
||||
LOG_DEBUG("DOS: Allocation Info call not supported correctly");
|
||||
SetSegment_16(ds,0xf000);
|
||||
reg_bx=0;
|
||||
real_writeb(0xf000,0,0);
|
||||
reg_al=0x7f;
|
||||
reg_cx=0x200;
|
||||
reg_dx=0x1000;
|
||||
break; /* TODO maybe but hardly think a game needs this */
|
||||
case 0x1b: /* Get allocation info for default drive */
|
||||
LOG_DEBUG("DOS: Allocation Info call not supported correctly");
|
||||
SetSegment_16(ds,0xf000);
|
||||
reg_bx=0;
|
||||
real_writeb(0xf000,0,0);
|
||||
reg_al=0x7f;
|
||||
reg_cx=0x200;
|
||||
reg_dx=0x1000;
|
||||
break;
|
||||
case 0x1f: /* Get drive parameter block for default drive */
|
||||
case 0x32: /* Get drive parameter block for specific drive */
|
||||
E_Exit("DOS:Unhandled call %02X",reg_ah);
|
||||
break; /* TODO maybe but hardly think a game needs this */
|
||||
case 0x25: /* Set Interrupt Vector */
|
||||
RealSetVec(reg_al,RealMake(Segs[ds].value,reg_dx));
|
||||
break;
|
||||
case 0x26: /* Create new PSP */
|
||||
DOS_NewPSP(reg_dx);
|
||||
break;
|
||||
case 0x2a: /* Get System Date */
|
||||
reg_al=0; /* It's always sunday TODO find that correct formula */
|
||||
reg_cx=dos.date.year;
|
||||
reg_dh=dos.date.month;
|
||||
reg_dl=dos.date.day;
|
||||
break;
|
||||
case 0x2b: /* Set System Date */
|
||||
//TODO Check for months with less then 31 days
|
||||
if (reg_cx<1980) { reg_al=0xff;break;}
|
||||
if ((reg_dh>12) || (reg_dh==0)) { reg_al=0xff;break;}
|
||||
if ((reg_dl>31) || (reg_dl==0)) { reg_al=0xff;break;}
|
||||
dos.date.year=reg_cx;
|
||||
dos.date.month=reg_dh;
|
||||
dos.date.day=reg_dl;
|
||||
reg_al=0;
|
||||
break;
|
||||
case 0x2c: /* Get System Time */
|
||||
//TODO Get time through bios calls date is fixed
|
||||
{
|
||||
Bit32u ticks=mem_readd(BIOS_TIMER);
|
||||
Bit32u seconds=(ticks*10)/182;
|
||||
reg_ch=(Bit8u)(seconds/3600);
|
||||
reg_cl=(Bit8u)(seconds % 3600)/60;
|
||||
reg_dh=(Bit8u)(seconds % 60);
|
||||
reg_dl=(Bit8u)(ticks % 19)*5;
|
||||
}
|
||||
break;
|
||||
case 0x2d: /* Set System Time */
|
||||
LOG_DEBUG("DOS:Set System Time not supported");
|
||||
reg_al=0; /* Noone is changing system time */
|
||||
break;
|
||||
case 0x2e: /* Set Verify flag */
|
||||
dos.verify=(reg_al==1);
|
||||
break;
|
||||
case 0x2f: /* Get Disk Transfer Area */
|
||||
SetSegment_16(es,RealSeg(dos.dta));
|
||||
reg_bx=RealOff(dos.dta);
|
||||
break;
|
||||
case 0x30: /* Get DOS Version */
|
||||
if (reg_al==0) reg_bh=0xFF; /* Fake Microsoft DOS */
|
||||
if (reg_al==1) reg_bh=0x10; /* DOS is in HMA */
|
||||
reg_al=dos.version.major;
|
||||
reg_ah=dos.version.minor;
|
||||
break;
|
||||
case 0x31: /* Terminate and stay resident */
|
||||
//TODO First get normal files executing
|
||||
DOS_ResizeMemory(dos.psp,®_dx);
|
||||
if (DOS_Terminate(true)) {
|
||||
dos.return_code=reg_al;
|
||||
dos.return_mode=RETURN_TSR;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x33: /* Extended Break Checking */
|
||||
switch (reg_al) {
|
||||
case 0:reg_dl=dos.breakcheck;break; /* Get the breakcheck flag */
|
||||
case 1:dos.breakcheck=(reg_dl>0);break; /* Set the breakcheck flag */
|
||||
case 2:{bool old=dos.breakcheck;dos.breakcheck=(reg_dl>0);reg_dl=old;}break;
|
||||
case 5:reg_dl=3;break; /* Always boot from c: :) */
|
||||
case 6: /* Get true version number */
|
||||
reg_bl=dos.version.major;
|
||||
reg_bh=dos.version.minor;
|
||||
reg_dl=dos.version.revision;
|
||||
reg_dh=0x10; /* Dos in HMA */
|
||||
break;
|
||||
default:
|
||||
E_Exit("DOS:Illegal 0x33 Call %2X",reg_al);
|
||||
}
|
||||
break;
|
||||
case 0x34: /* Get INDos Flag */
|
||||
SetSegment_16(es,RealSeg(dos.tables.indosflag));
|
||||
reg_bx=RealOff(dos.tables.indosflag);
|
||||
break;
|
||||
case 0x35: /* Get interrupt vector */
|
||||
reg_bx=real_readw(0,((Bit16u)reg_al)*4);
|
||||
SetSegment_16(es,real_readw(0,((Bit16u)reg_al)*4+2));
|
||||
break;
|
||||
case 0x36: /* Get Free Disk Space */
|
||||
{
|
||||
Bit16u bytes,sectors,clusters,free;
|
||||
if (DOS_GetFreeDiskSpace(reg_dl,&bytes,§ors,&clusters,&free)) {
|
||||
reg_ax=sectors;
|
||||
reg_bx=free;
|
||||
reg_cx=bytes;
|
||||
reg_dx=clusters;
|
||||
} else {
|
||||
reg_ax=0xffff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x37: /* Get/Set Switch char Get/Set Availdev thing */
|
||||
//TODO Give errors for these functions to see if anyone actually uses this shit-
|
||||
switch (reg_al) {
|
||||
case 0:
|
||||
reg_al=0;reg_dl=0x2f;break; /* always return '/' like dos 5.0+ */
|
||||
case 1:
|
||||
reg_al=0;break;
|
||||
case 2:
|
||||
reg_al=0;reg_dl=0x2f;break;
|
||||
case 3:
|
||||
reg_al=0;break;
|
||||
};
|
||||
LOG_DEBUG("DOS:0x37:Call for not supported switchchar");
|
||||
break;
|
||||
case 0x38: /* Set Country Code */
|
||||
LOG_DEBUG("DOS:Setting country code not supported");
|
||||
CALLBACK_SCF(true);
|
||||
break;
|
||||
if (reg_al==0) { /* Get country specidic information */
|
||||
|
||||
} else { /* Set country code */
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case 0x39: /* MKDIR Create directory */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_MakeDir(name1)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x3a: /* RMDIR Remove directory */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_RemoveDir(name1)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x3b: /* CHDIR Set current directory */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_ChangeDir(name1)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x3c: /* CREATE Create of truncate file */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_CreateFile(name1,reg_cx,®_ax)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x3d: /* OPEN Open existing file */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_OpenFile(name1,reg_al,®_ax)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x3e: /* CLOSE Close file */
|
||||
if (DOS_CloseFile(reg_bx)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x3f: /* READ Read from file or device */
|
||||
{
|
||||
Bit16u toread=reg_cx;
|
||||
if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) {
|
||||
MEM_BlockWrite(real_phys(Segs[ds].value,reg_dx),dos_copybuf,toread);
|
||||
reg_ax=toread;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x40: /* WRITE Write to file or device */
|
||||
{
|
||||
Bit16u towrite=reg_cx;
|
||||
MEM_BlockRead(real_phys(Segs[ds].value,reg_dx),dos_copybuf,towrite);
|
||||
if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) {
|
||||
reg_ax=towrite;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
};
|
||||
case 0x41: /* UNLINK Delete file */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_UnlinkFile(name1)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x42: /* LSEEK Set current file position */
|
||||
{
|
||||
Bit32u pos=(reg_cx<<16) + reg_dx;
|
||||
if (DOS_SeekFile(reg_bx,&pos,reg_al)) {
|
||||
reg_dx=(Bit16u)(pos >> 16);
|
||||
reg_ax=(Bit16u)(pos & 0xFFFF);
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x43: /* Get/Set file attributes */
|
||||
//TODO FIX THIS HACK
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
switch (reg_al)
|
||||
case 0x00: /* Get */
|
||||
{
|
||||
if (DOS_GetFileAttr(name1,®_cx)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
CALLBACK_SCF(true);
|
||||
reg_ax=dos.errorcode;
|
||||
}
|
||||
break;
|
||||
case 0x01: /* Set */
|
||||
LOG_DEBUG("DOS:Set File Attributes for %s not supported",name1);
|
||||
CALLBACK_SCF(false);
|
||||
break;
|
||||
default:
|
||||
E_Exit("DOS:0x43:Illegal subfunction %2X",reg_al);
|
||||
}
|
||||
break;
|
||||
case 0x44: /* IOCTL Functions */
|
||||
if (DOS_IOCTL(reg_al,reg_bx)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x45: /* DUP Duplicate file handle */
|
||||
if (DOS_DuplicateEntry(reg_bx,®_ax)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x46: /* DUP2,FORCEDUP Force duplicate file handle */
|
||||
E_Exit("Unhandled Dos 21 call %02X",reg_ah);
|
||||
break;
|
||||
case 0x47: /* CWD Get current directory */
|
||||
//TODO Memory
|
||||
if (DOS_GetCurrentDir(reg_dl,real_off(Segs[ds].value,reg_si))) {
|
||||
reg_ax=0x0100;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x48: /* Allocate memory */
|
||||
{
|
||||
Bit16u size=reg_bx;Bit16u seg;
|
||||
if (DOS_AllocateMemory(&seg,&size)) {
|
||||
reg_ax=seg;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
reg_bx=size;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x49: /* Free memory */
|
||||
if (DOS_FreeMemory(Segs[es].value)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x4a: /* Resize memory block */
|
||||
{
|
||||
Bit16u size=reg_bx;
|
||||
if (DOS_ResizeMemory(Segs[es].value,&size)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
reg_bx=size;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x4b: /* EXEC Load and/or execute program */
|
||||
{
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_Execute(name1,(ParamBlock *)real_off(Segs[es].value,reg_bx),reg_al)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
//TODO Check for use of execution state AL=5
|
||||
case 0x4c: /* EXIT Terminate with return code */
|
||||
{
|
||||
if (DOS_Terminate(false)) {
|
||||
dos.return_code=reg_al;
|
||||
dos.return_mode=RETURN_EXIT;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x4d: /* Get Return code */
|
||||
reg_al=dos.return_code;
|
||||
reg_ah=dos.return_mode;
|
||||
break;
|
||||
case 0x4e: /* FINDFIRST Find first matching file */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_FindFirst(name1,reg_cx)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
};
|
||||
break;
|
||||
case 0x4f: /* FINDNEXT Find next matching file */
|
||||
if (DOS_FindNext()) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
};
|
||||
break;
|
||||
case 0x50: /* Set current PSP */
|
||||
dos.psp=reg_bx;
|
||||
break;
|
||||
case 0x51: /* Get current PSP */
|
||||
reg_bx=dos.psp;
|
||||
break;
|
||||
case 0x52: /* Get list of lists */
|
||||
SetSegment_16(es,0);
|
||||
reg_bx=0;
|
||||
LOG_ERROR("Call is made for list of lists not supported let's hope for the best");
|
||||
break;
|
||||
//TODO Think hard how shit this is gonna be
|
||||
//And will any game ever use this :)
|
||||
case 0x53: /* Translate BIOS parameter block to drive parameter block */
|
||||
//YEAH RIGHT
|
||||
case 0x54: /* Get verify flag */
|
||||
case 0x55: /* Create Child PSP*/
|
||||
E_Exit("Unhandled Dos 21 call %02X",reg_ah);
|
||||
break;
|
||||
case 0x56: /* RENAME Rename file */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
MEM_StrCopy(real_phys(Segs[es].value,reg_di),name2,DOSNAMEBUF);
|
||||
if (DOS_Rename(name1,name2)) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x57: /* Get/Set File's Date and Time */
|
||||
reg_cx=0;
|
||||
reg_dx=0;
|
||||
LOG_DEBUG("DOS:57:Getting/Setting File Date is faked",reg_ah);
|
||||
break;
|
||||
case 0x58: /* Get/Set Memory allocation strategy */
|
||||
LOG_DEBUG("DOS:58:Not Supported Set//Get memory allocation");
|
||||
break;
|
||||
case 0x59: /* Get Extended error information */
|
||||
E_Exit("Unhandled Dos 21 call %02X",reg_ah);
|
||||
break;
|
||||
case 0x5a: /* Create temporary file */
|
||||
{
|
||||
Bit16u handle;
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_CreateTempFile(name1,&handle)) {
|
||||
reg_ax=handle;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x5b: /* Create new file */
|
||||
{
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
Bit16u handle;
|
||||
if (DOS_OpenFile(name1,0,&handle)) {
|
||||
DOS_CloseFile(handle);
|
||||
DOS_SetError(DOSERR_ACCESS_DENIED);
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(false);
|
||||
break;
|
||||
}
|
||||
if (DOS_CreateFile(name1,reg_cx,&handle)) {
|
||||
reg_ax=handle;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x5c: /* FLOCK File region locking */
|
||||
case 0x5d: /* Network Functions */
|
||||
case 0x5e: /* More Network Functions */
|
||||
case 0x5f: /* And Even More Network Functions */
|
||||
E_Exit("DOS:Unhandled call %02X",reg_ah);
|
||||
break;
|
||||
case 0x60: /* Canonicalize filename or path */
|
||||
MEM_StrCopy(real_phys(Segs[ds].value,reg_dx),name1,DOSNAMEBUF);
|
||||
if (DOS_Canonicalize(name1,real_off(Segs[es].value,reg_di))) {
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax=dos.errorcode;
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
break;
|
||||
case 0x62: /* Get Current PSP Address */
|
||||
reg_bx=dos.psp;
|
||||
break;
|
||||
case 0x63: /* Weirdo double byte stuff */
|
||||
reg_al=0xff;
|
||||
LOG_WARN("DOS:0x63:Doubly byte characters not supported");
|
||||
break;
|
||||
case 0x64: /* Set device driver lookahead flag */
|
||||
E_Exit("Unhandled Dos 21 call %02X",reg_ah);
|
||||
break;
|
||||
case 0x65: /* Get extented country information and a lot of other useless shit*/
|
||||
/* Todo maybe fully support this for now we set it standard for USA */
|
||||
{
|
||||
LOG_DEBUG("DOS:65:Extended country information call");
|
||||
Bit8u * data=real_off(Segs[es].value,reg_di);
|
||||
switch (reg_al) {
|
||||
case 1:
|
||||
real_writeb(Segs[es].value,reg_di,reg_al);
|
||||
real_writew(Segs[es].value,reg_di+1,4);
|
||||
real_writew(Segs[es].value,reg_di+3,1);
|
||||
real_writew(Segs[es].value,reg_di+5,37);
|
||||
reg_cx=4;
|
||||
CALLBACK_SCF(false);
|
||||
break;
|
||||
default:
|
||||
E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al);
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 0x66: /* Get/Set global code page table */
|
||||
if (reg_al==1) {
|
||||
LOG_DEBUG("Getting global code page table");
|
||||
reg_bx=reg_dx=437;
|
||||
CALLBACK_SCF(false);
|
||||
break;
|
||||
}
|
||||
LOG_ERROR("DOS:Setting code page table is not supported");
|
||||
break;
|
||||
case 0x67: /* Set handle countr */
|
||||
/* Weird call to increase amount of file handles needs to allocate memory if >20 */
|
||||
LOG_DEBUG("DOS:67:Set Handle Count not working");
|
||||
CALLBACK_SCF(false);
|
||||
break;
|
||||
case 0x68: /* FFLUSH Commit file */
|
||||
E_Exit("Unhandled Dos 21 call %02X",reg_ah);
|
||||
break;
|
||||
case 0x69: /* Get/Set disk serial number */
|
||||
{
|
||||
Bit8u * temp=real_off(Segs[ds].value,reg_dx);
|
||||
switch(reg_al) {
|
||||
case 0x00: /* Get */
|
||||
LOG_DEBUG("DOS:Get Disk serial number");
|
||||
CALLBACK_SCF(true);
|
||||
break;
|
||||
case 0x01:
|
||||
LOG_DEBUG("DOS:Set Disk serial number");
|
||||
default:
|
||||
E_Exit("DOS:Illegal Get Serial Number call %2X",reg_al);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x6c: /* Extended Open/Create */
|
||||
E_Exit("Unhandled Dos 21 call %02X",reg_ah);
|
||||
break;
|
||||
case 0x71: /* Unknown probably 4dos detection */
|
||||
reg_ax=0x7100;
|
||||
CALLBACK_SCF(true);
|
||||
LOG_WARN("DOS:Windows long file name support call %2X",reg_al);
|
||||
break;
|
||||
case 0xE0:
|
||||
LOG_DEBUG("DOS:E0:Unhandled, what should this call do?");
|
||||
break;
|
||||
default:
|
||||
E_Exit("DOS:Unhandled call %02X",reg_ah);
|
||||
break;
|
||||
};
|
||||
return CBRET_NONE;
|
||||
/* That's it now let's get it working */
|
||||
};
|
||||
|
||||
|
||||
|
||||
static Bitu DOS_20Handler(void) {
|
||||
|
||||
reg_ax=0x4c00;
|
||||
DOS_21Handler();
|
||||
return CBRET_NONE;
|
||||
}
|
||||
|
||||
|
||||
void DOS_Init(void) {
|
||||
call_20=CALLBACK_Allocate();
|
||||
CALLBACK_Setup(call_20,DOS_20Handler,CB_IRET);
|
||||
RealSetVec(0x20,CALLBACK_RealPointer(call_20));
|
||||
|
||||
call_21=CALLBACK_Allocate();
|
||||
CALLBACK_Setup(call_21,DOS_21Handler,CB_IRET);
|
||||
RealSetVec(0x21,CALLBACK_RealPointer(call_21));
|
||||
|
||||
DOS_SetupFiles(); /* Setup system File tables */
|
||||
DOS_SetupDevices(); /* Setup dos devices */
|
||||
DOS_SetupMemory(); /* Setup first MCB */
|
||||
DOS_SetupTables();
|
||||
DOS_SetupPrograms();
|
||||
DOS_SetupMisc(); /* Some additional dos interrupts */
|
||||
DOS_SetDefaultDrive(25);
|
||||
/* Execute the file that should be */
|
||||
dos.version.major=5;
|
||||
dos.version.minor=0;
|
||||
// DOS_RunProgram(startname);
|
||||
};
|
181
src/dos/dos_classes.cpp
Normal file
181
src/dos/dos_classes.cpp
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "dosbox.h"
|
||||
#include "mem.h"
|
||||
#include "dos_inc.h"
|
||||
|
||||
/*
|
||||
Work in progress, making classes for handling certain internal memory structures in dos
|
||||
This should make it somewhat easier for porting to other endian machines and make
|
||||
dos work a bit easier.
|
||||
*/
|
||||
|
||||
|
||||
struct sPSP {
|
||||
Bit8u exit[2]; /* CP/M-like exit poimt */
|
||||
Bit16u next_seg; /* Segment of first byte beyond memory allocated or program */
|
||||
Bit8u fill_1; /* single char fill */
|
||||
|
||||
/* CPM Stuff dunno what this is*/
|
||||
//TODO Add some checks for people using this i think
|
||||
Bit8u far_call; /* far call opcode */
|
||||
RealPt cpm_entry; /* CPM Service Request address*/
|
||||
RealPt int_22; /* Terminate Address */
|
||||
RealPt int_23; /* Break Address */
|
||||
RealPt int_24; /* Critical Error Address */
|
||||
Bit16u psp_parent; /* Parent PSP Segment */
|
||||
Bit8u files[20]; /* File Table - 0xff is unused */
|
||||
Bit16u environment; /* Segment of evironment table */
|
||||
RealPt stack; /* SS:SP Save point for int 0x21 calls */
|
||||
Bit16u max_files; /* Maximum open files */
|
||||
RealPt file_table; /* Pointer to File Table PSP:0x18 */
|
||||
RealPt prev_psp; /* Pointer to previous PSP */
|
||||
RealPt dta; /* Pointer to current Process DTA */
|
||||
Bit8u fill_2[16]; /* Lot's of unused stuff i can't care aboue */
|
||||
Bit8u service[3]; /* INT 0x21 Service call int 0x21;retf; */
|
||||
Bit8u fill_3[45]; /* This has some blocks with FCB info */
|
||||
|
||||
CommandTail cmdtail;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DOS_PSP {
|
||||
public:
|
||||
DOS_PSP(Bit16u segment);
|
||||
|
||||
void MakeNew(Bit16u env,Bit16u memsize);
|
||||
|
||||
|
||||
Bit16u base_seg;
|
||||
|
||||
private:
|
||||
PhysPt off;
|
||||
};
|
||||
|
||||
DOS_PSP::DOS_PSP(Bit16u segment) {
|
||||
base_seg=segment;
|
||||
off=Real2Phys(RealMake(segment,0));
|
||||
};
|
||||
|
||||
void DOS_PSP::MakeNew(Bit16u env,Bit16u next_para) {
|
||||
Bitu i;
|
||||
for (i=0;i<256;i++) mem_writeb(off+i,0);
|
||||
/* Standard blocks */
|
||||
mem_writeb(off+offsetof(sPSP,exit[0]),0xcd);
|
||||
mem_writeb(off+offsetof(sPSP,exit[1]),0x20);
|
||||
|
||||
mem_writeb(off+offsetof(sPSP,service[0]),0xcd);
|
||||
mem_writeb(off+offsetof(sPSP,service[1]),0x21);
|
||||
mem_writeb(off+offsetof(sPSP,service[2]),0xcb);
|
||||
|
||||
mem_writew(off+offsetof(sPSP,next_seg),next_para);
|
||||
|
||||
|
||||
// mem_writew(off+offsetof(sPSP,psp_parent),dos.psp->base_seg);
|
||||
|
||||
/* Setup initial file table */
|
||||
mem_writed(off+offsetof(sPSP,int_22),RealGetVec(0x22));
|
||||
mem_writed(off+offsetof(sPSP,int_23),RealGetVec(0x23));
|
||||
mem_writed(off+offsetof(sPSP,int_24),RealGetVec(0x24));
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
newpsp->mem_size=prevpsp->mem_size;
|
||||
newpsp->environment=0;
|
||||
|
||||
newpsp->int_22.full=real_getvec(0x22);
|
||||
newpsp->int_23.full=real_getvec(0x23);
|
||||
newpsp->int_24.full=real_getvec(0x24);
|
||||
|
||||
newpsp->psp_parent=dos.psp;
|
||||
newpsp->prev_psp.full=0xFFFFFFFF;
|
||||
|
||||
Bit32u i;
|
||||
Bit8u * prevfile=real_off(prevpsp->file_table.seg,prevpsp->file_table.off);
|
||||
for (i=0;i<20;i++) newpsp->files[i]=prevfile[i];
|
||||
|
||||
newpsp->max_files=20;
|
||||
newpsp->file_table.seg=pspseg;
|
||||
newpsp->file_table.off=offsetof(PSP,files);
|
||||
/* Save the old DTA in this psp */
|
||||
newpsp->dta.seg=dos.dta.seg;
|
||||
newpsp->dta.off=dos.dta.off;
|
||||
/* Setup the DTA */
|
||||
dos.dta.seg=pspseg;
|
||||
dos.dta.off=0x80;
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DOS_FCB::Set_drive(Bit8u a){
|
||||
mem_writeb(off+offsetof(FCB,drive),a);
|
||||
}
|
||||
void DOS_FCB::Set_filename(char * a){
|
||||
MEM_BlockWrite(off+offsetof(FCB,filename),a,8);
|
||||
}
|
||||
void DOS_FCB::Set_ext(char * a) {
|
||||
MEM_BlockWrite(off+offsetof(FCB,ext),a,3);
|
||||
}
|
||||
void DOS_FCB::Set_current_block(Bit16u a){
|
||||
mem_writew(off+offsetof(FCB,current_block),a);
|
||||
}
|
||||
void DOS_FCB::Set_record_size(Bit16u a){
|
||||
mem_writew(off+offsetof(FCB,record_size),a);
|
||||
}
|
||||
void DOS_FCB::Set_filesize(Bit32u a){
|
||||
mem_writed(off+offsetof(FCB,filesize),a);
|
||||
}
|
||||
void DOS_FCB::Set_date(Bit16u a){
|
||||
mem_writew(off+offsetof(FCB,date),a);
|
||||
}
|
||||
void DOS_FCB::Set_time(Bit16u a){
|
||||
mem_writew(off+offsetof(FCB,time),a);
|
||||
}
|
||||
Bit8u DOS_FCB::Get_drive(void){
|
||||
return mem_readb(off+offsetof(FCB,drive));
|
||||
}
|
||||
void DOS_FCB::Get_filename(char * a){
|
||||
MEM_BlockRead(off+offsetof(FCB,filename),a,8);
|
||||
}
|
||||
void DOS_FCB::Get_ext(char * a){
|
||||
MEM_BlockRead(off+offsetof(FCB,ext),a,3);
|
||||
}
|
||||
Bit16u DOS_FCB::Get_current_block(void){
|
||||
return mem_readw(off+offsetof(FCB,current_block));
|
||||
}
|
||||
Bit16u DOS_FCB::Get_record_size(void){
|
||||
return mem_readw(off+offsetof(FCB,record_size));
|
||||
}
|
||||
Bit32u DOS_FCB::Get_filesize(void){
|
||||
return mem_readd(off+offsetof(FCB,filesize));
|
||||
}
|
||||
Bit16u DOS_FCB::Get_date(void){
|
||||
return mem_readw(off+offsetof(FCB,date));
|
||||
}
|
||||
Bit16u DOS_FCB::Get_time(void){
|
||||
return mem_readw(off+offsetof(FCB,time));
|
||||
}
|
77
src/dos/dos_devices.cpp
Normal file
77
src/dos/dos_devices.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "dosbox.h"
|
||||
#include "callback.h"
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
#include "bios.h"
|
||||
#include "dos_inc.h"
|
||||
#include "support.h"
|
||||
|
||||
#define MAX_DEVICES 10
|
||||
/* Include all the devices */
|
||||
|
||||
#include "dev_con.h"
|
||||
|
||||
|
||||
static DOS_Device * devices[MAX_DEVICES];
|
||||
static Bit32u device_count;
|
||||
|
||||
Bit8u DOS_FindDevice(char * name) {
|
||||
/* loop through devices */
|
||||
Bit8u index=0;
|
||||
while (index<device_count) {
|
||||
if (devices[index]) {
|
||||
if (strcasecmp(name,devices[index]->name)==0) return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 255;
|
||||
}
|
||||
|
||||
|
||||
void DOS_AddDevice(DOS_Device * adddev) {
|
||||
//TODO Give the Device a real handler in low memory that responds to calls
|
||||
if (device_count<MAX_DEVICES) {
|
||||
devices[device_count]=adddev;
|
||||
device_count++;
|
||||
/* Add the device in the main file Table */
|
||||
Bit8u handle=DOS_FILES;Bit8u i;
|
||||
for (i=0;i<DOS_FILES;i++) {
|
||||
if (!Files[i]) {
|
||||
handle=i;
|
||||
Files[i]=adddev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle==DOS_FILES) E_Exit("DOS:Not enough file handles for device");
|
||||
adddev->fhandle=handle;
|
||||
} else {
|
||||
E_Exit("DOS:Too many devices added");
|
||||
}
|
||||
}
|
||||
|
||||
void DOS_SetupDevices(void) {
|
||||
device_count=0;
|
||||
DOS_Device * newdev;
|
||||
newdev=new device_CON();
|
||||
DOS_AddDevice(newdev);
|
||||
}
|
||||
|
439
src/dos/dos_execute.cpp
Normal file
439
src/dos/dos_execute.cpp
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "dosbox.h"
|
||||
#include "mem.h"
|
||||
#include "dos_inc.h"
|
||||
#include "cpu.h"
|
||||
#pragma pack(1)
|
||||
|
||||
|
||||
struct EXE_Header {
|
||||
|
||||
Bit16u signature; /* EXE Signature MZ or ZM */
|
||||
Bit16u extrabytes; /* Bytes on the last page */
|
||||
Bit16u pages; /* Pages in file */
|
||||
Bit16u relocations; /* Relocations in file */
|
||||
Bit16u headersize; /* Paragraphs in header */
|
||||
Bit16u minmemory; /* Minimum amount of memory */
|
||||
Bit16u maxmemory; /* Maximum amount of memory */
|
||||
Bit16u initSS;
|
||||
Bit16u initSP;
|
||||
Bit16u checksum;
|
||||
Bit16u initIP;
|
||||
Bit16u initCS;
|
||||
Bit16u reloctable;
|
||||
Bit16u overlay;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
#define MAGIC1 0x5a4d
|
||||
#define MAGIC2 0x4d5a
|
||||
#define MAXENV 32768u
|
||||
#define ENV_KEEPFREE 83 /* keep unallocated by environment variables */
|
||||
/* The '65' added to nEnvSize does not cover the additional stuff:
|
||||
+ 2 bytes: number of strings
|
||||
+ 80 bytes: maximum absolute filename
|
||||
+ 1 byte: '\0'
|
||||
-- 1999/04/21 ska */
|
||||
#define LOADNGO 0
|
||||
#define LOAD 1
|
||||
#define OVERLAY 3
|
||||
|
||||
|
||||
bool DOS_Terminate(bool tsr) {
|
||||
PSP * psp=(PSP *)real_off(dos.psp,0);
|
||||
if (!tsr) {
|
||||
/* Free Files owned by process */
|
||||
for (Bit16u i=0;i<psp->max_files;i++) {
|
||||
DOS_CloseFile(i);
|
||||
}
|
||||
DOS_FreeProcessMemory(dos.psp);
|
||||
};
|
||||
dos.psp=psp->psp_parent;
|
||||
PSP * oldpsp=(PSP *)real_off(dos.psp,0);
|
||||
/* Restore the DTA */
|
||||
dos.dta=psp->dta;
|
||||
/* Restore the old CS:IP from int 22h */
|
||||
RealPt old22;
|
||||
old22=RealGetVec(0x22);
|
||||
SetSegment_16(cs,RealSeg(old22));
|
||||
reg_ip=RealOff(old22);
|
||||
/* Restore the SS:SP to the previous one */
|
||||
SetSegment_16(ss,RealSeg(oldpsp->stack));
|
||||
reg_sp=RealOff(oldpsp->stack);
|
||||
/* Restore interrupt 22,23,24 */
|
||||
RealSetVec(0x22,psp->int_22);
|
||||
RealSetVec(0x23,psp->int_23);
|
||||
RealSetVec(0x24,psp->int_24);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool MakeEnv(char * name,Bit16u * segment) {
|
||||
|
||||
/* If segment to copy environment is 0 copy the caller's environment */
|
||||
PSP * psp=(PSP *)real_off(dos.psp,0);
|
||||
Bit8u * envread,*envwrite;
|
||||
Bit16u envsize=1;
|
||||
bool parentenv=true;
|
||||
|
||||
if (*segment==0) {
|
||||
if (!psp->environment) parentenv=false; //environment seg=0
|
||||
envread=real_off(psp->environment,0);
|
||||
} else {
|
||||
if (!*segment) parentenv=false; //environment seg=0
|
||||
envread=real_off(*segment,0);
|
||||
}
|
||||
|
||||
//TODO Make a good DOS first psp
|
||||
if (parentenv) {
|
||||
for (envsize=0; ;envsize++) {
|
||||
if (envsize>=MAXENV - ENV_KEEPFREE) {
|
||||
DOS_SetError(DOSERR_ENVIRONMENT_INVALID);
|
||||
return false;
|
||||
}
|
||||
if (readw(envread+envsize)==0) break;
|
||||
}
|
||||
envsize += 2; /* account for trailing \0\0 */
|
||||
}
|
||||
Bit16u size=long2para(envsize+ENV_KEEPFREE);
|
||||
if (!DOS_AllocateMemory(segment,&size)) return false;
|
||||
envwrite=real_off(*segment,0);
|
||||
if (parentenv) {
|
||||
bmemcpy(envwrite,envread,envsize);
|
||||
envwrite+=envsize;
|
||||
} else {
|
||||
*envwrite++=0;
|
||||
}
|
||||
*((Bit16u *) envwrite)=1;
|
||||
envwrite+=2;
|
||||
//TODO put the filename here
|
||||
return DOS_Canonicalize(name,envwrite);
|
||||
};
|
||||
|
||||
bool DOS_NewPSP(Bit16u pspseg) {
|
||||
PSP * newpsp=(PSP *)real_off(pspseg,0);
|
||||
PSP * prevpsp=(PSP *)real_off(dos.psp,0);
|
||||
|
||||
memset((void *)newpsp,0,sizeof(PSP));
|
||||
newpsp->exit[0]=0xcd;newpsp->exit[1]=0x20;
|
||||
newpsp->service[0]=0xcd;newpsp->service[0]=0x21;newpsp->service[0]=0xcb;
|
||||
|
||||
newpsp->mem_size=prevpsp->mem_size;
|
||||
newpsp->environment=0;
|
||||
|
||||
newpsp->int_22=RealGetVec(0x22);
|
||||
newpsp->int_23=RealGetVec(0x23);
|
||||
newpsp->int_24=RealGetVec(0x24);
|
||||
|
||||
newpsp->psp_parent=dos.psp;
|
||||
newpsp->prev_psp=0xFFFFFFFF;
|
||||
|
||||
Bit32u i;
|
||||
Bit8u * prevfile=Real2Host(prevpsp->file_table);
|
||||
for (i=0;i<20;i++) newpsp->files[i]=prevfile[i];
|
||||
|
||||
newpsp->max_files=20;
|
||||
newpsp->file_table=RealMake(pspseg,offsetof(PSP,files));
|
||||
/* Save the old DTA in this psp */
|
||||
newpsp->dta=dos.dta;
|
||||
/* Setup the DTA */
|
||||
dos.dta=RealMake(pspseg,0x80);
|
||||
return true;
|
||||
};
|
||||
|
||||
static void SetupPSP(Bit16u pspseg,Bit16u memsize,Bit16u envseg) {
|
||||
|
||||
PSP * psp=(PSP *)real_off(pspseg,0);
|
||||
/* Fix the PSP index of this MCB */
|
||||
MCB * pspmcb=(MCB *)real_off(pspseg-1,0);
|
||||
pspmcb->psp_segment=pspseg;
|
||||
MCB * envmcb=(MCB *)real_off(envseg-1,0);
|
||||
envmcb->psp_segment=pspseg;
|
||||
|
||||
memset((void *)psp,0,sizeof(PSP));
|
||||
Bit32u i;
|
||||
|
||||
psp->exit[0]=0xcd;psp->exit[1]=0x20;
|
||||
psp->mem_size=memsize+pspseg;
|
||||
psp->environment=envseg;
|
||||
|
||||
psp->int_22=RealGetVec(0x22);
|
||||
psp->int_23=RealGetVec(0x23);
|
||||
psp->int_24=RealGetVec(0x24);
|
||||
|
||||
psp->service[0]=0xcd;psp->service[0]=0x21;psp->service[0]=0xcb;
|
||||
|
||||
psp->psp_parent=dos.psp;
|
||||
psp->prev_psp=RealMake(dos.psp,0);
|
||||
|
||||
for (i=0;i<20;i++) psp->files[i]=0xff;
|
||||
psp->files[STDIN]=DOS_FindDevice("CON");
|
||||
psp->files[STDOUT]=DOS_FindDevice("CON");
|
||||
psp->files[STDERR]=DOS_FindDevice("CON");
|
||||
psp->files[STDAUX]=DOS_FindDevice("CON");
|
||||
psp->files[STDNUL]=DOS_FindDevice("CON");
|
||||
psp->files[STDPRN]=DOS_FindDevice("CON");
|
||||
|
||||
psp->max_files=20;
|
||||
psp->file_table=RealMake(pspseg,offsetof(PSP,files));
|
||||
/* Save old DTA in psp */
|
||||
psp->dta=dos.dta;
|
||||
|
||||
/* Setup the DTA */
|
||||
dos.dta=RealMake(pspseg,0x80);
|
||||
}
|
||||
|
||||
static void SetupCMDLine(Bit16u pspseg,ParamBlock * block) {
|
||||
PSP * psp=(PSP *)real_off(pspseg,0);
|
||||
|
||||
if (block->exec.cmdtail) {
|
||||
memcpy((void *)&psp->cmdtail,(void *)Real2Host(block->exec.cmdtail),128);
|
||||
} else {
|
||||
char temp[]="";
|
||||
psp->cmdtail.count=strlen(temp);
|
||||
strcpy((char *)&psp->cmdtail.buffer,temp);
|
||||
psp->cmdtail.buffer[0]=0x0d;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static bool COM_Load(char * name,ParamBlock * block,Bit8u flag) {
|
||||
Bit16u fhandle;
|
||||
Bit16u size;Bit16u readsize;
|
||||
Bit16u envseg,comseg;
|
||||
Bit32u pos;
|
||||
|
||||
PSP * callpsp=(PSP *)real_off(dos.psp,0);
|
||||
|
||||
if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) return false;
|
||||
if (flag!=OVERLAY) {
|
||||
/* Allocate a new Environment */
|
||||
envseg=block->exec.envseg;
|
||||
if (!MakeEnv(name,&envseg)) return false;
|
||||
/* Allocate max memory for COM file and PSP */
|
||||
size=0xffff;
|
||||
DOS_AllocateMemory(&comseg,&size);
|
||||
//TODO Errors check for minimun of 64kb in pages
|
||||
if (Bit32u(size <<4)<0x1000) {
|
||||
DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
|
||||
DOS_FreeMemory(envseg);
|
||||
return false;
|
||||
}
|
||||
DOS_AllocateMemory(&comseg,&size);
|
||||
} else {
|
||||
comseg=block->overlay.loadseg;
|
||||
}
|
||||
/* Memory allocated now load the program */
|
||||
/* Now copy the File into allocated memory */
|
||||
pos=0;
|
||||
DOS_SeekFile(fhandle,&pos,0);
|
||||
readsize=0xffff-256;
|
||||
if (flag==OVERLAY) {
|
||||
DOS_ReadFile(fhandle,real_host(comseg,0),&readsize);
|
||||
} else {
|
||||
DOS_ReadFile(fhandle,real_host(comseg,256),&readsize);
|
||||
}
|
||||
DOS_CloseFile(fhandle);
|
||||
if (flag==OVERLAY) /* Everything what should be done for Overlays */
|
||||
return true;
|
||||
SetupPSP(comseg,size,envseg);
|
||||
SetupCMDLine(comseg,block);
|
||||
/* Setup termination Address */
|
||||
RealSetVec(0x22,RealMake(Segs[cs].value,reg_ip));
|
||||
/* Everything setup somewhat setup CS:IP and SS:SP */
|
||||
/* First save the SS:SP of program that called execute */
|
||||
callpsp->stack=RealMake(Segs[ss].value,reg_sp);
|
||||
/* Clear out first Stack entry to point to int 20h at psp:0 */
|
||||
real_writew(comseg,0xfffe,0);
|
||||
dos.psp=comseg;
|
||||
switch (flag) {
|
||||
case LOADNGO:
|
||||
SetSegment_16(cs,comseg);
|
||||
SetSegment_16(ss,comseg);
|
||||
SetSegment_16(ds,comseg);
|
||||
SetSegment_16(es,comseg);
|
||||
flags.intf=true;
|
||||
reg_ip=0x100;
|
||||
reg_sp=0xFFFE;
|
||||
reg_ax=0;
|
||||
reg_bx=reg_cx=reg_dx=reg_si=reg_di=reg_bp=0;
|
||||
return true;
|
||||
case LOAD:
|
||||
block->exec.initsssp=RealMake(comseg,0xfffe);
|
||||
block->exec.initcsip=RealMake(comseg,0x100);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static bool EXE_Load(char * name,ParamBlock * block,Bit8u flag) {
|
||||
|
||||
EXE_Header header;
|
||||
Bit16u fhandle;Bit32u i;
|
||||
Bit16u size,minsize,maxsize,freesize;Bit16u readsize;
|
||||
Bit16u envseg,pspseg,exeseg;
|
||||
Bit32u imagesize,headersize;
|
||||
|
||||
PSP * callpsp=(PSP *)real_off(dos.psp,0);
|
||||
|
||||
if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) return false;
|
||||
if (flag!=OVERLAY) {
|
||||
/* Allocate a new Environment */
|
||||
envseg=block->exec.envseg;
|
||||
if (!MakeEnv(name,&envseg)) return false;
|
||||
};
|
||||
|
||||
/* First Read the EXE Header */
|
||||
readsize=sizeof(EXE_Header);
|
||||
DOS_ReadFile(fhandle,(Bit8u*)&header,&readsize);
|
||||
/* Calculate the size of the image to load */
|
||||
headersize=header.headersize*16;
|
||||
imagesize=header.pages*512-headersize;
|
||||
if (flag!=OVERLAY) {
|
||||
minsize=long2para(imagesize+(header.minmemory<<4)+256);
|
||||
if (header.maxmemory!=0) maxsize=long2para(imagesize+(header.maxmemory<<4)+256);
|
||||
else maxsize=0xffff;
|
||||
freesize=0xffff;
|
||||
/* Check for enough free memory */
|
||||
DOS_AllocateMemory(&exeseg,&freesize);
|
||||
if (minsize>freesize) {
|
||||
DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
|
||||
DOS_FreeMemory(envseg);
|
||||
return false;
|
||||
}
|
||||
if (maxsize>freesize) {
|
||||
size=freesize;
|
||||
} else size=maxsize;
|
||||
if ((header.minmemory|header.maxmemory)==0) {
|
||||
size=freesize;
|
||||
E_Exit("Special case exe header max and min=0");
|
||||
}
|
||||
if (!DOS_AllocateMemory(&pspseg,&size)) {
|
||||
DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
|
||||
DOS_FreeMemory(envseg);
|
||||
return false;
|
||||
}
|
||||
SetupPSP(pspseg,size,envseg);
|
||||
SetupCMDLine(pspseg,block);
|
||||
exeseg=pspseg+16;
|
||||
} else {
|
||||
/* For OVERLAY */
|
||||
exeseg=block->overlay.loadseg;
|
||||
}
|
||||
/* Load the image in 32k blocks */
|
||||
DOS_SeekFile(fhandle,&headersize,0);
|
||||
Bit8u * imageoff=real_off(exeseg,0);
|
||||
//TODO File size checking and remove size
|
||||
// Remove psp size
|
||||
// imagesize=256;
|
||||
// Maybe remove final page and add last bytes on page
|
||||
if (header.extrabytes) {
|
||||
imagesize-=512;
|
||||
imagesize+=header.extrabytes;
|
||||
};
|
||||
while (imagesize>0x7FFF) {
|
||||
readsize=0x8000;
|
||||
DOS_ReadFile(fhandle,imageoff,&readsize);
|
||||
if (readsize!=0x8000) {
|
||||
E_Exit("Illegal header");
|
||||
}
|
||||
imageoff+=0x8000;
|
||||
imagesize-=0x8000;
|
||||
}
|
||||
if (imagesize>0) {
|
||||
readsize=(Bit16u) imagesize;
|
||||
DOS_ReadFile(fhandle,imageoff,&readsize);
|
||||
}
|
||||
headersize=header.reloctable;
|
||||
DOS_SeekFile(fhandle,&headersize,0);
|
||||
RealPt reloc;
|
||||
for (i=0;i<header.relocations;i++) {
|
||||
readsize=4;
|
||||
DOS_ReadFile(fhandle,(Bit8u *)&reloc,&readsize);
|
||||
PhysPt address=Real2Phys(RealMake(RealSeg(reloc)+exeseg,RealOff(reloc)));
|
||||
Bit16u change=mem_readw(address);
|
||||
if (flag==OVERLAY) {
|
||||
change+=block->overlay.relocation;
|
||||
} else {
|
||||
change+=exeseg;
|
||||
};
|
||||
mem_writew(address,change);
|
||||
|
||||
};
|
||||
DOS_CloseFile(fhandle);
|
||||
if (flag==OVERLAY) return true;
|
||||
|
||||
/* Setup termination Address */
|
||||
RealSetVec(0x22,RealMake(Segs[cs].value,reg_ip));
|
||||
/* Start up the actual EXE if we need to */
|
||||
//TODO check for load and return
|
||||
callpsp->stack=RealMake(Segs[ss].value,reg_sp);
|
||||
dos.psp=pspseg;
|
||||
SetSegment_16(cs,exeseg+header.initCS);
|
||||
SetSegment_16(ss,exeseg+header.initSS);
|
||||
SetSegment_16(ds,pspseg);
|
||||
SetSegment_16(es,pspseg);
|
||||
reg_ip=header.initIP;
|
||||
reg_sp=header.initSP;
|
||||
reg_ax=0;
|
||||
reg_bx=reg_cx=reg_dx=reg_si=reg_di=reg_bp=0;
|
||||
flags.intf=true;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
bool DOS_Execute(char * name,ParamBlock * block,Bit8u flags) {
|
||||
|
||||
EXE_Header head;
|
||||
Bit16u fhandle;
|
||||
Bit16u size;
|
||||
bool iscom=false;
|
||||
if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) return false;
|
||||
|
||||
size=sizeof(EXE_Header);
|
||||
if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&size)) {
|
||||
DOS_CloseFile(fhandle);
|
||||
return false;
|
||||
}
|
||||
if (!DOS_CloseFile(fhandle)) return false;
|
||||
if (size<sizeof(EXE_Header)) iscom=true;
|
||||
if ((head.signature!=MAGIC1) && (head.signature!=MAGIC2)) iscom=true;
|
||||
|
||||
if (iscom) {
|
||||
return COM_Load(name,block,flags);
|
||||
|
||||
} else {
|
||||
return EXE_Load(name,block,flags);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
614
src/dos/dos_files.cpp
Normal file
614
src/dos/dos_files.cpp
Normal file
|
@ -0,0 +1,614 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "dosbox.h"
|
||||
#include "mem.h"
|
||||
#include "cpu.h"
|
||||
#include "dos_inc.h"
|
||||
#include "drives.h"
|
||||
|
||||
#define DOS_FILESTART 4
|
||||
|
||||
|
||||
DOS_File * Files[DOS_FILES];
|
||||
DOS_Drive * Drives[DOS_DRIVES];
|
||||
static Bit8u CurrentDrive=2; //Init on C:
|
||||
|
||||
|
||||
Bit8u DOS_GetDefaultDrive(void) {
|
||||
return CurrentDrive;
|
||||
}
|
||||
|
||||
void DOS_SetDefaultDrive(Bit8u drive) {
|
||||
CurrentDrive=drive;
|
||||
}
|
||||
|
||||
bool DOS_MakeName(char * name,char * fullname,Bit8u * drive) {
|
||||
//TODO Hope this is ok :)
|
||||
char upname[DOS_PATHLENGTH];
|
||||
Bit32u r=0;Bit32u w=0;Bit32u namestart=0;
|
||||
bool hasdrive=false;
|
||||
*drive=CurrentDrive;
|
||||
char tempdir[DOS_NAMELENGTH];
|
||||
//TODO Maybe check for illegal characters
|
||||
while (name[r]!=0 && (r<DOS_PATHLENGTH)) {
|
||||
Bit8u c=name[r++];
|
||||
if ((c>='a') && (c<='z')) {upname[w++]=c-32;continue;}
|
||||
if ((c>='A') && (c<='Z')) {upname[w++]=c;continue;}
|
||||
if ((c>='0') && (c<='9')) {upname[w++]=c;continue;}
|
||||
switch (c) {
|
||||
case ':':
|
||||
if (hasdrive) { DOS_SetError(DOSERR_PATH_NOT_FOUND);return false; }
|
||||
else hasdrive=true;
|
||||
if ((upname[0]>='A') && (upname[0]<='Z')) {
|
||||
*drive=upname[0]-'A';
|
||||
w=0;
|
||||
} else {
|
||||
DOS_SetError(DOSERR_PATH_NOT_FOUND);return false;
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
upname[w++]='\\';
|
||||
break;
|
||||
case ' ':
|
||||
break;
|
||||
case '\\': case '$': case '#': case '@': case '(': case ')':
|
||||
case '!': case '%': case '{': case '}': case '`': case '~':
|
||||
case '_': case '-': case '.': case '*': case '?': case '&':
|
||||
upname[w++]=c;
|
||||
break;
|
||||
default:
|
||||
DOS_SetError(DOSERR_PATH_NOT_FOUND);return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
upname[w]=0;
|
||||
/* This should get us an upcase filename and no incorrect chars */
|
||||
/* Now parse the new file name to make the final filename */
|
||||
if ((*drive>=26)) {
|
||||
DOS_SetError(DOSERR_INVALID_DRIVE);return false;
|
||||
};
|
||||
if (!Drives[*drive]) {
|
||||
DOS_SetError(DOSERR_INVALID_DRIVE);return false;
|
||||
};
|
||||
if (upname[0]!='\\') strcpy(fullname,Drives[*drive]->curdir);
|
||||
else fullname[0]=0;
|
||||
Bit32u lastdir=0;Bit32u t=0;
|
||||
while (fullname[t]!=0) {
|
||||
if ((fullname[t]=='\\') && (fullname[t+1]!=0))lastdir=t;
|
||||
t++;
|
||||
};
|
||||
r=0;w=0;
|
||||
tempdir[0]=0;
|
||||
bool stop=false;
|
||||
while (!stop) {
|
||||
if (upname[r]==0) stop=true;
|
||||
if ((upname[r]=='\\') || (upname[r]==0)){
|
||||
tempdir[w]=0;
|
||||
if (tempdir[0]==0) { w=0;r++;continue;}
|
||||
if (strcmp(tempdir,".")==0) {
|
||||
tempdir[0]=0;
|
||||
w=0;r++;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(tempdir,"..")==0) {
|
||||
fullname[lastdir]=0;
|
||||
Bit32u t=0;lastdir=0;
|
||||
while (fullname[t]!=0) {
|
||||
if ((fullname[t]=='\\') && (fullname[t+1]!=0))lastdir=t;
|
||||
t++;
|
||||
}
|
||||
tempdir[0]=0;
|
||||
w=0;r++;
|
||||
continue;
|
||||
}
|
||||
lastdir=strlen(fullname);
|
||||
//TODO Maybe another check for correct type because of .... stuff
|
||||
if (lastdir!=0) strcat(fullname,"\\");
|
||||
strcat(fullname,tempdir);
|
||||
|
||||
tempdir[0]=0;
|
||||
w=0;r++;
|
||||
continue;
|
||||
}
|
||||
tempdir[w++]=upname[r++];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DOS_GetCurrentDir(Bit8u drive,Bit8u * buffer) {
|
||||
if (drive==0) drive=DOS_GetDefaultDrive();
|
||||
else drive--;
|
||||
if ((drive>DOS_DRIVES) || (!Drives[drive])) {
|
||||
DOS_SetError(DOSERR_INVALID_DRIVE);
|
||||
return false;
|
||||
}
|
||||
strcpy((char *) buffer,Drives[drive]->curdir);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DOS_ChangeDir(char * dir) {
|
||||
Bit8u drive;char fulldir[DOS_PATHLENGTH];
|
||||
if (!DOS_MakeName(dir,fulldir,&drive)) return false;
|
||||
if (Drives[drive]->TestDir(fulldir)) {
|
||||
strcpy(Drives[drive]->curdir,fulldir);
|
||||
return true;
|
||||
} else {
|
||||
DOS_SetError(DOSERR_PATH_NOT_FOUND);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DOS_MakeDir(char * dir) {
|
||||
Bit8u drive;char fulldir[DOS_PATHLENGTH];
|
||||
if (!DOS_MakeName(dir,fulldir,&drive)) return false;
|
||||
return Drives[drive]->MakeDir(fulldir);
|
||||
}
|
||||
|
||||
bool DOS_RemoveDir(char * dir) {
|
||||
Bit8u drive;char fulldir[DOS_PATHLENGTH];
|
||||
if (!DOS_MakeName(dir,fulldir,&drive)) return false;
|
||||
return Drives[drive]->RemoveDir(fulldir);
|
||||
}
|
||||
|
||||
bool DOS_Rename(char * oldname,char * newname) {
|
||||
Bit8u driveold;char fullold[DOS_PATHLENGTH];
|
||||
Bit8u drivenew;char fullnew[DOS_PATHLENGTH];
|
||||
if (!DOS_MakeName(oldname,fullold,&driveold)) return false;
|
||||
if (!DOS_MakeName(newname,fullnew,&drivenew)) return false;
|
||||
//TODO Test for different drives maybe
|
||||
if (Drives[drivenew]->Rename(fullold,fullnew)) return true;
|
||||
DOS_SetError(DOSERR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
};
|
||||
|
||||
bool DOS_FindFirst(char * search,Bit16u attr) {
|
||||
Bit8u drive;char fullsearch[DOS_PATHLENGTH];
|
||||
if (!DOS_MakeName(search,fullsearch,&drive)) return false;
|
||||
DTA_FindBlock * dtablock=(DTA_FindBlock *)Real2Host(dos.dta);
|
||||
dtablock->sattr=attr | DOS_ATTR_ARCHIVE;
|
||||
dtablock->sdrive=drive;
|
||||
return Drives[drive]->FindFirst(fullsearch,dtablock);
|
||||
};
|
||||
|
||||
bool DOS_FindNext(void) {
|
||||
DTA_FindBlock * dtablock=(DTA_FindBlock *)Real2Host(dos.dta);
|
||||
return Drives[dtablock->sdrive]->FindNext(dtablock);
|
||||
};
|
||||
|
||||
|
||||
bool DOS_ReadFile(Bit16u entry,Bit8u * data,Bit16u * amount) {
|
||||
Bit32u handle=RealHandle(entry);
|
||||
if (handle>=DOS_FILES) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
if (!Files[handle]) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
//TODO maybe another code :)
|
||||
/*
|
||||
if (!(Files[handle]->flags & OPEN_READ)) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
Bit16u toread=*amount;
|
||||
bool ret=Files[handle]->Read(data,&toread);
|
||||
*amount=toread;
|
||||
return ret;
|
||||
};
|
||||
|
||||
bool DOS_WriteFile(Bit16u entry,Bit8u * data,Bit16u * amount) {
|
||||
Bit32u handle=RealHandle(entry);
|
||||
if (handle>=DOS_FILES) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
if (!Files[handle]) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
//TODO maybe another code :)
|
||||
/*
|
||||
if (!(Files[handle]->flags & OPEN_WRITE)) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
Bit16u towrite=*amount;
|
||||
bool ret=Files[handle]->Write(data,&towrite);
|
||||
*amount=towrite;
|
||||
return ret;
|
||||
};
|
||||
|
||||
bool DOS_SeekFile(Bit16u entry,Bit32u * pos,Bit32u type) {
|
||||
Bit32u handle=RealHandle(entry);
|
||||
if (handle>=DOS_FILES) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
if (!Files[handle]) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
return Files[handle]->Seek(pos,type);
|
||||
};
|
||||
|
||||
bool DOS_CloseFile(Bit16u entry) {
|
||||
Bit32u handle=RealHandle(entry);
|
||||
if (handle>=DOS_FILES) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
if (!Files[handle]) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
//TODO Figure this out with devices :)
|
||||
|
||||
PSP * psp=(PSP *)real_off(dos.psp,0);
|
||||
Bit8u * table=Real2Host(psp->file_table);
|
||||
table[entry]=0xFF;
|
||||
/* Devices won't allow themselves to be closed or killed */
|
||||
if (Files[handle]->Close()) {
|
||||
delete Files[handle];
|
||||
Files[handle]=0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DOS_CreateFile(char * name,Bit16u attributes,Bit16u * entry) {
|
||||
char fullname[DOS_PATHLENGTH];Bit8u drive;
|
||||
PSP * psp=(PSP *)real_off(dos.psp,0);
|
||||
if (!DOS_MakeName(name,fullname,&drive)) return false;
|
||||
/* Check for a free file handle */
|
||||
Bit8u handle=DOS_FILES;Bit8u i;
|
||||
for (i=0;i<DOS_FILES;i++) {
|
||||
if (!Files[i]) {
|
||||
handle=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle==DOS_FILES) {
|
||||
DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
|
||||
return false;
|
||||
}
|
||||
/* We have a position in the main table now find one in the psp table */
|
||||
Bit8u * table=Real2Host(psp->file_table);
|
||||
*entry=0xff;
|
||||
for (i=0;i<psp->max_files;i++) {
|
||||
if (table[i]==0xFF) {
|
||||
*entry=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*entry==0xff) {
|
||||
DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
|
||||
return false;
|
||||
}
|
||||
bool foundit=Drives[drive]->FileCreate(&Files[handle],fullname,attributes);
|
||||
if (foundit) {
|
||||
table[*entry]=handle;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DOS_OpenFile(char * name,Bit8u flags,Bit16u * entry) {
|
||||
/* First check for devices */
|
||||
PSP * psp=(PSP *)real_off(dos.psp,0);
|
||||
Bit8u handle=DOS_FindDevice((char *)name);
|
||||
bool device=false;char fullname[DOS_PATHLENGTH];Bit8u drive;Bit8u i;
|
||||
if (handle!=255) {
|
||||
device=true;
|
||||
} else {
|
||||
/* First check if the name is correct */
|
||||
if (!DOS_MakeName(name,fullname,&drive)) return false;
|
||||
/* Check for a free file handle */
|
||||
for (i=0;i<DOS_FILES;i++) {
|
||||
if (!Files[i]) {
|
||||
handle=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle==255) {
|
||||
DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* We have a position in the main table now find one in the psp table */
|
||||
Bit8u * table=Real2Host(psp->file_table);
|
||||
*entry=0xff;
|
||||
for (i=0;i<psp->max_files;i++) {
|
||||
if (table[i]==0xFF) {
|
||||
*entry=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*entry==0xff) {
|
||||
DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
|
||||
return false;
|
||||
}
|
||||
bool exists=false;
|
||||
if (!device) exists=Drives[drive]->FileOpen(&Files[handle],fullname,flags);
|
||||
if (exists || device ) {
|
||||
table[*entry]=handle;
|
||||
return true;
|
||||
} else {
|
||||
DOS_SetError(DOSERR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DOS_UnlinkFile(char * name) {
|
||||
char fullname[DOS_PATHLENGTH];Bit8u drive;
|
||||
if (!DOS_MakeName(name,fullname,&drive)) return false;
|
||||
return Drives[drive]->FileUnlink(fullname);
|
||||
}
|
||||
|
||||
bool DOS_GetFileAttr(char * name,Bit16u * attr) {
|
||||
char fullname[DOS_PATHLENGTH];Bit8u drive;
|
||||
if (!DOS_MakeName(name,fullname,&drive)) return false;
|
||||
if (Drives[drive]->GetFileAttr(fullname,attr)) {
|
||||
return true;
|
||||
} else {
|
||||
DOS_SetError(DOSERR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DOS_Canonicalize(char * name,Bit8u * big) {
|
||||
//TODO Add Better support for devices and shit but will it be needed i doubt it :)
|
||||
Bit8u drive;
|
||||
char fullname[DOS_PATHLENGTH];
|
||||
if (!DOS_MakeName(name,fullname,&drive)) return false;
|
||||
big[0]=drive+'A';
|
||||
big[1]=':';
|
||||
big[2]='\\';
|
||||
strcpy((char *)&big[3],fullname);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit16u * sectors,Bit16u * clusters,Bit16u * free) {
|
||||
if (drive==0) drive=DOS_GetDefaultDrive();
|
||||
else drive--;
|
||||
if ((drive>DOS_DRIVES) || (!Drives[drive])) {
|
||||
DOS_SetError(DOSERR_INVALID_DRIVE);
|
||||
return false;
|
||||
}
|
||||
return Drives[drive]->FreeSpace(bytes,sectors,clusters,free);
|
||||
}
|
||||
|
||||
bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry) {
|
||||
Bit8u handle=RealHandle(entry);
|
||||
if (handle>=DOS_FILES) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
if (!Files[handle]) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
PSP * psp=(PSP *)real_off(dos.psp,0);
|
||||
Bit8u * table=Real2Host(psp->file_table);
|
||||
*newentry=0xff;
|
||||
for (Bit16u i=0;i<psp->max_files;i++) {
|
||||
if (table[i]==0xFF) {
|
||||
*newentry=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*newentry==0xff) {
|
||||
DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES);
|
||||
return false;
|
||||
}
|
||||
table[*newentry]=handle;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
bool DOS_CreateTempFile(char * name,Bit16u * entry) {
|
||||
|
||||
/* First add random crap to the end of the name and try to open */
|
||||
/* Todo maybe check for euhm existence of the path name */
|
||||
char * tempname;
|
||||
tempname=name+strlen(name);
|
||||
do {
|
||||
Bit32u i;
|
||||
for (i=0;i<8;i++) {
|
||||
tempname[i]=(rand()%26)+'A';
|
||||
}
|
||||
tempname[8]='.';
|
||||
for (i=9;i<12;i++) {
|
||||
tempname[i]=(rand()%26)+'A';
|
||||
}
|
||||
tempname[13]=0;
|
||||
} while (!DOS_CreateFile(name,0,entry));
|
||||
return true;
|
||||
}
|
||||
#if 0
|
||||
void FCB_MakeName (DOS_FCB* fcb, char* outname, Bit8u* outdrive){
|
||||
char naam[15];
|
||||
Bit8u drive=fcb->Get_drive();
|
||||
if(drive!=0){
|
||||
naam[0]=(drive-1)+'A';
|
||||
naam[1]=':';
|
||||
naam[2]='\0';}
|
||||
else{
|
||||
naam[0]='\0';
|
||||
};
|
||||
char temp[9];
|
||||
fcb->Get_filename(temp);
|
||||
temp[9]='.';
|
||||
strncat(naam,temp,9);
|
||||
char ext[3];
|
||||
fcb->Get_ext(ext);
|
||||
if(drive!=0) {
|
||||
strncat(&naam[11],ext,3);
|
||||
naam[14]='\0';
|
||||
}else{
|
||||
strncat(&naam[9],ext,3);
|
||||
naam[12]='\0';
|
||||
};
|
||||
DOS_MakeName(naam,outname, outdrive);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool DOS_FCBOpen(Bit16u seg,Bit16u offset) {
|
||||
DOS_FCB fcb(seg,offset);
|
||||
Bit8u drive;
|
||||
char fullname[DOS_PATHLENGTH];
|
||||
FCB_MakeName (&fcb, fullname, &drive);
|
||||
if(DOS_FileExists(fullname)==false) return false;
|
||||
|
||||
|
||||
struct stat stat_block;
|
||||
stat(fullname, &stat_block);
|
||||
fcb.Set_filesize((Bit32u)stat_block.st_size);
|
||||
Bit16u constant =0;
|
||||
fcb.Set_current_block(constant);
|
||||
constant=0x80;
|
||||
fcb.Set_record_size(constant);
|
||||
struct tm *time;
|
||||
time=localtime(&stat_block.st_mtime);
|
||||
|
||||
constant=(time->tm_hour<<11)+(time->tm_min<<5)+(time->tm_sec/2); /* standard way. */
|
||||
fcb->Set_time(constant);
|
||||
constant=((time->tm_year-80)<<9)+((time->tm_mon+1)<<5)+(time->tm_mday);
|
||||
fcb->Set_date(constant);
|
||||
fcb->Set_drive(drive +1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FCB_Close(void)
|
||||
{ return true;}
|
||||
|
||||
bool FCB_FindFirst(Bit16u seg,Bit16u offset)
|
||||
{FCB* fcb = new FCB(seg,offset);
|
||||
Bit8u drive;
|
||||
Bitu i;
|
||||
char fullname[DOS_PATHLENGTH];
|
||||
FCB_MakeName (fcb, fullname, &drive);
|
||||
DTA_FindBlock * dtablock=(DTA_FindBlock *)Real2Host(dos.dta);
|
||||
if(Drives[drive]->FindFirst(fullname,dtablock)==false) return false;
|
||||
|
||||
char naam[8];
|
||||
char ext[3];
|
||||
char * point=strrchr(dtablock->name,'.');
|
||||
if(point==NULL|| *(point+1)=='\0') {
|
||||
ext[0]=' ';
|
||||
ext[1]=' ';
|
||||
ext[2]=' ';
|
||||
}else
|
||||
{strcpy(ext,point+1);
|
||||
Bitu i;
|
||||
i=strlen(point+1);
|
||||
while(i!=3) ext[i-1]=' ';
|
||||
}
|
||||
|
||||
if(point!=NULL) *point='\0';
|
||||
|
||||
strcpy (naam,dtablock->name);
|
||||
i=strlen(dtablock->name);
|
||||
while(i!=8) naam[i-1]=' ';
|
||||
FCB* fcbout= new FCB((PhysPt)Real2Host(dos.dta));
|
||||
fcbout->Set_drive(drive +1);
|
||||
fcbout->Set_filename(naam);
|
||||
fcbout->Set_ext(ext);
|
||||
return true;
|
||||
}
|
||||
bool FCB_FindNext(Bit16u seg,Bit16u offset)
|
||||
{FCB* fcb = new FCB(seg,offset);
|
||||
Bit8u drive;
|
||||
Bitu i;
|
||||
char fullname[DOS_PATHLENGTH];
|
||||
FCB_MakeName (fcb, fullname, &drive);
|
||||
DTA_FindBlock * dtablock=(DTA_FindBlock *)Real2Host(dos.dta);
|
||||
if(Drives[dtablock->sdrive]->FindNext(dtablock)==false) return false;
|
||||
char naam[8];
|
||||
char ext[3];
|
||||
char * point=strrchr(dtablock->name,'.');
|
||||
if(point==NULL|| *(point+1)=='\0') {
|
||||
ext[0]=' ';
|
||||
ext[1]=' ';
|
||||
ext[2]=' ';
|
||||
}else
|
||||
{strcpy(ext,point+1);
|
||||
Bitu i;
|
||||
i=strlen(point+1);
|
||||
while(i!=3) ext[i-1]=' ';
|
||||
}
|
||||
|
||||
if(point!=NULL) *point='\0';
|
||||
|
||||
strcpy (naam,dtablock->name);
|
||||
i=strlen(dtablock->name);
|
||||
while(i!=8) naam[i-1]=' ';
|
||||
FCB* fcbout= new FCB((PhysPt)Real2Host(dos.dta));
|
||||
fcbout->Set_drive(drive +1);
|
||||
fcbout->Set_filename(naam);
|
||||
fcbout->Set_ext(ext);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool DOS_FileExists(char * name) {
|
||||
Bit16u handle;
|
||||
if (DOS_OpenFile(name,0,&handle)) {
|
||||
DOS_CloseFile(handle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool DOS_SetDrive(Bit8u drive) {
|
||||
if (Drives[drive]) {
|
||||
DOS_SetDefaultDrive(drive);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void DOS_SetupFiles (void) {
|
||||
/* Setup the File Handles */
|
||||
Bit32u i;
|
||||
for (i=0;i<DOS_FILES;i++) {
|
||||
Files[i]=0;
|
||||
}
|
||||
/* Setup the Virtual Disk System */
|
||||
for (i=0;i<DOS_DRIVES;i++) {
|
||||
Drives[i]=0;
|
||||
}
|
||||
Drives[25]=new Virtual_Drive();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
60
src/dos/dos_ioctl.cpp
Normal file
60
src/dos/dos_ioctl.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "dosbox.h"
|
||||
#include "callback.h"
|
||||
#include "mem.h"
|
||||
#include "cpu.h"
|
||||
#include "dos_inc.h"
|
||||
|
||||
#define MAX_DEVICE 20
|
||||
static DOS_File * dos_devices[MAX_DEVICE];
|
||||
|
||||
bool DOS_IOCTL(Bit8u call,Bit16u entry) {
|
||||
Bit32u handle=RealHandle(entry);
|
||||
if (handle>=DOS_FILES) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
if (!Files[handle]) {
|
||||
DOS_SetError(DOSERR_INVALID_HANDLE);
|
||||
return false;
|
||||
};
|
||||
switch(reg_al) {
|
||||
case 0x00: /* Get Device Information */
|
||||
reg_dx=Files[handle]->GetInformation();
|
||||
return true;
|
||||
case 0x07: /* Get Output Status */
|
||||
LOG_DEBUG("DOS:IOCTL:07:Fakes output status is ready for handle %d",handle);
|
||||
reg_al=0xff;
|
||||
return true;
|
||||
default:
|
||||
LOG_ERROR("DOS:IOCTL Call %2X Handle %2X unhandled",reg_al,handle);
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
bool DOS_GetSTDINStatus(void) {
|
||||
Bit32u handle=RealHandle(STDIN);
|
||||
if (Files[handle]->GetInformation() & 64) return false;
|
||||
return true;
|
||||
};
|
||||
|
162
src/dos/dos_memory.cpp
Normal file
162
src/dos/dos_memory.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "mem.h"
|
||||
#include "dos_inc.h"
|
||||
|
||||
|
||||
#define MEM_START 0x60 //First Segment that DOS can use
|
||||
//#define MEM_START 4000 //First Segment that DOS can use
|
||||
|
||||
|
||||
static void DOS_CompressMemory(void) {
|
||||
MCB * pmcb;MCB * pmcbnext;
|
||||
Bit16u mcb_segment;
|
||||
mcb_segment=dos.firstMCB;
|
||||
pmcb=(MCB *)real_off(mcb_segment,0);
|
||||
while (pmcb->type!=0x5a) {
|
||||
pmcbnext=pmcbnext=(MCB *)real_off(mcb_segment+pmcb->size+1,0);
|
||||
if ((pmcb->psp_segment==0) && (pmcbnext->psp_segment==0)) {
|
||||
pmcb->size+=pmcbnext->size+1;
|
||||
pmcb->type=pmcbnext->type;
|
||||
} else {
|
||||
mcb_segment+=pmcb->size+1;
|
||||
pmcb=(MCB *)real_off(mcb_segment,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DOS_FreeProcessMemory(Bit16u pspseg) {
|
||||
MCB * pmcb;
|
||||
Bit16u mcb_segment=dos.firstMCB;
|
||||
pmcb=(MCB *)real_off(mcb_segment,0);
|
||||
while (true) {
|
||||
if (pmcb->psp_segment==pspseg) {
|
||||
pmcb->psp_segment=MCB_FREE;
|
||||
}
|
||||
mcb_segment+=pmcb->size+1;
|
||||
if (pmcb->type==0x5a) break;
|
||||
pmcb=(MCB *)real_off(mcb_segment,0);
|
||||
}
|
||||
DOS_CompressMemory();
|
||||
};
|
||||
|
||||
|
||||
bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) {
|
||||
MCB * pmcb;MCB * pmcbnext;
|
||||
Bit16u bigsize=0;Bit16u mcb_segment;
|
||||
bool stop=false;mcb_segment=dos.firstMCB;
|
||||
DOS_CompressMemory();
|
||||
while(!stop) {
|
||||
pmcb=(MCB *)real_off(mcb_segment,0);
|
||||
if (pmcb->psp_segment==0) {
|
||||
/* Check for enough free memory in current block */
|
||||
if (pmcb->size<(*blocks)) {
|
||||
if (bigsize<pmcb->size) {
|
||||
bigsize=pmcb->size;
|
||||
}
|
||||
|
||||
} else if (pmcb->size==*blocks) {
|
||||
pmcb->psp_segment=dos.psp;
|
||||
*segment=mcb_segment+1;
|
||||
return true;
|
||||
} else {
|
||||
/* If so allocate it */
|
||||
pmcbnext=(MCB *)real_off(mcb_segment+*blocks+1,0);
|
||||
pmcbnext->psp_segment=MCB_FREE;
|
||||
pmcbnext->type=pmcb->type;
|
||||
pmcbnext->size=pmcb->size-*blocks-1;
|
||||
pmcb->size=*blocks;
|
||||
pmcb->type=0x4D;
|
||||
pmcb->psp_segment=dos.psp;
|
||||
//TODO Filename
|
||||
*segment=mcb_segment+1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Onward to the next MCB if there is one */
|
||||
if (pmcb->type==0x5a) {
|
||||
*blocks=bigsize;
|
||||
DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
|
||||
return false;
|
||||
}
|
||||
mcb_segment+=pmcb->size+1;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks) {
|
||||
DOS_CompressMemory();
|
||||
MCB * pmcb,* pmcbnext,* pmcbnew;
|
||||
pmcb=(MCB *)real_off(segment-1,0);
|
||||
pmcbnext=(MCB *)real_off(segment+pmcb->size,0);
|
||||
Bit16u total=pmcb->size;
|
||||
if (pmcb->type!=0x5a) {
|
||||
if (pmcbnext->psp_segment==MCB_FREE) {
|
||||
total+=pmcbnext->size+1;
|
||||
}
|
||||
};
|
||||
if (*blocks<total) {
|
||||
if (pmcb->type!=0x5a) {
|
||||
pmcb->type=pmcbnext->type;
|
||||
}
|
||||
pmcb->size=*blocks;
|
||||
pmcbnew=(MCB *)real_off(segment+*blocks,0);
|
||||
pmcbnew->size=total-*blocks-1;
|
||||
pmcbnew->type=pmcb->type;
|
||||
pmcbnew->psp_segment=MCB_FREE;
|
||||
pmcb->type=0x4D;
|
||||
return true;
|
||||
}
|
||||
if (*blocks==total) {
|
||||
if (pmcb->type!=0x5a) {
|
||||
pmcb->type=pmcbnext->type;
|
||||
}
|
||||
pmcb->size=*blocks;
|
||||
return true;
|
||||
}
|
||||
*blocks=total;
|
||||
DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
bool DOS_FreeMemory(Bit16u segment) {
|
||||
//TODO Check if allowed to free this segment
|
||||
MCB * pmcb;
|
||||
pmcb=(MCB *)real_off(segment-1,0);
|
||||
pmcb->psp_segment=MCB_FREE;
|
||||
DOS_CompressMemory();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void DOS_SetupMemory(void) {
|
||||
//TODO Maybe allocate some memory for dos transfer buffers
|
||||
//Although i could use bios regions for that for max free low memory
|
||||
MCB * mcb=(MCB *) real_off(MEM_START,0);
|
||||
mcb->psp_segment=MCB_FREE; //Free
|
||||
mcb->size=0x9FFE - MEM_START;
|
||||
mcb->type=0x5a; //Last Block
|
||||
dos.firstMCB=MEM_START;
|
||||
}
|
||||
|
69
src/dos/dos_misc.cpp
Normal file
69
src/dos/dos_misc.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "callback.h"
|
||||
#include "mem.h"
|
||||
#include "regs.h"
|
||||
#include "dos_inc.h"
|
||||
|
||||
|
||||
static Bitu call_int2f,call_int2a;
|
||||
struct MultiplexBlock {
|
||||
MultiplexHandler * handler;
|
||||
MultiplexBlock * next;
|
||||
};
|
||||
|
||||
static MultiplexBlock * first_multiplex;
|
||||
|
||||
void DOS_AddMultiplexHandler(MultiplexHandler * handler) {
|
||||
MultiplexBlock * new_multiplex=new(MultiplexBlock);
|
||||
new_multiplex->next=first_multiplex;
|
||||
new_multiplex->handler=handler;
|
||||
first_multiplex=new_multiplex;
|
||||
}
|
||||
|
||||
static Bitu INT2F_Handler(void) {
|
||||
MultiplexBlock * loop_multiplex=first_multiplex;
|
||||
while (loop_multiplex) {
|
||||
if ((*loop_multiplex->handler)()) return CBRET_NONE;
|
||||
loop_multiplex=loop_multiplex->next;
|
||||
}
|
||||
LOG_WARN("DOS:Multiplex Unhandled call %4X",reg_ax);
|
||||
return CBRET_NONE;
|
||||
};
|
||||
|
||||
|
||||
static Bitu INT2A_Handler(void) {
|
||||
|
||||
return CBRET_NONE;
|
||||
};
|
||||
|
||||
|
||||
void DOS_SetupMisc(void) {
|
||||
/* Setup the dos multiplex interrupt */
|
||||
first_multiplex=0;
|
||||
call_int2f=CALLBACK_Allocate();
|
||||
CALLBACK_Setup(call_int2f,&INT2F_Handler,CB_IRET);
|
||||
RealSetVec(0x2f,CALLBACK_RealPointer(call_int2f));
|
||||
/* Setup the dos network interrupt */
|
||||
call_int2a=CALLBACK_Allocate();
|
||||
CALLBACK_Setup(call_int2a,&INT2A_Handler,CB_IRET);
|
||||
RealSetVec(0x2A<<2,CALLBACK_RealPointer(call_int2a));
|
||||
};
|
||||
|
210
src/dos/dos_programs.cpp
Normal file
210
src/dos/dos_programs.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "programs.h"
|
||||
#include "support.h"
|
||||
#include "drives.h"
|
||||
#include "cross.h"
|
||||
|
||||
|
||||
class MOUNT : public Program {
|
||||
public:
|
||||
MOUNT(PROGRAM_Info * program_info);
|
||||
void Run(void);
|
||||
};
|
||||
|
||||
|
||||
MOUNT::MOUNT(PROGRAM_Info * info):Program(info) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void MOUNT::Run(void) {
|
||||
/* Parse the command line */
|
||||
/* if the command line is empty show current mounts */
|
||||
|
||||
if (!*prog_info->cmd_line) {
|
||||
WriteOut("Current mounted drives are\n");
|
||||
for (int d=0;d<DOS_DRIVES;d++) {
|
||||
if (Drives[d]) {
|
||||
WriteOut("Drive %c is mounted as %s\n",d+'A',Drives[d]->GetInfo());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char drive;
|
||||
drive=toupper(*prog_info->cmd_line);
|
||||
char * dir=strchr(prog_info->cmd_line,' ');
|
||||
if (dir) {
|
||||
if (!*dir) dir=0;
|
||||
else dir=trim(dir);
|
||||
};
|
||||
|
||||
if (!isalpha(drive) || !dir) {
|
||||
WriteOut("Usage MOUNT Drive-Letter Local-Directory\nSo a MOUNT c c:\\windows mounts windows directory as the c: drive in DOSBox\n");
|
||||
return;
|
||||
};
|
||||
struct stat test;
|
||||
if (stat(dir,&test)) {
|
||||
WriteOut("Directory %s Doesn't exist",dir);
|
||||
return;
|
||||
}
|
||||
/* Not a switch so a normal directory/file */
|
||||
if (!(test.st_mode & S_IFDIR)) {
|
||||
WriteOut("%s isn't a directory",dir);
|
||||
return;
|
||||
}
|
||||
if (Drives[drive-'A']) {
|
||||
WriteOut("Drive %c already mounted with %s\n",drive,Drives[drive-'A']->GetInfo());
|
||||
return;
|
||||
}
|
||||
char fulldir[DOS_PATHLENGTH];
|
||||
strcpy(fulldir,dir);
|
||||
static char theend[2]={CROSS_FILESPLIT,0};
|
||||
char * last=strrchr(fulldir,CROSS_FILESPLIT);
|
||||
if (!last || *(++last)) strcat(fulldir,theend);
|
||||
Drives[drive-'A']=new localDrive(fulldir);
|
||||
WriteOut("Mounting drive %c as %s\n",drive,fulldir);
|
||||
}
|
||||
|
||||
static void MOUNT_ProgramStart(PROGRAM_Info * info) {
|
||||
MOUNT * tempmount=new MOUNT(info);
|
||||
tempmount->Run();
|
||||
delete tempmount;
|
||||
}
|
||||
|
||||
|
||||
class MEM : public Program {
|
||||
public:
|
||||
MEM(PROGRAM_Info * program_info);
|
||||
void Run(void);
|
||||
};
|
||||
|
||||
|
||||
MEM::MEM(PROGRAM_Info * info):Program(info) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void MEM::Run(void) {
|
||||
|
||||
}
|
||||
|
||||
static void MEM_ProgramStart(PROGRAM_Info * info) {
|
||||
MEM mem(info);
|
||||
mem.Run();
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if !defined (WIN32) /* Unix */
|
||||
class UPCASE : public Program {
|
||||
public:
|
||||
UPCASE(PROGRAM_Info * program_info);
|
||||
void Run(void);
|
||||
void upcasedir(char * directory);
|
||||
};
|
||||
|
||||
|
||||
UPCASE::UPCASE(PROGRAM_Info * info):Program(info) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void UPCASE::upcasedir(char * directory) {
|
||||
DIR * sdir;
|
||||
char fullname[512];
|
||||
char newname[512];
|
||||
struct dirent *tempdata;
|
||||
struct stat finfo;
|
||||
|
||||
if(!(sdir=opendir(directory))) {
|
||||
WriteOut("Failed to open directory %s\n",directory);
|
||||
return;
|
||||
}
|
||||
WriteOut("Scanning directory %s\n",fullname);
|
||||
while (tempdata=readdir(sdir)) {
|
||||
if (strcmp(tempdata->d_name,".")==0) continue;
|
||||
if (strcmp(tempdata->d_name,"..")==0) continue;
|
||||
strcpy(fullname,directory);
|
||||
strcat(fullname,"/");
|
||||
strcat(fullname,tempdata->d_name);
|
||||
strcpy(newname,directory);
|
||||
strcat(newname,"/");
|
||||
upcase(tempdata->d_name);
|
||||
strcat(newname,tempdata->d_name);
|
||||
WriteOut("Renaming %s to %s\n",fullname,newname);
|
||||
rename(fullname,newname);
|
||||
stat(fullname,&finfo);
|
||||
if(S_ISDIR(finfo.st_mode)) {
|
||||
upcasedir(fullname);
|
||||
}
|
||||
}
|
||||
closedir(sdir);
|
||||
}
|
||||
|
||||
|
||||
void UPCASE::Run(void) {
|
||||
/* First check if the directory exists */
|
||||
struct stat info;
|
||||
WriteOut("UPCASE 0.1 Directory case convertor.\n");
|
||||
if (!strlen(prog_info->cmd_line)) {
|
||||
WriteOut("Usage UPCASE [local directory]\n");
|
||||
WriteOut("This tool will convert all files and subdirectories in a directory.\n");
|
||||
WriteOut("Be VERY sure this directory contains only dos related material.\n");
|
||||
WriteOut("Otherwise you might horribly screw up your filesystem.\n");
|
||||
return;
|
||||
}
|
||||
if (stat(prog_info->cmd_line,&info)) {
|
||||
WriteOut("%s doesn't exist\n",prog_info->cmd_line);
|
||||
return;
|
||||
}
|
||||
if(!S_ISDIR(info.st_mode)) {
|
||||
WriteOut("%s isn't a directory\n",prog_info->cmd_line);
|
||||
return;
|
||||
}
|
||||
WriteOut("Converting the wrong directories can be very harmfull, please be carefull.\n");
|
||||
WriteOut("Are you really really sure you want to convert %s to upcase?Y/N\n",prog_info->cmd_line);
|
||||
Bit8u key;Bit16u n=1;
|
||||
DOS_ReadFile(STDIN,&key,&n);
|
||||
if (toupper(key)=='Y') {
|
||||
upcasedir(prog_info->cmd_line);
|
||||
} else {
|
||||
WriteOut("Okay better not do it.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void UPCASE_ProgramStart(PROGRAM_Info * info) {
|
||||
UPCASE * tempUPCASE=new UPCASE(info);
|
||||
tempUPCASE->Run();
|
||||
delete tempUPCASE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void DOS_SetupPrograms(void) {
|
||||
PROGRAMS_MakeFile("MOUNT.COM",MOUNT_ProgramStart);
|
||||
// PROGRAMS_MakeFile("MEM.COM",MEM_ProgramStart); /*next release */
|
||||
#if !defined (WIN32) /* Unix */
|
||||
PROGRAMS_MakeFile("UPCASE.COM",UPCASE_ProgramStart);
|
||||
#endif
|
||||
}
|
54
src/dos/dos_tables.cpp
Normal file
54
src/dos/dos_tables.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "mem.h"
|
||||
#include "dos_inc.h"
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct DOS_TableCase {
|
||||
Bit16u size;
|
||||
Bit8u chars[256];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
|
||||
RealPt DOS_TableUpCase;
|
||||
RealPt DOS_TableLowCase;
|
||||
|
||||
static Bit16u dos_memseg=0xd000;
|
||||
Bit16u DOS_GetMemory(Bit16u pages) {
|
||||
if (pages+dos_memseg>=0xe000) {
|
||||
E_Exit("DOS:Not enough memory for internal tables");
|
||||
}
|
||||
Bit16u page=dos_memseg;
|
||||
dos_memseg+=pages;
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
void DOS_SetupTables(void) {
|
||||
dos.tables.indosflag=RealMake(DOS_GetMemory(1),0);
|
||||
mem_writeb(Real2Phys(dos.tables.indosflag),0);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
277
src/dos/drive_local.cpp
Normal file
277
src/dos/drive_local.cpp
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "dosbox.h"
|
||||
#include "dos_system.h"
|
||||
#include "drives.h"
|
||||
#include "support.h"
|
||||
#include "cross.h"
|
||||
|
||||
|
||||
class localFile : public DOS_File {
|
||||
public:
|
||||
localFile(FILE * handle,Bit16u devinfo);
|
||||
bool Read(Bit8u * data,Bit16u * size);
|
||||
bool Write(Bit8u * data,Bit16u * size);
|
||||
bool Seek(Bit32u * pos,Bit32u type);
|
||||
bool Close();
|
||||
Bit16u GetInformation(void);
|
||||
private:
|
||||
FILE * fhandle;
|
||||
Bit16u info;
|
||||
};
|
||||
|
||||
|
||||
bool localDrive:: FileCreate(DOS_File * * file,char * name,Bit16u attributes) {
|
||||
//TODO Maybe care for attributes but not likely
|
||||
char newname[512];
|
||||
strcpy(newname,basedir);
|
||||
strcat(newname,name);
|
||||
CROSS_FILENAME(newname);
|
||||
FILE * hand=fopen(newname,"wb+");
|
||||
if (!hand) return false;
|
||||
/* Make the 16 bit device information */
|
||||
*file=new localFile(hand,0x202);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
|
||||
char * type;
|
||||
switch (flags) {
|
||||
case OPEN_READ:type="rb";break;
|
||||
case OPEN_WRITE:type="rb+";break;
|
||||
case OPEN_READWRITE:type="rb+";break;
|
||||
default:
|
||||
//TODO FIX IT
|
||||
type="rb+";
|
||||
// return false;
|
||||
|
||||
};
|
||||
char newname[512];
|
||||
strcpy(newname,basedir);
|
||||
strcat(newname,name);
|
||||
CROSS_FILENAME(newname);
|
||||
FILE * hand=fopen(newname,type);
|
||||
Bit32u err=errno;
|
||||
if (!hand) return false;
|
||||
*file=new localFile(hand,0x202);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool localDrive::FileUnlink(char * name) {
|
||||
char newname[512];
|
||||
strcpy(newname,basedir);
|
||||
strcat(newname,name);
|
||||
CROSS_FILENAME(newname);
|
||||
if (!unlink(newname)) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
bool localDrive::FindFirst(char * search,DTA_FindBlock * dta) {
|
||||
//TODO Find some way for lowcase and highcase drives oneday
|
||||
char name[512];
|
||||
strcpy(name,basedir);
|
||||
strcat(name,search);
|
||||
CROSS_FILENAME(name);
|
||||
char * last=strrchr(name, CROSS_FILESPLIT);
|
||||
*last=0;
|
||||
last++;
|
||||
/* Check the wildcard string for an extension */
|
||||
strcpy(wild_name,last);
|
||||
wild_ext=strrchr(wild_name,'.');
|
||||
if (wild_ext) {
|
||||
*wild_ext++=0;
|
||||
}
|
||||
strcpy(directory,name);
|
||||
/* make sure / is last sign */
|
||||
if (pdir) closedir(pdir);
|
||||
if(directory[(strlen(directory)-1)]!=CROSS_FILESPLIT) strcat(directory, "/");
|
||||
if((pdir=opendir(directory))==NULL) return false;
|
||||
return FindNext(dta);
|
||||
}
|
||||
|
||||
bool localDrive::FindNext(DTA_FindBlock * dta) {
|
||||
Bit8u tempattr=0;
|
||||
struct dirent *tempdata;
|
||||
struct stat stat_block;
|
||||
char werkbuffer[512];
|
||||
|
||||
if(pdir==NULL){
|
||||
return false;
|
||||
};
|
||||
start:
|
||||
if((tempdata=readdir(pdir))==NULL) {
|
||||
closedir(pdir);
|
||||
pdir=NULL;
|
||||
return false;
|
||||
}
|
||||
strcpy(werkbuffer,tempdata->d_name);
|
||||
if (wild_ext) {
|
||||
char * ext=strrchr(werkbuffer,'.');
|
||||
if (!ext) ext="*";
|
||||
else *ext++=0;
|
||||
if(!wildcmp(wild_ext,ext)) goto start;
|
||||
}
|
||||
if(!wildcmp(wild_name,werkbuffer)) goto start;
|
||||
werkbuffer[0]='\0';
|
||||
strcpy(werkbuffer,directory);
|
||||
strcat(werkbuffer,tempdata->d_name);
|
||||
if(stat(werkbuffer,&stat_block)!=0){
|
||||
/*nu is er iets fout!*/ exit(1);
|
||||
}
|
||||
if(S_ISDIR(stat_block.st_mode)) tempattr=DOS_ATTR_DIRECTORY;
|
||||
else tempattr=DOS_ATTR_ARCHIVE;
|
||||
if(!(dta->sattr & tempattr)) goto start;
|
||||
/*file is oke so filldtablok */
|
||||
if(strlen(tempdata->d_name)<=DOS_NAMELENGTH) strcpy(dta->name,upcase(tempdata->d_name));
|
||||
dta->attr=tempattr;
|
||||
dta->size=(Bit32u) stat_block.st_size;
|
||||
struct tm *time;
|
||||
time=localtime(&stat_block.st_mtime);
|
||||
|
||||
dta->time=(time->tm_hour<<11)+(time->tm_min<<5)+(time->tm_sec/2); /* standard way. */
|
||||
dta->date=((time->tm_year-80)<<9)+((time->tm_mon+1)<<5)+(time->tm_mday);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool localDrive::GetFileAttr(char * name,Bit16u * attr) {
|
||||
char newname[512];
|
||||
strcpy(newname,basedir);
|
||||
strcat(newname,name);
|
||||
CROSS_FILENAME(newname);
|
||||
FILE * hand=fopen(newname,"rb");
|
||||
if (hand) {
|
||||
fclose(hand);
|
||||
*attr=DOS_ATTR_ARCHIVE;
|
||||
return true;
|
||||
}
|
||||
*attr=0;
|
||||
return false;
|
||||
};
|
||||
|
||||
bool localDrive::MakeDir(char * dir) {
|
||||
char newdir[512];
|
||||
strcpy(newdir,basedir);
|
||||
strcat(newdir,dir);
|
||||
CROSS_FILENAME(newdir);
|
||||
#if defined (WIN32) /* MS Visual C++ */
|
||||
int temp=mkdir(newdir);
|
||||
#else
|
||||
int temp=mkdir(newdir,0);
|
||||
#endif
|
||||
return (temp==0);
|
||||
}
|
||||
|
||||
bool localDrive::RemoveDir(char * dir) {
|
||||
char newdir[512];
|
||||
strcpy(newdir,basedir);
|
||||
strcat(newdir,dir);
|
||||
int temp=rmdir(newdir);
|
||||
return (temp==0);
|
||||
}
|
||||
|
||||
bool localDrive::TestDir(char * dir) {
|
||||
char newdir[512];
|
||||
strcpy(newdir,basedir);
|
||||
strcat(newdir,dir);
|
||||
int temp=access(newdir,F_OK);
|
||||
return (temp==0);
|
||||
}
|
||||
|
||||
bool localDrive::Rename(char * oldname,char * newname) {
|
||||
char newold[512];
|
||||
strcpy(newold,basedir);
|
||||
strcat(newold,oldname);
|
||||
CROSS_FILENAME(newold);
|
||||
char newnew[512];
|
||||
strcpy(newnew,basedir);
|
||||
strcat(newnew,newnew);
|
||||
CROSS_FILENAME(newname);
|
||||
int temp=rename(newold,newnew);
|
||||
return (temp==0);
|
||||
|
||||
};
|
||||
|
||||
bool localDrive::FreeSpace(Bit16u * bytes,Bit16u * sectors,Bit16u * clusters,Bit16u * free) {
|
||||
/* Always report 100 mb free should be enough */
|
||||
/* Total size is always 1 gb */
|
||||
*bytes=512;
|
||||
*sectors=127;
|
||||
*clusters=16513;
|
||||
*free=1700;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
localDrive::localDrive(char * startdir) {
|
||||
strcpy(basedir,startdir);
|
||||
sprintf(info,"local directory %s",startdir);
|
||||
pdir=NULL;
|
||||
}
|
||||
|
||||
|
||||
bool localFile::Read(Bit8u * data,Bit16u * size) {
|
||||
*size=fread(data,1,*size,fhandle);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool localFile::Write(Bit8u * data,Bit16u * size) {
|
||||
*size=fwrite(data,1,*size,fhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool localFile::Seek(Bit32u * pos,Bit32u type) {
|
||||
int seektype;
|
||||
switch (type) {
|
||||
case DOS_SEEK_SET:seektype=SEEK_SET;break;
|
||||
case DOS_SEEK_CUR:seektype=SEEK_CUR;break;
|
||||
case DOS_SEEK_END:seektype=SEEK_END;break;
|
||||
default:
|
||||
//TODO Give some doserrorcode;
|
||||
return false;//ERROR
|
||||
}
|
||||
fpos_t temppos;
|
||||
int ret=fseek(fhandle,*pos,seektype);
|
||||
fgetpos(fhandle,&temppos);
|
||||
//TODO Hope we don't encouter files with 64 bits size
|
||||
Bit32u * fake_pos=(Bit32u*)&temppos;
|
||||
*pos=*fake_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool localFile::Close() {
|
||||
fclose(fhandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
Bit16u localFile::GetInformation(void) {
|
||||
return info;
|
||||
}
|
||||
|
||||
localFile::localFile(FILE * handle,Bit16u devinfo) {
|
||||
fhandle=handle;
|
||||
info=devinfo;
|
||||
}
|
||||
|
205
src/dos/drive_virtual.cpp
Normal file
205
src/dos/drive_virtual.cpp
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "dosbox.h"
|
||||
#include "dos_system.h"
|
||||
#include "drives.h"
|
||||
#include "support.h"
|
||||
|
||||
struct VFILE_Block {
|
||||
char * name;
|
||||
Bit8u * data;
|
||||
Bit32u size;
|
||||
VFILE_Block * next;
|
||||
};
|
||||
|
||||
|
||||
static VFILE_Block * first_file;
|
||||
|
||||
void VFILE_Register(char * name,Bit8u * data,Bit32u size) {
|
||||
VFILE_Block * new_file=new VFILE_Block;
|
||||
new_file->name=name;
|
||||
new_file->data=data;
|
||||
new_file->size=size;
|
||||
new_file->next=first_file;
|
||||
first_file=new_file;
|
||||
}
|
||||
|
||||
|
||||
class Virtual_File : public DOS_File {
|
||||
public:
|
||||
Virtual_File(Bit8u * in_data,Bit32u in_size);
|
||||
bool Read(Bit8u * data,Bit16u * size);
|
||||
bool Write(Bit8u * data,Bit16u * size);
|
||||
bool Seek(Bit32u * pos,Bit32u type);
|
||||
bool Close();
|
||||
Bit16u GetInformation(void);
|
||||
private:
|
||||
Bit32u file_size;
|
||||
Bit32u file_pos;
|
||||
Bit8u * file_data;
|
||||
};
|
||||
|
||||
|
||||
Virtual_File::Virtual_File(Bit8u * in_data,Bit32u in_size) {
|
||||
file_size=in_size;
|
||||
file_data=in_data;
|
||||
file_pos=0;
|
||||
}
|
||||
|
||||
bool Virtual_File::Read(Bit8u * data,Bit16u * size) {
|
||||
Bit32u left=file_size-file_pos;
|
||||
if (left<=*size) {
|
||||
memcpy(data,&file_data[file_pos],left);
|
||||
*size=(Bit16u)left;
|
||||
} else {
|
||||
memcpy(data,&file_data[file_pos],*size);
|
||||
}
|
||||
file_pos+=*size;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool Virtual_File::Write(Bit8u * data,Bit16u * size){
|
||||
/* Not really writeable */
|
||||
return false;
|
||||
};
|
||||
|
||||
bool Virtual_File::Seek(Bit32u * new_pos,Bit32u type){
|
||||
switch (type) {
|
||||
case DOS_SEEK_SET:
|
||||
if (*new_pos<=file_size) file_pos=*new_pos;
|
||||
else return false;
|
||||
break;
|
||||
case DOS_SEEK_CUR:
|
||||
if ((*new_pos+file_pos)<=file_size) file_pos=*new_pos+file_pos;
|
||||
else return false;
|
||||
break;
|
||||
case DOS_SEEK_END:
|
||||
if (*new_pos<=file_size) file_pos=file_size-*new_pos;
|
||||
else return false;
|
||||
break;
|
||||
}
|
||||
*new_pos=file_pos;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool Virtual_File::Close(){
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Bit16u Virtual_File::GetInformation(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Virtual_Drive::Virtual_Drive() {
|
||||
strcpy(info,"Internal Virtual Drive");
|
||||
search_file=0;
|
||||
}
|
||||
|
||||
|
||||
bool Virtual_Drive::FileOpen(DOS_File * * file,char * name,Bit32u flags) {
|
||||
/* Scan through the internal list of files */
|
||||
VFILE_Block * cur_file=first_file;
|
||||
while (cur_file) {
|
||||
if (strcasecmp(name,cur_file->name)==0) {
|
||||
/* We have a match */
|
||||
*file=new Virtual_File(cur_file->data,cur_file->size);
|
||||
return true;
|
||||
}
|
||||
cur_file=cur_file->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::FileCreate(DOS_File * * file,char * name,Bit16u attributes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::FileUnlink(char * name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::RemoveDir(char * dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::MakeDir(char * dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::TestDir(char * dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void FillDTABlock(DTA_FindBlock * dta,VFILE_Block * fill_file) {
|
||||
strcpy(dta->name,fill_file->name);
|
||||
dta->size=fill_file->size;
|
||||
dta->attr=DOS_ATTR_ARCHIVE;
|
||||
dta->time=2;
|
||||
dta->date=6;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::FindFirst(char * search,DTA_FindBlock * dta) {
|
||||
search_file=first_file;
|
||||
strcpy(search_string,search);
|
||||
while (search_file) {
|
||||
if (wildcmp(search_string,search_file->name)) {
|
||||
FillDTABlock(dta,search_file);
|
||||
search_file=search_file->next;
|
||||
return true;
|
||||
}
|
||||
search_file=search_file->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::FindNext(DTA_FindBlock * dta) {
|
||||
while (search_file) {
|
||||
if (wildcmp(search_string,search_file->name)) {
|
||||
FillDTABlock(dta,search_file);
|
||||
search_file=search_file->next;
|
||||
return true;
|
||||
}
|
||||
search_file=search_file->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::GetFileAttr(char * name,Bit16u * attr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::Rename(char * oldname,char * newname) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Virtual_Drive::FreeSpace(Bit16u * bytes,Bit16u * sectors,Bit16u * clusters,Bit16u * free) {
|
||||
/* Always report 100 mb free should be enough */
|
||||
/* Total size is always 1 gb */
|
||||
*bytes=512;
|
||||
*sectors=127;
|
||||
*clusters=16513;
|
||||
*free=00;
|
||||
return true;
|
||||
}
|
||||
|
31
src/dos/drives.cpp
Normal file
31
src/dos/drives.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "dos_system.h"
|
||||
#include "drives.h"
|
||||
|
||||
|
||||
DOS_Drive::DOS_Drive() {
|
||||
curdir[0]=0;
|
||||
info[0]=0;
|
||||
}
|
||||
|
||||
char * DOS_Drive::GetInfo(void) {
|
||||
return info;
|
||||
}
|
71
src/dos/drives.h
Normal file
71
src/dos/drives.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVES_H__
|
||||
#define _DRIVES_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
|
||||
class localDrive : public DOS_Drive {
|
||||
public:
|
||||
localDrive(char * startdir);
|
||||
bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
|
||||
bool FileCreate(DOS_File * * file,char * name,Bit16u attributes);
|
||||
bool FileUnlink(char * name);
|
||||
bool RemoveDir(char * dir);
|
||||
bool MakeDir(char * dir);
|
||||
bool TestDir(char * dir);
|
||||
bool FindFirst(char * search,DTA_FindBlock * dta);
|
||||
bool FindNext(DTA_FindBlock * dta);
|
||||
bool GetFileAttr(char * name,Bit16u * attr);
|
||||
bool Rename(char * oldname,char * newname);
|
||||
bool FreeSpace(Bit16u * bytes,Bit16u * sectors,Bit16u * clusters,Bit16u * free);
|
||||
private:
|
||||
bool FillDTABlock(DTA_FindBlock * dta);
|
||||
char basedir[512];
|
||||
char directory[512];
|
||||
char wild_name[15];
|
||||
char * wild_ext;
|
||||
DIR *pdir;
|
||||
};
|
||||
|
||||
struct VFILE_Block;
|
||||
|
||||
|
||||
class Virtual_Drive: public DOS_Drive {
|
||||
public:
|
||||
Virtual_Drive();
|
||||
bool FileOpen(DOS_File * * file,char * name,Bit32u flags);
|
||||
bool FileCreate(DOS_File * * file,char * name,Bit16u attributes);
|
||||
bool FileUnlink(char * name);
|
||||
bool RemoveDir(char * dir);
|
||||
bool MakeDir(char * dir);
|
||||
bool TestDir(char * dir);
|
||||
bool FindFirst(char * search,DTA_FindBlock * dta);
|
||||
bool FindNext(DTA_FindBlock * dta);
|
||||
bool GetFileAttr(char * name,Bit16u * attr);
|
||||
bool Rename(char * oldname,char * newname);
|
||||
bool FreeSpace(Bit16u * bytes,Bit16u * sectors,Bit16u * clusters,Bit16u * free);
|
||||
private:
|
||||
VFILE_Block * search_file;
|
||||
char search_string[255];
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue