DOSBox-X
|
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 }