1
0
Fork 0

New execute/terminate that saves registers and segments.

Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@272
This commit is contained in:
Sjoerd van der Berg 2002-09-16 13:10:48 +00:00
parent 16f6308ef7
commit af67029cdc

View file

@ -21,12 +21,10 @@
#include "mem.h"
#include "dos_inc.h"
#include "cpu.h"
#if defined (_MSC_VER)
#pragma pack(1)
#endif
#include "callback.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 */
@ -41,13 +39,8 @@ struct EXE_Header {
Bit16u initCS;
Bit16u reloctable;
Bit16u overlay;
}
#if defined (_MSC_VER)
;
} GCC_ATTRIBUTE(packed);
#pragma pack()
#else
__attribute__ ((packed));
#endif
#define MAGIC1 0x5a4d
#define MAGIC2 0x4d5a
@ -63,31 +56,64 @@ __attribute__ ((packed));
#define OVERLAY 3
static void SaveRegisters(void) {
reg_sp-=20;
mem_writew(SegPhys(ss)+reg_sp+ 0,reg_ax);
mem_writew(SegPhys(ss)+reg_sp+ 2,reg_cx);
mem_writew(SegPhys(ss)+reg_sp+ 4,reg_dx);
mem_writew(SegPhys(ss)+reg_sp+ 6,reg_bx);
mem_writew(SegPhys(ss)+reg_sp+ 8,reg_si);
mem_writew(SegPhys(ss)+reg_sp+10,reg_di);
mem_writew(SegPhys(ss)+reg_sp+12,reg_bp);
mem_writew(SegPhys(ss)+reg_sp+14,SegValue(ds));
mem_writew(SegPhys(ss)+reg_sp+16,SegValue(es));
}
static void RestoreRegisters(void) {
reg_ax=mem_readw(SegPhys(ss)+reg_sp+ 0);
reg_cx=mem_readw(SegPhys(ss)+reg_sp+ 2);
reg_dx=mem_readw(SegPhys(ss)+reg_sp+ 4);
reg_bx=mem_readw(SegPhys(ss)+reg_sp+ 6);
reg_si=mem_readw(SegPhys(ss)+reg_sp+ 8);
reg_di=mem_readw(SegPhys(ss)+reg_sp+10);
reg_bp=mem_readw(SegPhys(ss)+reg_sp+12);
SegSet16(ds,mem_readw(SegPhys(ss)+reg_sp+14));
SegSet16(es,mem_readw(SegPhys(ss)+reg_sp+16));
reg_sp+=20;
}
bool DOS_Terminate(bool tsr) {
dos.return_code=reg_al;
dos.return_mode=RETURN_EXIT;
Bit16u mempsp = dos.psp;
DOS_PSP curpsp(dos.psp);
if (dos.psp==curpsp.GetParent()) return true;
/* Free Files owned by process */
if (!tsr) curpsp.CloseFiles();
// restore vectors
/* Get the termination address */
RealPt old22 = RealGetVec(0x22);
/* Restore vector 22,23,24 */
curpsp.RestoreVectors();
// Set parent psp
/* Set the parent PSP */
dos.psp = curpsp.GetParent();
DOS_PSP parentpsp(curpsp.GetParent());
/* Restore the DTA */
parentpsp.SetDTA(curpsp.GetDTA());
/* Restore the DTA of the parent psp */
dos.dta = parentpsp.GetDTA();
/* Restore the SS:SP to the previous one */
SegSet16(ss,RealSeg(parentpsp.GetStack()));
reg_sp = RealOff(parentpsp.GetStack());
/* Restore the old CS:IP from int 22h */
RealPt old22 = curpsp.GetInt22();
reg_ip = RealOff(old22);
SegSet16 (cs,RealSeg(old22));
/* Restore the stored registers */
RestoreRegisters();
/* Set the CS:IP stored in int 0x22 back on the stack */
mem_writew(SegPhys(ss)+reg_sp+0,RealOff(old22));
mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(old22));
// Free memory owned by process
if (!tsr) DOS_FreeProcessMemory(mempsp);
return true;
}
@ -109,7 +135,6 @@ static bool MakeEnv(char * name,Bit16u * segment) {
envread=HostMake(*segment,0);
}
//TODO Make a good DOS first psp
if (parentenv) {
for (envsize=0; ;envsize++) {
if (envsize>=MAXENV - ENV_KEEPFREE) {
@ -131,7 +156,7 @@ static bool MakeEnv(char * name,Bit16u * segment) {
}
*((Bit16u *) envwrite)=1;
envwrite+=2;
//TODO put the filename here
return DOS_Canonicalize(name,(char *)envwrite);
};
@ -145,14 +170,14 @@ bool DOS_NewPSP(Bit16u segment, Bit16u size)
static void SetupPSP(Bit16u pspseg,Bit16u memsize,Bit16u envseg) {
/* Fix the PSP index of this MCB */
/* Fix the PSP for psp and environment MCB's */
MCB * pspmcb=(MCB *)HostMake(pspseg-1,0);
pspmcb->psp_segment=pspseg;
MCB * envmcb=(MCB *)HostMake(envseg-1,0);
envmcb->psp_segment=pspseg;
DOS_PSP psp(pspseg);
psp.MakeNew(memsize+pspseg);
psp.MakeNew(memsize);
psp.SetEnvironment(envseg);
psp.SetFileHandle(STDIN ,DOS_FindDevice("CON"));
psp.SetFileHandle(STDOUT,DOS_FindDevice("CON"));
@ -173,233 +198,132 @@ static void SetupCMDLine(Bit16u pspseg,ParamBlock * block)
psp.SetCommandTail(block->exec.cmdtail);
}
static bool COM_Load(char * name,ParamBlock * block,Bit8u flag) {
Bit16u fhandle;
Bit16u size;Bit16u readsize;
Bit16u envseg,comseg;
Bit32u pos;
DOS_PSP callpsp(dos.psp);
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,HostMake(comseg,0),&readsize);
} else {
DOS_ReadFile(fhandle,HostMake(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,RealMakeSeg(cs,reg_ip));
DOS_PSP compsp(comseg);
compsp.SetInt22(RealMakeSeg(cs,reg_ip));
/* Everything setup somewhat setup CS:IP and SS:SP */
/* First save the SS:SP of program that called execute */
callpsp.SetStack(RealMakeSeg(ss,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:
/* copy fcbs */
compsp.SetFCB1(block->exec.fcb1);
compsp.SetFCB2(block->exec.fcb2);
/* setup reg */
SegSet16(cs,comseg);
SegSet16(ss,comseg);
SegSet16(ds,comseg);
SegSet16(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;
// During loading process, th param-block-mem might be overwritten (HostPt!) and
// therefore change the relocation address, so save these values.
ParamBlock block;
memcpy(&block,_block,sizeof(ParamBlock));
DOS_PSP callpsp(dos.psp);
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=HostMake(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,RealMakeSeg(cs,reg_ip));
DOS_PSP exepsp(pspseg);
exepsp.SetInt22(RealMakeSeg(cs,reg_ip));
/* Start up the actual EXE if we need to */
//TODO check for load and return
callpsp.SetStack(RealMakeSeg(ss,reg_sp));
dos.psp=pspseg;
SegSet16(cs,exeseg+header.initCS);
SegSet16(ss,exeseg+header.initSS);
SegSet16(ds,pspseg);
SegSet16(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;
EXE_Header head;Bitu i;
Bit16u fhandle;Bit16u len;Bit32u pos;
Bit16u pspseg,envseg,loadseg,memsize,readsize;
HostPt loadaddress;RealPt relocpt;
Bitu headersize,imagesize;
if (flags!=LOADNGO && flags!=OVERLAY) {
E_Exit("DOS:Not supported execute mode %d for file %s",flags,name);
}
/* Check for EXE or COM File */
bool iscom=false;
if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) return false;
size=sizeof(EXE_Header);
if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&size)) {
len=sizeof(EXE_Header);
if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&len)) {
DOS_CloseFile(fhandle);
return false;
}
if (!DOS_CloseFile(fhandle)) return false;
if (size<sizeof(EXE_Header)) iscom=true;
if (len<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);
else {
headersize=head.headersize*16;
imagesize=(head.pages-1)*512-headersize+head.extrabytes;
}
if (flags!=OVERLAY) {
/* Create an environment block */
envseg=block->exec.envseg;
if (!MakeEnv(name,&envseg)) {
DOS_CloseFile(fhandle);
return false;
}
/* Get Memory */
Bit16u minsize,maxsize;Bit16u maxfree=0xffff;DOS_AllocateMemory(&pspseg,&maxfree);
if (iscom) {
minsize=0x1000;maxsize=0xffff;
} else { /* Exe size calculated from header */
minsize=long2para(imagesize+(head.minmemory<<4)+256);
if (head.maxmemory!=0) maxsize=long2para(imagesize+(head.maxmemory<<4)+256);
else maxsize=0xffff;
}
if (maxfree<minsize) {
DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
DOS_FreeMemory(envseg);
return false;
}
if (maxfree<maxsize) memsize=maxfree;
else memsize=maxsize;
if (!DOS_AllocateMemory(&pspseg,&memsize)) E_Exit("DOS:Exec error in memory");
loadseg=pspseg+16;
/* Setup a psp */
SetupPSP(pspseg,memsize,envseg);
SetupCMDLine(pspseg,block);
} else loadseg=block->overlay.loadseg,0;
/* Load the executable */
loadaddress=HostMake(loadseg,0);
if (iscom) { /* COM Load 64k - 256 bytes max */
pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);
readsize=0xffff-256;
DOS_ReadFile(fhandle,loadaddress,&readsize);
} else { /* EXE Load in 32kb blocks and then relocate */
pos=headersize;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);
while (imagesize>0x7FFF) {
readsize=0x8000;DOS_ReadFile(fhandle,loadaddress,&readsize);
if (readsize!=0x8000) E_Exit("Illegal header");
loadaddress+=0x8000;imagesize-=0x8000;
}
if (imagesize>0) {
readsize=(Bit16u)imagesize;DOS_ReadFile(fhandle,loadaddress,&readsize);
if (readsize!=imagesize) E_Exit("Illegal header");
}
/* Relocate the exe image */
Bit16u relocate;
if (flags==OVERLAY) relocate=block->overlay.relocation;
else relocate=loadseg;
pos=head.reloctable;DOS_SeekFile(fhandle,&pos,0);
for (i=0;i<head.relocations;i++) {
readsize=4;DOS_ReadFile(fhandle,(Bit8u *)&relocpt,&readsize);
relocpt=readd((Bit8u *)&relocpt); //Endianize
PhysPt address=PhysMake(RealSeg(relocpt)+loadseg,RealOff(relocpt));
mem_writew(address,mem_readw(address)+relocate);
}
}
DOS_CloseFile(fhandle);
CALLBACK_SCF(false); /* Carry flag cleared for caller if successfull */
if (flags==OVERLAY) return true; /* Everything done for overlays */
RealPt csip,sssp;
if (iscom) {
csip=RealMake(pspseg,0x100);
sssp=RealMake(pspseg,0xfffe);
mem_writew(PhysMake(pspseg,0xfffe),0);
} else {
csip=RealMake(loadseg+head.initCS,head.initIP);
sssp=RealMake(loadseg+head.initSS,head.initSP);
}
if (flags==LOADNGO) {
/* Get Caller's program CS:IP of the stack and set termination address to that */
RealSetVec(0x22,RealMake(mem_readw(SegPhys(ss)+reg_sp+2),mem_readw(SegPhys(ss)+reg_sp)));
SaveRegisters();
DOS_PSP callpsp(dos.psp);
/* Save the SS:SP on the PSP of calling program */
callpsp.SetStack(RealMakeSeg(ss,reg_sp));
/* Switch the psp's and set new DTA */
dos.psp=pspseg;
DOS_PSP newpsp(dos.psp);
dos.dta=newpsp.GetDTA();
/* copy fcbs */
newpsp.SetFCB1(block->exec.fcb1);
newpsp.SetFCB2(block->exec.fcb2);
/* Set the stack for new program */
SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp);
/* Add some flags and CS:IP on the stack for the IRET */
reg_sp-=6;
mem_writew(SegPhys(ss)+reg_sp+0,RealOff(csip));
mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(csip));
mem_writew(SegPhys(ss)+reg_sp+4,0x200);
/* Setup the rest of the registers */
reg_ax=0;
reg_cx=reg_dx=reg_bx=reg_si=reg_di=reg_bp=0;
SegSet16(ds,pspseg);SegSet16(es,pspseg);
return true;
}
return false;
}