DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_execute.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <ctype.h>
00022 #include "dosbox.h"
00023 #include "mem.h"
00024 #include "dos_inc.h"
00025 #include "regs.h"
00026 #include "callback.h"
00027 #include "debug.h"
00028 #include "cpu.h"
00029 
00030 const char * RunningProgram="DOSBOX-X";
00031 
00032 #ifdef _MSC_VER
00033 #pragma pack(1)
00034 #endif
00035 struct EXE_Header {
00036         Bit16u signature;                                       /* EXE Signature MZ or ZM */
00037         Bit16u extrabytes;                                      /* Bytes on the last page */
00038         Bit16u pages;                                           /* Pages in file */
00039         Bit16u relocations;                                     /* Relocations in file */
00040         Bit16u headersize;                                      /* Paragraphs in header */
00041         Bit16u minmemory;                                       /* Minimum amount of memory */
00042         Bit16u maxmemory;                                       /* Maximum amount of memory */
00043         Bit16u initSS;
00044         Bit16u initSP;
00045         Bit16u checksum;
00046         Bit16u initIP;
00047         Bit16u initCS;
00048         Bit16u reloctable;
00049         Bit16u overlay;
00050 } GCC_ATTRIBUTE(packed);
00051 #ifdef _MSC_VER
00052 #pragma pack()
00053 #endif
00054 
00055 #define MAGIC1 0x5a4d
00056 #define MAGIC2 0x4d5a
00057 
00058 unsigned int MAXENV = 32768u;
00059 unsigned int ENV_KEEPFREE = 83;
00060 
00061 //#define MAXENV 65535u                                         /* mainline DOSBOx: original value was 32768u */
00062 //#define ENV_KEEPFREE 1024                                     /* keep unallocated by environment variables (original value mainline DOSBox: 83) */
00063 
00064 #define LOADNGO 0
00065 #define LOAD    1
00066 #define OVERLAY 3
00067 
00068 
00069 
00070 static void SaveRegisters(void) {
00071         reg_sp-=18;
00072         mem_writew(SegPhys(ss)+reg_sp+ 0,reg_ax);
00073         mem_writew(SegPhys(ss)+reg_sp+ 2,reg_cx);
00074         mem_writew(SegPhys(ss)+reg_sp+ 4,reg_dx);
00075         mem_writew(SegPhys(ss)+reg_sp+ 6,reg_bx);
00076         mem_writew(SegPhys(ss)+reg_sp+ 8,reg_si);
00077         mem_writew(SegPhys(ss)+reg_sp+10,reg_di);
00078         mem_writew(SegPhys(ss)+reg_sp+12,reg_bp);
00079         mem_writew(SegPhys(ss)+reg_sp+14,SegValue(ds));
00080         mem_writew(SegPhys(ss)+reg_sp+16,SegValue(es));
00081 }
00082 
00083 static void RestoreRegisters(void) {
00084         reg_ax=mem_readw(SegPhys(ss)+reg_sp+ 0);
00085         reg_cx=mem_readw(SegPhys(ss)+reg_sp+ 2);
00086         reg_dx=mem_readw(SegPhys(ss)+reg_sp+ 4);
00087         reg_bx=mem_readw(SegPhys(ss)+reg_sp+ 6);
00088         reg_si=mem_readw(SegPhys(ss)+reg_sp+ 8);
00089         reg_di=mem_readw(SegPhys(ss)+reg_sp+10);
00090         reg_bp=mem_readw(SegPhys(ss)+reg_sp+12);
00091         SegSet16(ds,mem_readw(SegPhys(ss)+reg_sp+14));
00092         SegSet16(es,mem_readw(SegPhys(ss)+reg_sp+16));
00093         reg_sp+=18;
00094 }
00095 
00096 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00097 void DOS_UpdatePSPName(void) {
00098         DOS_MCB mcb(dos.psp()-1);
00099         static char name[9];
00100         mcb.GetFileName(name);
00101         name[8] = 0;
00102         if (!strlen(name)) strcpy(name,"DOSBOX-X");
00103         for(Bitu i = 0;i < 8;i++) { //Don't put garbage in the title bar. Mac OS X doesn't like it
00104                 if (name[i] == 0) break;
00105                 if ( !isprint(*reinterpret_cast<unsigned char*>(&name[i])) ) name[i] = '?';
00106         }
00107         RunningProgram = name;
00108         GFX_SetTitle(-1,-1,-1,false);
00109 }
00110 
00111 void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode) {
00112 
00113         dos.return_code=exitcode;
00114         dos.return_mode=(tsr)?(Bit8u)RETURN_TSR:(Bit8u)RETURN_EXIT;
00115         
00116         DOS_PSP curpsp(pspseg);
00117         if (pspseg==curpsp.GetParent()) return;
00118         /* Free Files owned by process */
00119         if (!tsr) curpsp.CloseFiles();
00120         
00121         /* Get the termination address */
00122         RealPt old22 = curpsp.GetInt22();
00123         /* Restore vector 22,23,24 */
00124         curpsp.RestoreVectors();
00125         /* Set the parent PSP */
00126         dos.psp(curpsp.GetParent());
00127         DOS_PSP parentpsp(curpsp.GetParent());
00128 
00129         /* Restore the SS:SP to the previous one */
00130         SegSet16(ss,RealSeg(parentpsp.GetStack()));
00131         reg_sp = RealOff(parentpsp.GetStack());         
00132         /* Restore the old CS:IP from int 22h */
00133         RestoreRegisters();
00134         /* Set the CS:IP stored in int 0x22 back on the stack */
00135         mem_writew(SegPhys(ss)+reg_sp+0,RealOff(old22));
00136         mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(old22));
00137         /* set IOPL=3 (Strike Commander), nested task set,
00138            interrupts enabled, test flags cleared */
00139         mem_writew(SegPhys(ss)+reg_sp+4,0x7202);
00140         // Free memory owned by process
00141         if (!tsr) DOS_FreeProcessMemory(pspseg);
00142         DOS_UpdatePSPName();
00143 
00144         if ((!(CPU_AutoDetermineMode>>CPU_AUTODETERMINE_SHIFT)) || (cpu.pmode)) return;
00145 
00146         CPU_AutoDetermineMode>>=CPU_AUTODETERMINE_SHIFT;
00147         if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
00148                 CPU_CycleAutoAdjust=false;
00149                 CPU_CycleLeft=0;
00150                 CPU_Cycles=0;
00151                 CPU_CycleMax=CPU_OldCycleMax;
00152                 GFX_SetTitle((Bit32s)CPU_OldCycleMax,-1,-1,false);
00153         } else {
00154                 GFX_SetTitle(-1,-1,-1,false);
00155         }
00156 #if (C_DYNAMIC_X86) || (C_DYNREC)
00157         if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
00158                 cpudecoder=&CPU_Core_Normal_Run;
00159                 CPU_CycleLeft=0;
00160                 CPU_Cycles=0;
00161         }
00162 #endif
00163 
00164         return;
00165 }
00166 
00167 static bool MakeEnv(const char* name, Bit16u* segment) {
00168         /* If segment to copy environment is 0 copy the caller's environment */
00169         DOS_PSP psp(dos.psp());
00170         PhysPt envread,envwrite;
00171         unsigned int keepfree;
00172         Bit16u envsize=1;
00173         bool parentenv=true;
00174 
00175         /* below 83 bytes, we must not append the mystery 0x01 + program name string */
00176         keepfree = ENV_KEEPFREE;
00177 
00178         if (*segment==0) {
00179                 if (!psp.GetEnvironment()) parentenv=false;                             //environment seg=0
00180                 envread=PhysMake(psp.GetEnvironment(),0);
00181         } else {
00182                 if (!*segment) parentenv=false;                                         //environment seg=0
00183                 envread=PhysMake(*segment,0);
00184         }
00185 
00186         if (parentenv) {
00187                 for (envsize=0; ;envsize++) {
00188                         if (envsize >= (MAXENV - keepfree)) {
00189                                 DOS_SetError(DOSERR_ENVIRONMENT_INVALID);
00190                                 return false;
00191                         }
00192                         if (mem_readw(envread+envsize)==0) break;
00193                 }
00194                 envsize += 2;                                                                   /* account for trailing \0\0 */
00195         }
00196         Bit16u size = long2para(envsize+keepfree);
00197         if (size == 0) size = 1;
00198         if (!DOS_AllocateMemory(segment,&size)) return false;
00199         envwrite=PhysMake(*segment,0);
00200         if (parentenv) {
00201                 MEM_BlockCopy(envwrite,envread,envsize);
00202 //              mem_memcpy(envwrite,envread,envsize);
00203                 envwrite+=envsize;
00204         } else {
00205                 mem_writeb(envwrite++,0);
00206         }
00207 
00208         /* If you look at environment blocks in real DOS, you see \x01 and then the name of your program follow
00209          * the environment block for some reason. If the user set the "keep free" below 83 bytes, then don't
00210          * do it. This might break some games that look for that string, but that's the user's fault for
00211          * setting the option, not ours */
00212         if (keepfree >= 83) {
00213                 mem_writew(envwrite,1);
00214                 envwrite+=2;
00215                 char namebuf[DOS_PATHLENGTH];
00216                 if (DOS_Canonicalize(name,namebuf)) {
00217                         MEM_BlockWrite(envwrite,namebuf,(Bitu)(strlen(namebuf)+1));
00218                         return true;
00219                 } else return false;
00220         }
00221 
00222         return true;
00223 }
00224 
00225 bool DOS_NewPSP(Bit16u segment, Bit16u size) {
00226         DOS_PSP psp(segment);
00227         psp.MakeNew(size);
00228         Bit16u parent_psp_seg=psp.GetParent();
00229         DOS_PSP psp_parent(parent_psp_seg);
00230         psp.CopyFileTable(&psp_parent,false);
00231         // copy command line as well (Kings Quest AGI -cga switch)
00232         psp.SetCommandTail(RealMake(parent_psp_seg,0x80));
00233         return true;
00234 }
00235 
00236 bool DOS_ChildPSP(Bit16u segment, Bit16u size) {
00237         DOS_PSP psp(segment);
00238         psp.MakeNew(size);
00239         Bit16u parent_psp_seg = psp.GetParent();
00240         DOS_PSP psp_parent(parent_psp_seg);
00241         psp.CopyFileTable(&psp_parent,true);
00242         psp.SetCommandTail(RealMake(parent_psp_seg,0x80));
00243         psp.SetFCB1(RealMake(parent_psp_seg,0x5c));
00244         psp.SetFCB2(RealMake(parent_psp_seg,0x6c));
00245         psp.SetEnvironment(psp_parent.GetEnvironment());
00246         psp.SetSize(size);
00247         // push registers in case child PSP is terminated
00248         SaveRegisters();
00249         psp.SetStack(RealMakeSeg(ss,reg_sp));
00250         reg_sp+=18;
00251         return true;
00252 }
00253 
00254 static void SetupPSP(Bit16u pspseg,Bit16u memsize,Bit16u envseg) {
00255         /* Fix the PSP for psp and environment MCB's */
00256         DOS_MCB mcb((Bit16u)(pspseg-1));
00257         mcb.SetPSPSeg(pspseg);
00258         mcb.SetPt((Bit16u)(envseg-1));
00259         mcb.SetPSPSeg(pspseg);
00260 
00261         DOS_PSP psp(pspseg);
00262         psp.MakeNew(memsize);
00263         psp.SetEnvironment(envseg);
00264 
00265         /* Copy file handles */
00266         DOS_PSP oldpsp(dos.psp());
00267         psp.CopyFileTable(&oldpsp,true);
00268 
00269 }
00270 
00271 static void SetupCMDLine(Bit16u pspseg, const DOS_ParamBlock& block) {
00272         DOS_PSP psp(pspseg);
00273         // if cmdtail==0 it will inited as empty in SetCommandTail
00274         psp.SetCommandTail(block.exec.cmdtail);
00275 }
00276 
00277 /* FIXME: This code (or the shell perhaps) isn't very good at returning or
00278  *        printing an error message when it is unable to load and run an
00279  *        executable for whatever reason. Worst offense is that if it can't
00280  *        run an EXE due to lack of memory, it silently returns to the
00281  *        shell without any error message. The least we could do is return
00282  *        an error code so that the INT 21h EXEC call can print an informative
00283  *        error message! --J.C. */
00284 bool DOS_Execute(const char* name, PhysPt block_pt, Bit8u flags) {
00285         EXE_Header head;Bitu i;
00286         Bit16u fhandle;Bit16u len;Bit32u pos;
00287         Bit16u pspseg,envseg,loadseg,memsize=0xffff,readsize;
00288         Bit16u maxsize,maxfree=0xffff;
00289         PhysPt loadaddress;RealPt relocpt;
00290     Bit32u headersize = 0, imagesize = 0;
00291         DOS_ParamBlock block(block_pt);
00292 
00293         block.LoadData();
00294         //Remove the loadhigh flag for the moment!
00295         if(flags&0x80) LOG(LOG_EXEC,LOG_ERROR)("using loadhigh flag!!!!!. dropping it");
00296         flags &= 0x7f;
00297         if (flags!=LOADNGO && flags!=OVERLAY && flags!=LOAD) {
00298                 DOS_SetError(DOSERR_FORMAT_INVALID);
00299                 return false;
00300 //              E_Exit("DOS:Not supported execute mode %d for file %s",flags,name);
00301         }
00302         /* Check for EXE or COM File */
00303         bool iscom=false;
00304         if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) {
00305                 DOS_SetError(DOSERR_FILE_NOT_FOUND);
00306                 return false;
00307         }
00308         len=sizeof(EXE_Header);
00309         if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&len)) {
00310                 DOS_CloseFile(fhandle);
00311                 return false;
00312         }
00313         if (len<sizeof(EXE_Header)) {
00314                 if (len==0) {
00315                         /* Prevent executing zero byte files */
00316                         DOS_SetError(DOSERR_ACCESS_DENIED);
00317                         DOS_CloseFile(fhandle);
00318                         return false;
00319                 }
00320                 /* Otherwise must be a .com file */
00321                 iscom=true;
00322         } else {
00323                 /* Convert the header to correct endian, i hope this works */
00324                 HostPt endian=(HostPt)&head;
00325                 for (i=0;i<sizeof(EXE_Header)/2;i++) {
00326                         *((Bit16u *)endian)=host_readw(endian);
00327                         endian+=2;
00328                 }
00329                 if ((head.signature!=MAGIC1) && (head.signature!=MAGIC2)) iscom=true;
00330                 else {
00331                         if(head.pages & ~0x07ffu) /* 1 MB dos maximum address limit. Fixes TC3 IDE (kippesoep) */
00332                                 LOG(LOG_EXEC,LOG_NORMAL)("Weird header: head.pages > 1 MB");
00333                         head.pages&=0x07ffu;
00334                         headersize = head.headersize*16u;
00335                         imagesize = head.pages*512u-headersize; 
00336                         if (imagesize+headersize<512u) imagesize = 512u-headersize;
00337                 }
00338         }
00339         Bit8u * loadbuf=(Bit8u *)new Bit8u[0x10000u];
00340         if (flags!=OVERLAY) {
00341                 /* Create an environment block */
00342                 envseg=block.exec.envseg;
00343                 if (!MakeEnv(name,&envseg)) {
00344                         DOS_CloseFile(fhandle);
00345                         return false;
00346                 }
00347                 Bit16u minsize;
00348                 /* Get Memory */                
00349                 DOS_AllocateMemory(&pspseg,&maxfree);
00350                 if (iscom) {
00351                         minsize=0x1000;maxsize=0xffff;
00352                         if (machine==MCH_PCJR) {
00353                                 /* try to load file into memory below 96k */ 
00354                                 pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);  
00355                                 Bit16u dataread=0x1800;
00356                                 DOS_ReadFile(fhandle,loadbuf,&dataread);
00357                                 if (dataread<0x1800) maxsize=dataread;
00358                                 if (minsize>maxsize) minsize=maxsize;
00359                         }
00360                 } else {        /* Exe size calculated from header */
00361                         minsize=long2para(imagesize+((unsigned int)head.minmemory<<4u)+256u);
00362                         if (head.maxmemory!=0) maxsize=long2para(imagesize+((unsigned int)head.maxmemory<<4u)+256u);
00363                         else maxsize=0xffffu;
00364 
00365             /* Bugfix: scene.org mirrors/hornet/demos/1991/putrefac.zip Putrefaction !PF.{3}
00366              *         has an EXE header that specifies a maxsize less than minsize, and a
00367              *         initial stack pointer that is only valid if we use the maxsize.
00368              *
00369              *         This allows it to run without the SS:IP out of range error below. */
00370             if (maxsize < minsize) maxsize = minsize;
00371                 }
00372                 if (maxfree<minsize) {
00373                         if (iscom) {
00374                                 /* Reduce minimum of needed memory size to filesize */
00375                                 pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);  
00376                                 Bit16u dataread=0xf800;
00377                                 DOS_ReadFile(fhandle,loadbuf,&dataread);
00378                                 if (dataread<0xf800) minsize=((dataread+0x10)>>4)+0x20;
00379                         }
00380                         if (maxfree<minsize) {
00381                                 DOS_CloseFile(fhandle);
00382                                 DOS_SetError(DOSERR_INSUFFICIENT_MEMORY);
00383                                 DOS_FreeMemory(envseg);
00384                                 return false;
00385                         }
00386                 }
00387                 if (maxfree<maxsize) memsize=maxfree;
00388                 else memsize=maxsize;
00389                 if (!DOS_AllocateMemory(&pspseg,&memsize)) E_Exit("DOS:Exec error in memory");
00390                 if (iscom && (machine==MCH_PCJR) && (pspseg<0x2000)) {
00391                         maxsize=0xffff;
00392                         /* resize to full extent of memory block */
00393                         DOS_ResizeMemory(pspseg,&maxsize);
00394                 }
00395                 loadseg=pspseg+16;
00396                 if (!iscom) {
00397                         /* Check if requested to load program into upper part of allocated memory */
00398                         if ((head.minmemory == 0) && (head.maxmemory == 0)) {
00399                                 loadseg = (pspseg+memsize);
00400                                 loadseg -= (imagesize+0xF)/0x10;
00401                                 if (loadseg < (pspseg+16)) loadseg = pspseg+16;
00402                                 if ((unsigned int)(loadseg+((imagesize+0xF)/0x10)) > (unsigned int)(pspseg+memsize))
00403                                         E_Exit("EXE loading error, unable to load to top of block, nor able to fit into block");
00404                         }
00405                 }
00406         } else loadseg=block.overlay.loadseg;
00407         /* Load the executable */
00408         loadaddress=PhysMake(loadseg,0);
00409 
00410         if (iscom) {    /* COM Load 64k - 256 bytes max */
00411                 /* how big is the COM image? make sure it fits */
00412                 pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_END);
00413                 readsize = pos;
00414                 if (readsize > (0xffff-256)) readsize = 0xffff-256;
00415                 pos += 256; /* plus stack */
00416                 if (pos > (unsigned int)(memsize*0x10)) E_Exit("DOS:Not enough memory for COM executable");
00417 
00418                 pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET);  
00419                 DOS_ReadFile(fhandle,loadbuf,&readsize);
00420                 MEM_BlockWrite(loadaddress,loadbuf,readsize);
00421         } else {        /* EXE Load in 32kb blocks and then relocate */
00422                 if (imagesize > (unsigned int)(memsize*0x10)) E_Exit("DOS:Not enough memory for EXE image");
00423                 pos=headersize;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET); 
00424                 while (imagesize>0x7FFF) {
00425                         readsize=0x8000;DOS_ReadFile(fhandle,loadbuf,&readsize);
00426                         MEM_BlockWrite(loadaddress,loadbuf,readsize);
00427 //                      if (readsize!=0x8000) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header");
00428                         loadaddress+=0x8000;imagesize-=0x8000;
00429                 }
00430                 if (imagesize>0) {
00431                         readsize=(Bit16u)imagesize;DOS_ReadFile(fhandle,loadbuf,&readsize);
00432                         MEM_BlockWrite(loadaddress,loadbuf,readsize);
00433 //                      if (readsize!=imagesize) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header");
00434                 }
00435                 /* Relocate the exe image */
00436                 Bit16u relocate;
00437                 if (flags==OVERLAY) relocate=block.overlay.relocation;
00438                 else relocate=loadseg;
00439                 pos=head.reloctable;DOS_SeekFile(fhandle,&pos,0);
00440                 for (i=0;i<head.relocations;i++) {
00441                         readsize=4;DOS_ReadFile(fhandle,(Bit8u *)&relocpt,&readsize);
00442                         relocpt=host_readd((HostPt)&relocpt);           //Endianize
00443                         PhysPt address=PhysMake(RealSeg(relocpt)+loadseg,RealOff(relocpt));
00444                         mem_writew(address,mem_readw(address)+relocate);
00445                 }
00446         }
00447         delete[] loadbuf;
00448         DOS_CloseFile(fhandle);
00449 
00450         /* Setup a psp */
00451         if (flags!=OVERLAY) {
00452                 // Create psp after closing exe, to avoid dead file handle of exe in copied psp
00453                 SetupPSP(pspseg,memsize,envseg);
00454                 SetupCMDLine(pspseg,block);
00455         }
00456         CALLBACK_SCF(false);            /* Carry flag cleared for caller if successfull */
00457         if (flags==OVERLAY) return true;                        /* Everything done for overlays */
00458         RealPt csip,sssp;
00459         if (iscom) {
00460                 unsigned int stack_sp = 0xfffe;
00461 
00462                 /* On most DOS systems the stack pointer is set to 0xFFFE.
00463                  * Limiting the stack pointer to stay within the memory block
00464                  * enables COM images to run correctly in less than 72KB of RAM.
00465                  * Doing this resolves the random crashes observed with DOSBox's
00466                  * builtin commands when less than 72KB of RAM is assigned.
00467                  *
00468                  * At some point I need to boot MS-DOS/PC-DOS 1.x and 2.x in small
00469                  * amounts of RAM to verify that's what actually happens. --J.C. */
00470                 if (stack_sp > ((memsize*0x10UL)-2))
00471                         stack_sp = (memsize*0x10UL)-2;
00472 
00473                 csip=RealMake(pspseg,0x100);
00474                 sssp=RealMake(pspseg,stack_sp);
00475                 mem_writew(Real2Phys(sssp),0);
00476         } else {
00477                 /* FIXME: I've heard of EXE files with entry points like FFFF:0100 or something (COM images turned EXE if I recall).
00478                  *        Does this check validate those too? */
00479                 csip=RealMake(loadseg+head.initCS,head.initIP);
00480                 sssp=RealMake(loadseg+head.initSS,head.initSP);
00481                 if (sssp >= RealMake(pspseg+memsize,0)) E_Exit("DOS:Initial SS:IP beyond allocated memory block for EXE image");
00482                 if (csip >= RealMake(pspseg+memsize,0)) E_Exit("DOS:Initial CS:IP beyond allocated memory block for EXE image");
00483                 if (head.initSP<4) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC SS:SP=%04x:%04x",head.initSS,head.initSP);
00484         }
00485 
00486 
00487         if ((flags == LOAD) || (flags == LOADNGO)) {
00488                 /* Get Caller's program CS:IP of the stack and set termination address to that */
00489                 RealSetVec(0x22,RealMake(mem_readw(SegPhys(ss)+reg_sp+2),mem_readw(SegPhys(ss)+reg_sp)));
00490                 SaveRegisters();
00491                 DOS_PSP callpsp(dos.psp());
00492                 /* Save the SS:SP on the PSP of calling program */
00493                 callpsp.SetStack(RealMakeSeg(ss,reg_sp));
00494                 /* Switch the psp's and set new DTA */
00495                 dos.psp(pspseg);
00496                 DOS_PSP newpsp(dos.psp());
00497                 dos.dta(RealMake(newpsp.GetSegment(),0x80));
00498                 /* save vectors */
00499                 newpsp.SaveVectors();
00500                 /* copy fcbs */
00501                 newpsp.SetFCB1(block.exec.fcb1);
00502                 newpsp.SetFCB2(block.exec.fcb2);
00503                 
00504                 /* Save the SS:SP on the PSP of new program */
00505                 newpsp.SetStack(RealMakeSeg(ss,reg_sp));
00506 
00507                 /* Setup bx, contains a 0xff in bl and bh if the drive in the fcb is not valid */
00508                 DOS_FCB fcb1(RealSeg(block.exec.fcb1),RealOff(block.exec.fcb1));
00509                 DOS_FCB fcb2(RealSeg(block.exec.fcb2),RealOff(block.exec.fcb2));
00510                 Bit8u d1 = fcb1.GetDrive(); //depends on 0 giving the dos.default drive
00511                 if ( (d1>=DOS_DRIVES) || !Drives[d1] ) reg_bl = 0xFF; else reg_bl = 0;
00512                 Bit8u d2 = fcb2.GetDrive();
00513                 if ( (d2>=DOS_DRIVES) || !Drives[d2] ) reg_bh = 0xFF; else reg_bh = 0;
00514 
00515                 /* Write filename in new program MCB */
00516                 char stripname[8]= { 0 };Bitu index=0;
00517                 while (char chr=*name++) {
00518                         switch (chr) {
00519                         case ':':case '\\':case '/':index=0;break;
00520                         default:if (index<8) stripname[index++]=(char)toupper(chr);
00521                         }
00522                 }
00523                 index=0;
00524                 while (index<8) {
00525                         if (stripname[index]=='.') break;
00526                         if (!stripname[index]) break;   
00527                         index++;
00528                 }
00529                 memset(&stripname[index],0,8-index);
00530                 DOS_MCB pspmcb(dos.psp()-1);
00531                 pspmcb.SetFileName(stripname);
00532                 DOS_UpdatePSPName();
00533         }
00534 
00535         if (flags==LOAD) {
00536                 /* First word on the stack is the value ax should contain on startup */
00537                 real_writew(RealSeg(sssp-2),RealOff(sssp-2),reg_bx);
00538                 /* Write initial CS:IP and SS:SP in param block */
00539                 block.exec.initsssp = sssp-2;
00540                 block.exec.initcsip = csip;
00541                 block.SaveData();
00542                 /* Changed registers */
00543                 reg_sp+=18;
00544                 reg_ax=RealOff(csip);
00545                 reg_bx=memsize;
00546                 reg_dx=0;
00547                 return true;
00548         }
00549 
00550         if (flags==LOADNGO) {
00551                 if ((reg_sp>0xfffe) || (reg_sp<18)) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC");
00552                 /* Set the stack for new program */
00553                 SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp);
00554                 /* Add some flags and CS:IP on the stack for the IRET */
00555                 CPU_Push16(RealSeg(csip));
00556                 CPU_Push16(RealOff(csip));
00557                 /* DOS starts programs with a RETF, so critical flags
00558                  * should not be modified (IOPL in v86 mode);
00559                  * interrupt flag is set explicitly, test flags cleared */
00560                 reg_flags=(reg_flags&(~FMASK_TEST))|FLAG_IF;
00561                 //Jump to retf so that we only need to store cs:ip on the stack
00562                 reg_ip++;
00563                 /* Setup the rest of the registers */
00564                 reg_ax = reg_bx;
00565                 reg_cx=0xff;
00566                 reg_dx=pspseg;
00567                 reg_si=RealOff(csip);
00568                 reg_di=RealOff(sssp);
00569                 reg_bp=0x91c;   /* DOS internal stack begin relict */
00570                 SegSet16(ds,pspseg);SegSet16(es,pspseg);
00571 #if C_DEBUG             
00572                 /* Started from debug.com, then set breakpoint at start */
00573                 DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip));
00574 #endif
00575                 return true;
00576         }
00577         return false;
00578 }