void CALLBACK_SCF(bool val) { Bit16u tempf = vPC_rLodsw(SegPhys(ss)+reg_sp+4); if (val) tempf |= FLAG_CF; else tempf &= ~FLAG_CF; vPC_rStosw(SegPhys(ss)+reg_sp+4, tempf); }
void CALLBACK_RunRealFar(Bit16u seg,Bit16u off) { reg_sp-=4; mem_writew(SegPhys(ss)+reg_sp,RealOff(CALLBACK_RealPointer(call_stop))); mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(CALLBACK_RealPointer(call_stop))); Bit32u oldeip=reg_eip; Bit16u oldcs=SegValue(cs); reg_eip=off; SegSet16(cs,seg); DOSBOX_RunMachine(); reg_eip=oldeip; SegSet16(cs,oldcs); }
void CALLBACK_RunRealFar(Bit16u seg,Bit16u off) { reg_sp-=4; mem_writew(SegPhys(ss)+reg_sp,call_stop<<4); mem_writew(SegPhys(ss)+reg_sp+2,CB_SEG); Bit32u oldeip=reg_eip; Bit16u oldcs=SegValue(cs); reg_eip=off; SegSet16(cs,seg); DOSBOX_RunMachine(); reg_eip=oldeip; SegSet16(cs,oldcs); }
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+=18; }
static void SaveRegisters(void) { reg_sp-=18; 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)); }
bool DOS_Terminate(bool tsr) { dos.return_code=reg_al; dos.return_mode=RETURN_EXIT; Bit16u mempsp = dos.psp(); DOS_PSP curpsp(mempsp); if (mempsp==curpsp.GetParent()) return true; /* Free Files owned by process */ if (!tsr) curpsp.CloseFiles(); /* Get the termination address */ RealPt old22 = curpsp.GetInt22(); /* Restore vector 22,23,24 */ curpsp.RestoreVectors(); /* Set the parent PSP */ dos.psp(curpsp.GetParent()); DOS_PSP parentpsp(curpsp.GetParent()); /* 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 */ 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)); /* set IOPL=3 (Strike Commander), nested task set, interrupts enabled, test flags cleared */ mem_writew(SegPhys(ss)+reg_sp+4,0x7202); // Free memory owned by process if (!tsr) DOS_FreeProcessMemory(mempsp); DOS_UpdatePSPName(); return true; }
CacheBlockDynRec * LinkBlocks(BlockReturn ret) { CacheBlockDynRec * block=NULL; // the last instruction was a control flow modifying instruction Bitu temp_ip=SegPhys(cs)+reg_eip; CodePageHandlerDynRec * temp_handler=(CodePageHandlerDynRec *)get_tlb_readhandler(temp_ip); if (temp_handler->flags & PFLAG_HASCODE) { // see if the target is an already translated block block=temp_handler->FindCacheBlock(temp_ip & 4095); if (!block) return NULL; // found it, link the current block to cache.block.running->LinkTo(ret==BR_Link2,block); return block; } return NULL; }
static Bitu INT15_Handler(void) { static Bit16u biosConfigSeg = 0; switch (reg_ah) { case 0xC0: // Get Configuration { if (biosConfigSeg == 0) biosConfigSeg = DOS_GetPrivatMemory(1); // We have 16 bytes PhysPt data = SegOff2Ptr(biosConfigSeg, 0); Mem_Stosw(data, 8); // 8 Bytes following Mem_Stosb(data+2, 0xFC); // Model ID (PC) Mem_Stosb(data+3, 0x02); // Submodel ID Mem_Stosb(data+4, 0x74); // Bios Revision Mem_Stosb(data+5, 0x70); // Feature Byte 1 Mem_Stosw(data+6, 0); // Feature Byte 2 + 3 Mem_Stosw(data+8, 0); // Feature Byte 4 + 5 CPU_SetSegGeneral(es, biosConfigSeg); reg_bx = 0; reg_ah = 0; CALLBACK_SCF(false); } break; case 0x87: // Copy extended memory { Bitu bytes = reg_cx * 2; PhysPt data = SegPhys(es)+reg_si; PhysPt source = (Mem_Lodsd(data+0x12) & 0x00FFFFFF) + (Mem_Lodsb(data+0x16)<<24); PhysPt dest = (Mem_Lodsd(data+0x1A) & 0x00FFFFFF) + (Mem_Lodsb(data+0x1E)<<24); Mem_rMovsb(dest, source, bytes); reg_ax = 0; CALLBACK_SCF(false); break; } case 0x88: // SYSTEM - Get extended memory size in KB (286+) reg_ax = TotEXTMB*1024; CALLBACK_SCF(false); break; default: LOG_MSG("Int 15 unhandled call %4X", reg_ax); reg_ah = 0x86; CALLBACK_SCF(true); } return CBRET_NONE; }
bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { EXE_Header head;Bitu i; Bit16u fhandle;Bit16u len;Bit32u pos; Bit16u pspseg,envseg,loadseg,memsize,readsize; PhysPt loadaddress;RealPt relocpt; Bitu headersize,imagesize; DOS_ParamBlock block(block_pt); block.LoadData(); if (flags!=LOADNGO && flags!=OVERLAY && flags!=LOAD) { 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; len=sizeof(EXE_Header); if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&len)) { DOS_CloseFile(fhandle); return false; } if (len<sizeof(EXE_Header)) { if (len==0) { /* Prevent executing zero byte files */ DOS_SetError(DOSERR_ACCESS_DENIED); DOS_CloseFile(fhandle); return false; } /* Otherwise must be a .com file */ iscom=true; } else { /* Convert the header to correct endian, i hope this works */ HostPt endian=(HostPt)&head; for (i=0;i<sizeof(EXE_Header)/2;i++) { *((Bit16u *)endian)=host_readw(endian); endian+=2; } if ((head.signature!=MAGIC1) && (head.signature!=MAGIC2)) iscom=true; else { if(head.pages & ~0x07ff) /* 1 MB dos maximum address limit. Fixes TC3 IDE (kippesoep) */ LOG(LOG_EXEC,LOG_NORMAL)("Weird header: head.pages > 1 MB"); head.pages&=0x07ff; headersize = head.headersize*16; imagesize = head.pages*512-headersize; if (imagesize+headersize<512) imagesize = 512-headersize; } } Bit8u * loadbuf=(Bit8u *)new Bit8u[0x10000]; 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; if (machine==MCH_PCJR) { /* try to load file into memory below 96k */ pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET); Bit16u dataread=0x1800; DOS_ReadFile(fhandle,loadbuf,&dataread); if (dataread<0x1800) maxsize=dataread; if (minsize>maxsize) minsize=maxsize; } } 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"); if (iscom && (machine==MCH_PCJR) && (pspseg<0x2000)) { maxsize=0xffff; /* resize to full extent of memory block */ DOS_ResizeMemory(pspseg,&maxsize); /* now try to lock out memory above segment 0x2000 */ if ((real_readb(0x2000,0)==0x5a) && (real_readw(0x2000,1)==0) && (real_readw(0x2000,3)==0x7ffe)) { /* MCB after PCJr graphics memory region is still free */ if (pspseg+maxsize==0x17ff) { DOS_MCB cmcb((Bit16u)(pspseg-1)); cmcb.SetType(0x5a); // last block } } } loadseg=pspseg+16; if (!iscom) { /* Check if requested to load program into upper part of allocated memory */ if ((head.minmemory == 0) && (head.maxmemory == 0)) loadseg = ((pspseg+memsize)*0x10-imagesize)/0x10; } } else loadseg=block.overlay.loadseg; /* Load the executable */ loadaddress=PhysMake(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,loadbuf,&readsize); MEM_BlockWrite(loadaddress,loadbuf,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,loadbuf,&readsize); MEM_BlockWrite(loadaddress,loadbuf,readsize); // if (readsize!=0x8000) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header"); loadaddress+=0x8000;imagesize-=0x8000; } if (imagesize>0) { readsize=(Bit16u)imagesize;DOS_ReadFile(fhandle,loadbuf,&readsize); MEM_BlockWrite(loadaddress,loadbuf,readsize); // if (readsize!=imagesize) LOG(LOG_EXEC,LOG_NORMAL)("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=host_readd((HostPt)&relocpt); //Endianize PhysPt address=PhysMake(RealSeg(relocpt)+loadseg,RealOff(relocpt)); mem_writew(address,mem_readw(address)+relocate); } } delete[] loadbuf; DOS_CloseFile(fhandle); /* Setup a psp */ if (flags!=OVERLAY) { // Create psp after closing exe, to avoid dead file handle of exe in copied psp SetupPSP(pspseg,memsize,envseg); SetupCMDLine(pspseg,block); }; 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==LOAD) { 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 */ dos.psp(pspseg); DOS_PSP newpsp(dos.psp()); dos.dta(RealMake(newpsp.GetSegment(),0x80)); block.exec.initsssp = sssp; block.exec.initcsip = csip; block.SaveData(); return true; } 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(RealMake(newpsp.GetSegment(),0x80)); /* save vectors */ newpsp.SaveVectors(); /* 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)); /* DOS starts programs with a RETF, so our IRET should not modify critical flags (IOPL in v86 mode); interrupt flag is set explicitly, test flags cleared */ mem_writew(SegPhys(ss)+reg_sp+4,(reg_flags&(~FMASK_TEST))|FLAG_IF); /* Setup the rest of the registers */ reg_ax=reg_bx=0;reg_cx=0xff; reg_dx=pspseg; reg_si=RealOff(csip); reg_di=RealOff(sssp); reg_bp=0x91c; /* DOS internal stack begin relict */ SegSet16(ds,pspseg);SegSet16(es,pspseg); #if C_DEBUG /* Started from debug.com, then set breakpoint at start */ DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip)); #endif /* Add the filename to PSP and environment MCB's */ char stripname[8];Bitu index=0; while (char chr=*name++) { switch (chr) { case ':':case '\\':case '/':index=0;break; default:if (index<8) stripname[index++]=toupper(chr); } } index=0; while (index<8) { if (stripname[index]=='.') break; if (!stripname[index]) break; index++; } memset(&stripname[index],0,8-index); DOS_MCB pspmcb(dos.psp()-1); pspmcb.SetFileName(stripname); DOS_UpdatePSPName(); return true; } return false; }
static Bitu INT33_Handler(void) { // LOG(LOG_MOUSE,LOG_NORMAL)("MOUSE: %04X %X %X %d %d",reg_ax,reg_bx,reg_cx,POS_X,POS_Y); switch (reg_ax) { case 0x00: /* Reset Driver and Read Status */ Mouse_ResetHardware(); /* fallthrough */ case 0x21: /* Software Reset */ reg_ax=0xffff; reg_bx=MOUSE_BUTTONS; Mouse_Reset(); Mouse_AutoLock(true); break; case 0x01: /* Show Mouse */ if(mouse.hidden) mouse.hidden--; Mouse_AutoLock(true); DrawCursor(); break; case 0x02: /* Hide Mouse */ { if (CurMode->type!=M_TEXT) RestoreCursorBackground(); else RestoreCursorBackgroundText(); mouse.hidden++; } break; case 0x03: /* Return position and Button Status */ reg_bx=mouse.buttons; reg_cx=POS_X; reg_dx=POS_Y; break; case 0x04: /* Position Mouse */ /* If position isn't different from current position * don't change it then. (as position is rounded so numbers get * lost when the rounded number is set) (arena/simulation Wolf) */ if ((Bit16s)reg_cx >= mouse.max_x) mouse.x = static_cast<float>(mouse.max_x); else if (mouse.min_x >= (Bit16s)reg_cx) mouse.x = static_cast<float>(mouse.min_x); else if ((Bit16s)reg_cx != POS_X) mouse.x = static_cast<float>(reg_cx); if ((Bit16s)reg_dx >= mouse.max_y) mouse.y = static_cast<float>(mouse.max_y); else if (mouse.min_y >= (Bit16s)reg_dx) mouse.y = static_cast<float>(mouse.min_y); else if ((Bit16s)reg_dx != POS_Y) mouse.y = static_cast<float>(reg_dx); DrawCursor(); break; case 0x05: /* Return Button Press Data */ { Bit16u but=reg_bx; reg_ax=mouse.buttons; if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1; reg_cx=mouse.last_pressed_x[but]; reg_dx=mouse.last_pressed_y[but]; reg_bx=mouse.times_pressed[but]; mouse.times_pressed[but]=0; break; } case 0x06: /* Return Button Release Data */ { Bit16u but=reg_bx; reg_ax=mouse.buttons; if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1; reg_cx=mouse.last_released_x[but]; reg_dx=mouse.last_released_y[but]; reg_bx=mouse.times_released[but]; mouse.times_released[but]=0; break; } case 0x07: /* Define horizontal cursor range */ { //lemmings set 1-640 and wants that. iron seeds set 0-640 but doesn't like 640 //Iron seed works if newvideo mode with mode 13 sets 0-639 //Larry 6 actually wants newvideo mode with mode 13 to set it to 0-319 Bit16s max,min; if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;} else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;} mouse.min_x=min; mouse.max_x=max; /* Battlechess wants this */ if(mouse.x > mouse.max_x) mouse.x = mouse.max_x; if(mouse.x < mouse.min_x) mouse.x = mouse.min_x; /* Or alternatively this: mouse.x = (mouse.max_x - mouse.min_x + 1)/2;*/ LOG(LOG_MOUSE,LOG_NORMAL)("Define Hortizontal range min:%d max:%d",min,max); } break; case 0x08: /* Define vertical cursor range */ { // not sure what to take instead of the CurMode (see case 0x07 as well) // especially the cases where sheight= 400 and we set it with the mouse_reset to 200 //disabled it at the moment. Seems to break syndicate who want 400 in mode 13 Bit16s max,min; if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;} else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;} mouse.min_y=min; mouse.max_y=max; /* Battlechess wants this */ if(mouse.y > mouse.max_y) mouse.y = mouse.max_y; if(mouse.y < mouse.min_y) mouse.y = mouse.min_y; /* Or alternatively this: mouse.y = (mouse.max_y - mouse.min_y + 1)/2;*/ LOG(LOG_MOUSE,LOG_NORMAL)("Define Vertical range min:%d max:%d",min,max); } break; case 0x09: /* Define GFX Cursor */ { PhysPt src = SegPhys(es)+reg_dx; MEM_BlockRead(src ,userdefScreenMask,CURSORY*2); MEM_BlockRead(src+CURSORY*2,userdefCursorMask,CURSORY*2); mouse.screenMask = userdefScreenMask; mouse.cursorMask = userdefCursorMask; mouse.hotx = reg_bx; mouse.hoty = reg_cx; mouse.cursorType = 2; DrawCursor(); } break; case 0x0a: /* Define Text Cursor */ mouse.cursorType = reg_bx; mouse.textAndMask = reg_cx; mouse.textXorMask = reg_dx; break; case 0x0b: /* Read Motion Data */ reg_cx=(Bit16s)(mouse.mickey_x*mouse.mickeysPerPixel_x); reg_dx=(Bit16s)(mouse.mickey_y*mouse.mickeysPerPixel_y); mouse.mickey_x=0; mouse.mickey_y=0; break; case 0x0c: /* Define interrupt subroutine parameters */ mouse.sub_mask=reg_cx; mouse.sub_seg=SegValue(es); mouse.sub_ofs=reg_dx; Mouse_AutoLock(true); //Some games don't seem to reset the mouse before using break; case 0x0f: /* Define mickey/pixel rate */ Mouse_SetMickeyPixelRate(reg_cx,reg_dx); break; case 0x10: /* Define screen region for updating */ mouse.updateRegion_x[0]=reg_cx; mouse.updateRegion_y[0]=reg_dx; mouse.updateRegion_x[1]=reg_si; mouse.updateRegion_y[1]=reg_di; break; case 0x11: /* Get number of buttons */ reg_ax=0xffff; reg_bx=MOUSE_BUTTONS; break; case 0x13: /* Set double-speed threshold */ mouse.doubleSpeedThreshold=(reg_bx ? reg_bx : 64); break; case 0x14: /* Exchange event-handler */ { Bit16u oldSeg = mouse.sub_seg; Bit16u oldOfs = mouse.sub_ofs; Bit16u oldMask= mouse.sub_mask; // Set new values mouse.sub_mask= reg_cx; mouse.sub_seg = SegValue(es); mouse.sub_ofs = reg_dx; // Return old values reg_cx = oldMask; reg_dx = oldOfs; SegSet16(es,oldSeg); } break; case 0x15: /* Get Driver storage space requirements */ reg_bx = sizeof(mouse); break; case 0x16: /* Save driver state */ { LOG(LOG_MOUSE,LOG_WARN)("Saving driver state..."); PhysPt dest = SegPhys(es)+reg_dx; MEM_BlockWrite(dest, &mouse, sizeof(mouse)); } break; case 0x17: /* load driver state */ { LOG(LOG_MOUSE,LOG_WARN)("Loading driver state..."); PhysPt src = SegPhys(es)+reg_dx; MEM_BlockRead(src, &mouse, sizeof(mouse)); } break; case 0x1a: /* Set mouse sensitivity */ // ToDo : double mouse speed value Mouse_SetSensitivity(reg_bx,reg_cx,reg_dx); LOG(LOG_MOUSE,LOG_WARN)("Set sensitivity used with %d %d (%d)",reg_bx,reg_cx,reg_dx); break; case 0x1b: /* Get mouse sensitivity */ reg_bx = mouse.senv_x_val; reg_cx = mouse.senv_y_val; reg_dx = mouse.dspeed_val; LOG(LOG_MOUSE,LOG_WARN)("Get sensitivity %d %d",reg_bx,reg_cx); break; case 0x1c: /* Set interrupt rate */ /* Can't really set a rate this is host determined */ break; case 0x1d: /* Set display page number */ mouse.page=reg_bl; break; case 0x1e: /* Get display page number */ reg_bx=mouse.page; break; case 0x1f: /* Disable Mousedriver */ /* ES:BX old mouse driver Zero at the moment TODO */ reg_bx=0; SegSet16(es,0); mouse.enabled=false; /* Just for reporting not doing a thing with it */ mouse.oldhidden=mouse.hidden; mouse.hidden=1; break; case 0x20: /* Enable Mousedriver */ mouse.enabled=true; mouse.hidden=mouse.oldhidden; break; case 0x22: /* Set language for messages */ /* * Values for mouse driver language: * * 00h English * 01h French * 02h Dutch * 03h German * 04h Swedish * 05h Finnish * 06h Spanish * 07h Portugese * 08h Italian * */ mouse.language=reg_bx; break; case 0x23: /* Get language for messages */ reg_bx=mouse.language; break; case 0x24: /* Get Software version and mouse type */ reg_bx=0x805; //Version 8.05 woohoo reg_ch=0x04; /* PS/2 type */ reg_cl=0; /* PS/2 (unused) */ break; case 0x26: /* Get Maximum virtual coordinates */ reg_bx=(mouse.enabled ? 0x0000 : 0xffff); reg_cx=(Bit16u)mouse.max_x; reg_dx=(Bit16u)mouse.max_y; break; case 0x31: /* Get Current Minimum/Maximum virtual coordinates */ reg_ax=(Bit16u)mouse.min_x; reg_bx=(Bit16u)mouse.min_y; reg_cx=(Bit16u)mouse.max_x; reg_dx=(Bit16u)mouse.max_y; break; default: LOG(LOG_MOUSE,LOG_ERROR)("Mouse Function %04X not implemented!",reg_ax); break; } return CBRET_NONE; }
void CALLBACK_SIF(bool val) { Bit16u tempf = mem_readw(SegPhys(ss)+reg_sp+4); if (val) tempf |= FLAG_IF; else tempf &= ~FLAG_IF; mem_writew(SegPhys(ss)+reg_sp+4,tempf); }
static Bitu DOS_21Handler(void) { if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah<0x6c)) { DOS_PSP psp(dos.psp()); psp.SetStack(RealMake(SegValue(ss),reg_sp-18)); } char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; static Bitu time_start = 0; //For emulating temporary time changes. switch (reg_ah) { case 0x00: /* Terminate Program */ DOS_Terminate(mem_readw(SegPhys(ss)+reg_sp+2),false,0); break; case 0x01: /* Read character from STDIN, with echo */ { Bit8u c;Bit16u n=1; dos.echo=true; DOS_ReadFile(STDIN,&c,&n); reg_al=c; dos.echo=false; } break; case 0x02: /* Write character to STDOUT */ { Bit8u c=reg_dl;Bit16u n=1; DOS_WriteFile(STDOUT,&c,&n); //Not in the official specs, but happens nonetheless. (last written character) reg_al = c;// reg_al=(c==9)?0x20:c; //Officially: tab to spaces } break; case 0x03: /* Read character from STDAUX */ { Bit16u port = real_readw(0x40,0); if(port!=0 && serialports[0]) { Bit8u status; // RTS/DTR on IO_WriteB(port+4,0x3); serialports[0]->Getchar(®_al, &status, true, 0xFFFFFFFF); } } break; case 0x04: /* Write Character to STDAUX */ { Bit16u port = real_readw(0x40,0); if(port!=0 && serialports[0]) { // RTS/DTR on IO_WriteB(port+4,0x3); serialports[0]->Putchar(reg_dl,true,true, 0xFFFFFFFF); // RTS off IO_WriteB(port+4,0x1); } } break; 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 */ { //Simulate DOS overhead for timing sensitive games //MM1 overhead(); //TODO Make this better according to standards if (!DOS_GetSTDINStatus()) { reg_al=0; 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); reg_al = reg_dl; } 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 (checks for breaks officially :)*/ { 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=SegPhys(ds)+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=SegPhys(ds)+reg_dx; Bit8u free=mem_readb(data); Bit8u read=0;Bit8u c;Bit16u n=1; if (!free) break; free--; for(;;) { DOS_ReadFile(STDIN,&c,&n); if (c == 8) { // Backspace if (read) { //Something to backspace. // STDOUT treats backspace as non-destructive. DOS_WriteFile(STDOUT,&c,&n); c = ' '; DOS_WriteFile(STDOUT,&c,&n); c = 8; DOS_WriteFile(STDOUT,&c,&n); --read; } continue; } if (read == free && c != 13) { // Keyboard buffer full Bit8u bell = 7; DOS_WriteFile(STDOUT, &bell, &n); continue; } DOS_WriteFile(STDOUT,&c,&n); mem_writeb(data+read+2,c); if (c==13) break; read++; }; mem_writeb(data+1,read); break; }; case 0x0b: /* Get STDIN Status */ if (!DOS_GetSTDINStatus()) {reg_al=0x00;} else {reg_al=0xFF;} //Simulate some overhead for timing issues //Tankwar menu (needs maybe even more) overhead(); break; case 0x0c: /* Flush Buffer and read STDIN call */ { /* flush buffer if STDIN is CON */ Bit8u handle=RealHandle(STDIN); if (handle!=0xFF && Files[handle] && Files[handle]->IsName("CON")) { Bit8u c;Bit16u n; while (DOS_GetSTDINStatus()) { n=1; DOS_ReadFile(STDIN,&c,&n); } } 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); reg_al=0; 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=DOS_DRIVES; break; case 0x0f: /* Open File using FCB */ if(DOS_FCBOpen(SegValue(ds),reg_dx)){ reg_al=0; }else{ reg_al=0xff; } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x0f FCB-fileopen used, result:al=%d",reg_al); break; case 0x10: /* Close File using FCB */ if(DOS_FCBClose(SegValue(ds),reg_dx)){ reg_al=0; }else{ reg_al=0xff; } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x10 FCB-fileclose used, result:al=%d",reg_al); break; case 0x11: /* Find First Matching File using FCB */ if(DOS_FCBFindFirst(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x11 FCB-FindFirst used, result:al=%d",reg_al); break; case 0x12: /* Find Next Matching File using FCB */ if(DOS_FCBFindNext(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x12 FCB-FindNext used, result:al=%d",reg_al); break; case 0x13: /* Delete File using FCB */ if (DOS_FCBDeleteFile(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Delete used, result:al=%d",reg_al); break; case 0x14: /* Sequential read from FCB */ reg_al = DOS_FCBRead(SegValue(ds),reg_dx,0); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x14 FCB-Read used, result:al=%d",reg_al); break; case 0x15: /* Sequential write to FCB */ reg_al=DOS_FCBWrite(SegValue(ds),reg_dx,0); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x15 FCB-Write used, result:al=%d",reg_al); break; case 0x16: /* Create or truncate file using FCB */ if (DOS_FCBCreate(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Create used, result:al=%d",reg_al); break; case 0x17: /* Rename file using FCB */ if (DOS_FCBRenameFile(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; break; case 0x1b: /* Get allocation info for default drive */ if (!DOS_GetAllocationInfo(0,®_cx,®_al,®_dx)) reg_al=0xff; break; case 0x1c: /* Get allocation info for specific drive */ if (!DOS_GetAllocationInfo(reg_dl,®_cx,®_al,®_dx)) reg_al=0xff; break; case 0x21: /* Read random record from FCB */ { Bit16u toread=1; reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,&toread,true); } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x21 FCB-Random read used, result:al=%d",reg_al); break; case 0x22: /* Write random record to FCB */ { Bit16u towrite=1; reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,&towrite,true); } LOG(LOG_FCB,LOG_NORMAL)("DOS:0x22 FCB-Random write used, result:al=%d",reg_al); break; case 0x23: /* Get file size for FCB */ if (DOS_FCBGetFileSize(SegValue(ds),reg_dx)) reg_al = 0x00; else reg_al = 0xFF; break; case 0x24: /* Set Random Record number for FCB */ DOS_FCBSetRandomRecord(SegValue(ds),reg_dx); break; case 0x27: /* Random block read from FCB */ reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,®_cx,false); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x27 FCB-Random(block) read used, result:al=%d",reg_al); break; case 0x28: /* Random Block write to FCB */ reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,®_cx,false); LOG(LOG_FCB,LOG_NORMAL)("DOS:0x28 FCB-Random(block) write used, result:al=%d",reg_al); break; case 0x29: /* Parse filename into FCB */ { Bit8u difference; char string[1024]; MEM_StrCopy(SegPhys(ds)+reg_si,string,1023); // 1024 toasts the stack reg_al=FCB_Parsename(SegValue(es),reg_di,reg_al ,string, &difference); reg_si+=difference; } LOG(LOG_FCB,LOG_NORMAL)("DOS:29:FCB Parse Filename, result:al=%d",reg_al); break; case 0x19: /* Get current default drive */ reg_al=DOS_GetDefaultDrive(); break; case 0x1a: /* Set Disk Transfer Area Address */ dos.dta(RealMakeSeg(ds,reg_dx)); break; case 0x25: /* Set Interrupt Vector */ RealSetVec(reg_al,RealMakeSeg(ds,reg_dx)); break; case 0x26: /* Create new PSP */ DOS_NewPSP(reg_dx,DOS_PSP(dos.psp()).GetSize()); reg_al=0xf0; /* al destroyed */ break; case 0x2a: /* Get System Date */ { reg_ax=0; // get time CALLBACK_RunRealInt(0x1a); if(reg_al) DOS_AddDays(reg_al); int a = (14 - dos.date.month)/12; int y = dos.date.year - a; int m = dos.date.month + 12*a - 2; reg_al=(dos.date.day+y+(y/4)-(y/100)+(y/400)+(31*m)/12) % 7; reg_cx=dos.date.year; reg_dh=dos.date.month; reg_dl=dos.date.day; } break; case 0x2b: /* Set System Date */ if (reg_cx<1980) { reg_al=0xff;break;} if ((reg_dh>12) || (reg_dh==0)) { reg_al=0xff;break;} if (reg_dl==0) { reg_al=0xff;break;} if (reg_dl>DOS_DATE_months[reg_dh]) { if(!((reg_dh==2)&&(reg_cx%4 == 0)&&(reg_dl==29))) // february pass { 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 */ reg_ax=0; // get time CALLBACK_RunRealInt(0x1a); if(reg_al) DOS_AddDays(reg_al); reg_ah=0x2c; Bitu ticks=((Bitu)reg_cx<<16)|reg_dx; if(time_start<=ticks) ticks-=time_start; Bitu time=(Bitu)((100.0/((double)PIT_TICK_RATE/65536.0)) * (double)ticks); reg_dl=(Bit8u)((Bitu)time % 100); // 1/100 seconds time/=100; reg_dh=(Bit8u)((Bitu)time % 60); // seconds time/=60; reg_cl=(Bit8u)((Bitu)time % 60); // minutes time/=60; reg_ch=(Bit8u)((Bitu)time % 24); // hours //Simulate DOS overhead for timing-sensitive games //Robomaze 2 overhead(); break; } case 0x2d: /* Set System Time */ LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Set System Time not supported"); //Check input parameters nonetheless if( reg_ch > 23 || reg_cl > 59 || reg_dh > 59 || reg_dl > 99 ) reg_al = 0xff; else { //Allow time to be set to zero. Restore the orginal time for all other parameters. (QuickBasic) if (reg_cx == 0 && reg_dx == 0) {time_start = mem_readd(BIOS_TIMER);LOG_MSG("Warning: game messes with DOS time!");} else time_start = 0; reg_al = 0; } break; case 0x2e: /* Set Verify flag */ dos.verify=(reg_al==1); break; case 0x2f: /* Get Disk Transfer Area */ SegSet16(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; /* Serialnumber */ reg_bl=0x00; reg_cx=0x0000; break; case 0x31: /* Terminate and stay resident */ // Important: This service does not set the carry flag! DOS_ResizeMemory(dos.psp(),®_dx); DOS_Terminate(dos.psp(),true,reg_al); break; case 0x1f: /* Get drive parameter block for default drive */ case 0x32: /* Get drive parameter block for specific drive */ { /* Officially a dpb should be returned as well. The disk detection part is implemented */ Bit8u drive=reg_dl; if (!drive || reg_ah==0x1f) drive = DOS_GetDefaultDrive(); else drive--; if (Drives[drive]) { reg_al = 0x00; SegSet16(ds,dos.tables.dpb); reg_bx = drive;//Faking only the first entry (that is the driveletter) LOG(LOG_DOSMISC,LOG_ERROR)("Get drive parameter block."); } else { reg_al=0xff; } } 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 3: /* Get cpsw */ /* Fallthrough */ case 4: /* Set cpsw */ LOG(LOG_DOSMISC,LOG_ERROR)("Someone playing with cpsw %x",reg_ax); break; case 5:reg_dl=3;break;//TODO should be z /* 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: LOG(LOG_DOSMISC,LOG_ERROR)("Weird 0x33 call %2X",reg_al); reg_al =0xff; break; } break; case 0x34: /* Get INDos Flag */ SegSet16(es,DOS_SDA_SEG); reg_bx=DOS_SDA_OFS + 0x01; break; case 0x35: /* Get interrupt vector */ reg_bx=real_readw(0,((Bit16u)reg_al)*4); SegSet16(es,real_readw(0,((Bit16u)reg_al)*4+2)); break; case 0x36: /* Get Free Disk Space */ { Bit16u bytes,clusters,free; Bit8u sectors; if (DOS_GetFreeDiskSpace(reg_dl,&bytes,§ors,&clusters,&free)) { reg_ax=sectors; reg_bx=free; reg_cx=bytes; reg_dx=clusters; } else { Bit8u drive=reg_dl; if (drive==0) drive=DOS_GetDefaultDrive(); else drive--; if (drive<2) { // floppy drive, non-present drivesdisks issue floppy check through int24 // (critical error handler); needed for Mixed up Mother Goose (hook) // CALLBACK_RunRealInt(0x24); } reg_ax=0xffff; // invalid drive specified } } 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(LOG_MISC,LOG_ERROR)("DOS:0x37:Call for not supported switchchar"); break; case 0x38: /* Set Country Code */ if (reg_al==0) { /* Get country specidic information */ PhysPt dest = SegPhys(ds)+reg_dx; MEM_BlockWrite(dest,dos.tables.country,0x18); reg_ax = reg_bx = 0x01; CALLBACK_SCF(false); break; } else { /* Set country code */ LOG(LOG_MISC,LOG_ERROR)("DOS:Setting country code not supported"); } CALLBACK_SCF(true); break; case 0x39: /* MKDIR Create directory */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_MakeDir(name1)) { reg_ax=0x05; /* ax destroyed */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3a: /* RMDIR Remove directory */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_RemoveDir(name1)) { reg_ax=0x05; /* ax destroyed */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode); } break; case 0x3b: /* CHDIR Set current directory */ MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); if (DOS_ChangeDir(name1)) { reg_ax=0x00; /* ax destroyed */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3c: /* CREATE Create of truncate file */ MEM_StrCopy(SegPhys(ds)+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(SegPhys(ds)+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)) { // reg_al=0x01; /* al destroyed. Refcount */ CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } break; case 0x3f: /* READ Read from file or device */ { Bit16u toread=reg_cx; dos.echo=true; if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) { MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread); reg_ax=toread; CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } modify_cycles(reg_ax); dos.echo=false; break; } case 0x40: /* WRITE Write to file or device */ { Bit16u towrite=reg_cx; MEM_BlockRead(SegPhys(ds)+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); } modify_cycles(reg_ax); break; }; case 0x41: /* UNLINK Delete file */ MEM_StrCopy(SegPhys(ds)+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; }
bool DOS_IOCTL(void) { Bitu handle=0;Bit8u drive=0; /* calls 0-4,6,7,10,12,16 use a file handle */ if ((reg_al<4) || (reg_al==0x06) || (reg_al==0x07) || (reg_al==0x0a) || (reg_al==0x0c) || (reg_al==0x10)) { handle=RealHandle(reg_bx); if (handle>=DOS_FILES) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; } if (!Files[handle]) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; } } else if (reg_al<0x12) { /* those use a diskdrive except 0x0b */ if (reg_al!=0x0b) { drive=reg_bl;if (!drive) drive = DOS_GetDefaultDrive();else drive--; if( !(( drive < DOS_DRIVES ) && Drives[drive]) ) { DOS_SetError(DOSERR_INVALID_DRIVE); return false; } } } else { LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } switch(reg_al) { case 0x00: /* Get Device Information */ if (Files[handle]->GetInformation() & 0x8000) { //Check for device reg_dx=Files[handle]->GetInformation(); } else { Bit8u hdrive=Files[handle]->GetDrive(); if (hdrive==0xff) { LOG(LOG_IOCTL,LOG_NORMAL)("00:No drive set"); hdrive=2; // defaulting to C: } /* return drive number in lower 5 bits for block devices */ reg_dx=(Files[handle]->GetInformation()&0xffe0)|hdrive; } reg_ax=reg_dx; //Destroyed officially return true; case 0x01: /* Set Device Information */ if (reg_dh != 0) { DOS_SetError(DOSERR_DATA_INVALID); return false; } else { if (Files[handle]->GetInformation() & 0x8000) { //Check for device reg_al=(Bit8u)(Files[handle]->GetInformation() & 0xff); } else { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } } return true; case 0x02: /* Read from Device Control Channel */ if (Files[handle]->GetInformation() & 0xc000) { /* is character device with IOCTL support */ PhysPt bufptr=PhysMake(SegValue(ds),reg_dx); Bit16u retcode=0; if (((DOS_Device*)(Files[handle]))->ReadFromControlChannel(bufptr,reg_cx,&retcode)) { reg_ax=retcode; return true; } } DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; case 0x03: /* Write to Device Control Channel */ if (Files[handle]->GetInformation() & 0xc000) { /* is character device with IOCTL support */ PhysPt bufptr=PhysMake(SegValue(ds),reg_dx); Bit16u retcode=0; if (((DOS_Device*)(Files[handle]))->WriteToControlChannel(bufptr,reg_cx,&retcode)) { reg_ax=retcode; return true; } } DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; case 0x06: /* Get Input Status */ if (Files[handle]->GetInformation() & 0x8000) { //Check for device reg_al=(Files[handle]->GetInformation() & 0x40) ? 0x0 : 0xff; } else { // FILE Bit32u oldlocation=0; Files[handle]->Seek(&oldlocation, DOS_SEEK_CUR); Bit32u endlocation=0; Files[handle]->Seek(&endlocation, DOS_SEEK_END); if(oldlocation < endlocation){//Still data available reg_al=0xff; } else { reg_al=0x0; //EOF or beyond } Files[handle]->Seek(&oldlocation, DOS_SEEK_SET); //restore filelocation LOG(LOG_IOCTL,LOG_NORMAL)("06:Used Get Input Status on regular file with handle %d",handle); } return true; case 0x07: /* Get Output Status */ LOG(LOG_IOCTL,LOG_NORMAL)("07:Fakes output status is ready for handle %d",handle); reg_al=0xff; return true; case 0x08: /* Check if block device removable */ /* cdrom drives and drive a&b are removable */ if (drive < 2) reg_ax=0; else if (!Drives[drive]->isRemovable()) reg_ax=1; else { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } return true; case 0x09: /* Check if block device remote */ if (Drives[drive]->isRemote()) { reg_dx=0x1000; // device is remote // undocumented bits always clear } else { reg_dx=0x0802; // Open/Close supported; 32bit access supported (any use? fixes Fable installer) // undocumented bits from device attribute word // TODO Set bit 9 on drives that don't support direct I/O } reg_ax=0x300; return true; case 0x0B: /* Set sharing retry count */ if (reg_dx==0) { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } return true; case 0x0D: /* Generic block device request */ { if (Drives[drive]->isRemovable()) { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } PhysPt ptr = SegPhys(ds)+reg_dx; switch (reg_cl) { case 0x60: /* Get Device parameters */ mem_writeb(ptr ,0x03); // special function mem_writeb(ptr+1,(drive>=2)?0x05:0x14); // fixed disc(5), 1.44 floppy(14) mem_writew(ptr+2,drive>=2); // nonremovable ? mem_writew(ptr+4,0x0000); // num of cylinders mem_writeb(ptr+6,0x00); // media type (00=other type) // drive parameter block following mem_writeb(ptr+7,drive); // drive mem_writeb(ptr+8,0x00); // unit number mem_writed(ptr+0x1f,0xffffffff); // next parameter block break; case 0x46: case 0x66: /* Volume label */ { char const* bufin=Drives[drive]->GetLabel(); char buffer[11] ={' '}; char const* find_ext=strchr(bufin,'.'); if (find_ext) { Bitu size=(Bitu)(find_ext-bufin); if (size>8) size=8; memcpy(buffer,bufin,size); find_ext++; memcpy(buffer+size,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); } else { memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin)); } char buf2[8]={ 'F','A','T','1','6',' ',' ',' '}; if(drive<2) buf2[4] = '2'; //FAT12 for floppies mem_writew(ptr+0,0); // 0 mem_writed(ptr+2,0x1234); //Serial number MEM_BlockWrite(ptr+6,buffer,11);//volumename if(reg_cl == 0x66) MEM_BlockWrite(ptr+0x11, buf2,8);//filesystem } break; default : LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled",reg_cl,drive); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } return true; } case 0x0E: /* Get Logical Drive Map */ if (Drives[drive]->isRemovable()) { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } reg_al = 0; /* Only 1 logical drive assigned */ return true; default: LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); break; } return false; }
static Bitu VESA_PMSetPalette(void) { VESA_SetPalette(SegPhys(es) + reg_edi, reg_dx, reg_cx ); return 0; }
Bitu XMS_Handler(void) { // LOG(LOG_MISC,LOG_ERROR)("XMS: CALL %02X",reg_ah); switch (reg_ah) { case XMS_GET_VERSION: /* 00 */ reg_ax=XMS_VERSION; reg_bx=XMS_DRIVER_VERSION; reg_dx=0; /* No we don't have HMA */ break; case XMS_ALLOCATE_HIGH_MEMORY: /* 01 */ reg_ax=0; reg_bl=HIGH_MEMORY_NOT_EXIST; break; case XMS_FREE_HIGH_MEMORY: /* 02 */ reg_ax=0; reg_bl=HIGH_MEMORY_NOT_EXIST; break; case XMS_GLOBAL_ENABLE_A20: /* 03 */ case XMS_LOCAL_ENABLE_A20: /* 05 */ SET_RESULT(XMS_EnableA20(true)); break; case XMS_GLOBAL_DISABLE_A20: /* 04 */ case XMS_LOCAL_DISABLE_A20: /* 06 */ SET_RESULT(XMS_EnableA20(false)); break; case XMS_QUERY_A20: /* 07 */ reg_ax = XMS_GetEnabledA20(); reg_bl = 0; break; case XMS_QUERY_FREE_EXTENDED_MEMORY: /* 08 */ reg_bl = XMS_QueryFreeMemory(reg_ax,reg_dx); break; case XMS_ALLOCATE_ANY_MEMORY: /* 89 */ reg_edx &= 0xffff; // fall through case XMS_ALLOCATE_EXTENDED_MEMORY: /* 09 */ { Bit16u handle = 0; SET_RESULT(XMS_AllocateMemory(reg_dx,handle)); reg_dx = handle; }; break; case XMS_FREE_EXTENDED_MEMORY: /* 0a */ SET_RESULT(XMS_FreeMemory(reg_dx)); break; case XMS_MOVE_EXTENDED_MEMORY_BLOCK: /* 0b */ SET_RESULT(XMS_MoveMemory(SegPhys(ds)+reg_si),false); break; case XMS_LOCK_EXTENDED_MEMORY_BLOCK: { /* 0c */ Bit32u address; Bitu res = XMS_LockMemory(reg_dx, address); if(res) reg_bl = (Bit8u)res; reg_ax = (res==0); if (res==0) { // success reg_bx=(Bit16u)(address & 0xFFFF); reg_dx=(Bit16u)(address >> 16); }; }; break; case XMS_UNLOCK_EXTENDED_MEMORY_BLOCK: /* 0d */ SET_RESULT(XMS_UnlockMemory(reg_dx)); break; case XMS_GET_EMB_HANDLE_INFORMATION: /* 0e */ SET_RESULT(XMS_GetHandleInformation(reg_dx,reg_bh,reg_bl,reg_dx),false); break; case XMS_RESIZE_ANY_EXTENDED_MEMORY_BLOCK: /* 0x8f */ if(reg_ebx > reg_bx) LOG_MSG("64MB memory limit!"); //fall through case XMS_RESIZE_EXTENDED_MEMORY_BLOCK: /* 0f */ SET_RESULT(XMS_ResizeMemory(reg_dx, reg_bx)); break; case XMS_ALLOCATE_UMB: { /* 10 */ if (!umb_available) { reg_ax=0; reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED; break; } Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); if (umb_start==0xffff) { reg_ax=0; reg_bl=UMB_NO_BLOCKS_AVAILABLE; reg_dx=0; // no upper memory available break; } /* Save status and linkage of upper UMB chain and link upper memory to the regular MCB chain */ Bit8u umb_flag=dos_infoblock.GetUMBChainState(); if ((umb_flag&1)==0) DOS_LinkUMBsToMemChain(1); Bit8u old_memstrat=DOS_GetMemAllocStrategy()&0xff; DOS_SetMemAllocStrategy(0x40); // search in UMBs only Bit16u size=reg_dx;Bit16u seg; if (DOS_AllocateMemory(&seg,&size)) { reg_ax=1; reg_bx=seg; } else { reg_ax=0; if (size==0) reg_bl=UMB_NO_BLOCKS_AVAILABLE; else reg_bl=UMB_ONLY_SMALLER_BLOCK; reg_dx=size; // size of largest available UMB } /* Restore status and linkage of upper UMB chain */ Bit8u current_umb_flag=dos_infoblock.GetUMBChainState(); if ((current_umb_flag&1)!=(umb_flag&1)) DOS_LinkUMBsToMemChain(umb_flag); DOS_SetMemAllocStrategy(old_memstrat); } break; case XMS_DEALLOCATE_UMB: /* 11 */ if (!umb_available) { reg_ax=0; reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED; break; } if (dos_infoblock.GetStartOfUMBChain()!=0xffff) { if (DOS_FreeMemory(reg_dx)) { reg_ax=0x0001; break; } } reg_ax=0x0000; reg_bl=UMB_NO_BLOCKS_AVAILABLE; break; case XMS_QUERY_ANY_FREE_MEMORY: /* 88 */ reg_bl = XMS_QueryFreeMemory(reg_ax,reg_dx); reg_eax &= 0xffff; reg_edx &= 0xffff; reg_ecx = (MEM_TotalPages()*MEM_PAGESIZE)-1; // highest known physical memory address break; case XMS_GET_EMB_HANDLE_INFORMATION_EXT: { /* 8e */ Bit8u free_handles; Bitu result = XMS_GetHandleInformation(reg_dx,reg_bh,free_handles,reg_dx); if (result != 0) reg_bl = result; else { reg_edx &= 0xffff; reg_cx = free_handles; } reg_ax = (result==0); } break; default: LOG(LOG_MISC,LOG_ERROR)("XMS: unknown function %02X",reg_ah); reg_ax=0; reg_bl=XMS_FUNCTION_NOT_IMPLEMENTED; }
void CALLBACK_SCF(bool val) { Bit16u tempf=mem_readw(SegPhys(ss)+reg_sp+4) & 0xFFFE; Bit16u newCF=(val==true); mem_writew(SegPhys(ss)+reg_sp+4,(tempf | newCF)); };
void CALLBACK_SZF(bool val) { Bit16u tempf=mem_readw(SegPhys(ss)+reg_sp+4) & 0xFFBF; Bit16u newZF=(val==true) << 6; mem_writew(SegPhys(ss)+reg_sp+4,(tempf | newZF)); };
bool DOS_Shell::Execute(char * name,char * args) { /* return true => don't check for hardware changes in do_command * return false => check for hardware changes in do_command */ char fullname[DOS_PATHLENGTH+4]; //stores results from Which char* p_fullname; char line[CMD_MAXLINE]; if(strlen(args)!= 0){ if(*args != ' '){ //put a space in front line[0]=' ';line[1]=0; strncat(line,args,CMD_MAXLINE-2); line[CMD_MAXLINE-1]=0; } else { safe_strncpy(line,args,CMD_MAXLINE); } }else{ line[0]=0; }; /* check for a drive change */ if (((strcmp(name + 1, ":") == 0) || (strcmp(name + 1, ":\\") == 0)) && isalpha(*name)) { if (!DOS_SetDrive(toupper(name[0])-'A')) { WriteOut(MSG_Get("SHELL_EXECUTE_DRIVE_NOT_FOUND"),toupper(name[0])); } return true; } /* Check for a full name */ p_fullname = Which(name); if (!p_fullname) return false; strcpy(fullname,p_fullname); const char* extension = strrchr(fullname,'.'); __android_log_print(ANDROID_LOG_INFO, "dosbox", "command fullname:%s", fullname); /*always disallow files without extension from being executed. */ /*only internal commands can be run this way and they never get in this handler */ if(extension == 0) { //Check if the result will fit in the parameters. Else abort if(strlen(fullname) >( DOS_PATHLENGTH - 1) ) return false; char temp_name[DOS_PATHLENGTH+4],* temp_fullname; //try to add .com, .exe and .bat extensions to filename strcpy(temp_name,fullname); strcat(temp_name,".COM"); temp_fullname=Which(temp_name); if (temp_fullname) { extension=".com";strcpy(fullname,temp_fullname); } else { strcpy(temp_name,fullname); strcat(temp_name,".EXE"); temp_fullname=Which(temp_name); if (temp_fullname) { extension=".exe";strcpy(fullname,temp_fullname);} else { strcpy(temp_name,fullname); strcat(temp_name,".BAT"); temp_fullname=Which(temp_name); if (temp_fullname) { extension=".bat";strcpy(fullname,temp_fullname);} else { return false; } } } } if (strcasecmp(extension, ".bat") == 0) { /* Run the .bat file */ /* delete old batch file if call is not active*/ bool temp_echo=echo; /*keep the current echostate (as delete bf might change it )*/ if(bf && !call) delete bf; bf=new BatchFile(this,fullname,name,line); echo=temp_echo; //restore it. } else { /* only .bat .exe .com extensions maybe be executed by the shell */ if(strcasecmp(extension, ".com") !=0) { if(strcasecmp(extension, ".exe") !=0) return false; } /* Run the .exe or .com file from the shell */ /* Allocate some stack space for tables in physical memory */ reg_sp-=0x200; //Add Parameter block DOS_ParamBlock block(SegPhys(ss)+reg_sp); block.Clear(); //Add a filename RealPt file_name=RealMakeSeg(ss,reg_sp+0x20); MEM_BlockWrite(Real2Phys(file_name),fullname,(Bitu)(strlen(fullname)+1)); /* HACK: Store full commandline for mount and imgmount */ full_arguments.assign(line); /* Fill the command line */ CommandTail cmdtail; cmdtail.count = 0; memset(&cmdtail.buffer,0,126); //Else some part of the string is unitialized (valgrind) if (strlen(line)>126) line[126]=0; cmdtail.count=(Bit8u)strlen(line); memcpy(cmdtail.buffer,line,strlen(line)); cmdtail.buffer[strlen(line)]=0xd; /* Copy command line in stack block too */ MEM_BlockWrite(SegPhys(ss)+reg_sp+0x100,&cmdtail,128); /* Parse FCB (first two parameters) and put them into the current DOS_PSP */ Bit8u add; FCB_Parsename(dos.psp(),0x5C,0x00,cmdtail.buffer,&add); FCB_Parsename(dos.psp(),0x6C,0x00,&cmdtail.buffer[add],&add); block.exec.fcb1=RealMake(dos.psp(),0x5C); block.exec.fcb2=RealMake(dos.psp(),0x6C); /* Set the command line in the block and save it */ block.exec.cmdtail=RealMakeSeg(ss,reg_sp+0x100); block.SaveData(); #if 0 /* Save CS:IP to some point where i can return them from */ Bit32u oldeip=reg_eip; Bit16u oldcs=SegValue(cs); RealPt newcsip=CALLBACK_RealPointer(call_shellstop); SegSet16(cs,RealSeg(newcsip)); reg_ip=RealOff(newcsip); #endif /* Start up a dos execute interrupt */ reg_ax=0x4b00; //Filename pointer SegSet16(ds,SegValue(ss)); reg_dx=RealOff(file_name); //Paramblock SegSet16(es,SegValue(ss)); reg_bx=reg_sp; SETFLAGBIT(IF,false); CALLBACK_RunRealInt(0x21); /* Restore CS:IP and the stack */ reg_sp+=0x200; #if 0 reg_eip=oldeip; SegSet16(cs,oldcs); #endif } return true; //Executable started }
Bits CPU_Core_Dynrec_Run(void) { for (;;) { // Determine the linear address of CS:EIP PhysPt ip_point=SegPhys(cs)+reg_eip; #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif CodePageHandlerDynRec * chandler=0; // see if the current page is present and contains code if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { // page not present, throw the exception CPU_Exception(cpu.exception.which,cpu.exception.error); continue; } // page doesn't contain code or is special if (GCC_UNLIKELY(!chandler)) return CPU_Core_Normal_Run(); // find correct Dynamic Block to run CacheBlockDynRec * block=chandler->FindCacheBlock(ip_point&4095); if (!block) { // no block found, thus translate the instruction stream // unless the instruction is known to be modified if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) { // translate up to 32 instructions block=CreateCacheBlock(chandler,ip_point,32); } else { // let the normal core handle this instruction to avoid zero-sized blocks Bitu old_cycles=CPU_Cycles; CPU_Cycles=1; Bits nc_retcode=CPU_Core_Normal_Run(); if (!nc_retcode) { CPU_Cycles=old_cycles-1; continue; } CPU_CycleLeft+=old_cycles; return nc_retcode; } } run_block: cache.block.running=0; // now we're ready to run the dynamic code block // BlockReturn ret=((BlockReturn (*)(void))(block->cache.start))(); BlockReturn ret=core_dynrec.runcode(block->cache.start); switch (ret) { case BR_Iret: #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif if (!GETFLAG(TF)) { if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE; break; } // trapflag is set, switch to the trap-aware decoder cpudecoder=CPU_Core_Dynrec_Trap_Run; return CBRET_NONE; case BR_Normal: // the block was exited due to a non-predictable control flow // modifying instruction (like ret) or some nontrivial cpu state // changing instruction (for example switch to/from pmode), // or the maximal number of instructions to translate was reached #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif break; case BR_Cycles: // cycles went negative, return from the core to handle // external events, schedule the pic... #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif return CBRET_NONE; case BR_CallBack: // the callback code is executed in dosbox.conf, return the callback number FillFlags(); return core_dynrec.callback; case BR_SMCBlock: // LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip); cpu.exception.which=0; // fallthrough, let the normal core handle the block-modifying instruction case BR_Opcode: // some instruction has been encountered that could not be translated // (thus it is not part of the code block), the normal core will // handle this instruction CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; return CPU_Core_Normal_Run(); #if (C_DEBUG) case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; return CPU_Core_Full_Run(); #endif case BR_Link1: case BR_Link2: block=LinkBlocks(ret); if (block) goto run_block; break; default: E_Exit("Invalid return code %d", ret); } } return CBRET_NONE; }
bool DOS_IOCTL(void) { Bitu handle = 0; Bit8u drive = 0; // calls 0-4,6,7,10,12,16 use a file handle if ((reg_al < 4) || (reg_al == 0x06) || (reg_al == 0x07) || (reg_al == 0x0a) || (reg_al == 0x0c) || (reg_al == 0x10)) { handle = RealHandle(reg_bx); if (handle >= DOS_FILES || !Files[handle]) { DOS_SetError(DOSERR_INVALID_HANDLE); return false; } } else if (reg_al < 0x12) { // those use a diskdrive except 0x0b if (reg_al != 0x0b) { drive = reg_bl; if (!drive) drive = DOS_GetDefaultDrive(); else drive--; if (!((drive < DOS_DRIVES) && Drives[drive])) { DOS_SetError(DOSERR_INVALID_DRIVE); return false; } } } else { LOG(LOG_DOSMISC, LOG_ERROR)("DOS:IOCTL Call %2X unhandled", reg_al); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } switch (reg_al) { case 0: // Get Device Information if (Files[handle]->GetInformation() & 0x8000) // Check for device reg_dx = Files[handle]->GetInformation(); else { Bit8u hdrive = Files[handle]->GetDrive(); if (hdrive == 0xff) hdrive = 2; // defaulting to C: // return drive number in lower 5 bits for block devices reg_dx = (Files[handle]->GetInformation()&0xffe0)|hdrive; } reg_ax = reg_dx; // Destroyed officially return true; case 1: // Set Device Information if (reg_dh != 0) { DOS_SetError(DOSERR_DATA_INVALID); return false; } if (Files[handle]->GetInformation() & 0x8000) // Check for device reg_al = (Bit8u)(Files[handle]->GetInformation() & 0xff); else { DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } return true; case 2: // Read from Device Control Channel DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; case 3: // Write to Device Control Channel DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; case 6: // Get Input Status if (Files[handle]->GetInformation() & 0x8000) // Check for device reg_al = (Files[handle]->GetInformation() & 0x40) ? 0 : 0xff; else { // FILE Bit32u oldlocation = 0; Files[handle]->Seek(&oldlocation, DOS_SEEK_CUR); Bit32u endlocation = 0; Files[handle]->Seek(&endlocation, DOS_SEEK_END); if (oldlocation < endlocation) reg_al = 0xff; // Still data available else reg_al = 0; //EOF or beyond Files[handle]->Seek(&oldlocation, DOS_SEEK_SET); // restore filelocation } return true; case 7: // Get Output Status reg_al = 0xff; return true; case 8: // Check if block device removable reg_ax = 1; return true; case 9: // Check if block device remote reg_dx = 0x0802; // Open/Close supported; 32bit access supported (any use? fixes Fable installer) // undocumented bits from device attribute word // TODO Set bit 9 on drives that don't support direct I/O reg_ax = 0x300; return true; case 0x0A: // Is Device or Handle Remote? reg_dx = 0; // No! return true; case 0x0B: // Set sharing retry count return true; case 0x0D: // Generic block device request { PhysPt ptr = SegPhys(ds)+reg_dx; switch (reg_cl) { case 0x60: // Get Device parameters vPC_rStosb(ptr, 3); // special function vPC_rStosb(ptr+1, (drive>=2) ? 0x05 : 0x14); // fixed disc(5), 1.44 floppy(14) vPC_rStosw(ptr+2, drive >= 2); // nonremovable ? vPC_rStosw(ptr+4, 0); // num of cylinders vPC_rStosb(ptr+6, 0); // media type (00=other type) // drive parameter block following vPC_rStosb(ptr+7, drive); // drive vPC_rStosb(ptr+8, 0); // unit number vPC_rStosd(ptr+0x1f, 0xffffffff); // next parameter block break; case 0x46: case 0x66: // Volume label { char const* bufin = Drives[drive]->GetLabel(); char buffer[11] ={' '}; char const* find_ext = strchr(bufin,'.'); if (find_ext) { Bitu size = (Bitu)(find_ext-bufin); if (size > 8) size = 8; memcpy(buffer, bufin,size); find_ext++; memcpy(buffer+size, find_ext, (strlen(find_ext)>3) ? 3 : strlen(find_ext)); } else memcpy(buffer,bufin, (strlen(bufin) > 8) ? 8 : strlen(bufin)); char buf2[8]={ 'F','A','T','1','6',' ',' ',' '}; if (drive < 2) buf2[4] = '2'; // FAT12 for floppies vPC_rStosw(ptr+0, 0); // 0 vPC_rStosd(ptr+2, 0x1234); // Serial number vPC_rBlockWrite(ptr+6, buffer,11); // volumename if (reg_cl == 0x66) vPC_rBlockWrite(ptr+0x11, buf2, 8); // filesystem } break; default : LOG(LOG_IOCTL, LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled", reg_cl, drive); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } return true; } case 0x0E: // Get Logical Drive Map reg_al = 0; // Only 1 logical drive assigned return true; default: LOG(LOG_DOSMISC, LOG_ERROR)("DOS:IOCTL Call %2X unhandled", reg_al); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); break; } return false; }
static Bitu DOS_21Handler(void) { if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah < 0x6c)) if (dos.psp() != 0) // No program loaded yet? { DOS_PSP psp(dos.psp()); psp.SetStack(SegOff2dWord(SegValue(ss), reg_sp-18)); psp.FindFreeFileEntry(); } char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; switch (reg_ah) { case 0x00: // Terminate Program DOS_Terminate(Mem_Lodsw(SegPhys(ss)+reg_sp+2), false, 0); break; case 0x01: // Read character from STDIN, with echo { Bit8u c; Bit16u n = 1; dos.echo = true; DOS_ReadFile(STDIN, &c, &n); reg_al = c; dos.echo = false; } break; case 0x02: // Write character to STDOUT { Bit8u c = reg_dl; Bit16u n = 1; DOS_WriteFile(STDOUT, &c, &n); // Not in the official specs, but happens nonetheless. (last written character) reg_al = c; // reg_al=(c==9)?0x20:c; //Officially: tab to spaces } break; case 0x03: // Read character from STDAUX { Bit16u port = Mem_Lodsw(0x40, 0); if (port != 0 && serialPorts[0]) serialPorts[0]->Getchar(®_al); } break; case 0x04: // Write Character to STDAUX { Bit16u port = Mem_Lodsw(0x40, 0); if (port != 0 && serialPorts[0]) serialPorts[0]->Putchar(reg_dl); } break; case 0x05: // Write Character to PRINTER parallelPorts[0]->Putchar(reg_dl); break; case 0x06: // Direct Console Output/Input if (reg_dl == 0xff) // Input { // TODO Make this better according to standards if (!DOS_GetSTDINStatus()) { reg_al = 0; CALLBACK_SZF(true); break; } Bit8u c; Bit16u n = 1; DOS_ReadFile(STDIN, &c, &n); reg_al = c; CALLBACK_SZF(false); } else // Ouput { Bit8u c = reg_dl; Bit16u n = 1; DOS_WriteFile(STDOUT, &c, &n); reg_al = reg_dl; } break; case 0x07: // Character Input, without echo case 0x08: // Direct Character Input, without echo (checks for breaks officially :) { 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 = SegPhys(ds)+reg_dx; while ((c = Mem_Lodsb(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 = SegPhys(ds)+reg_dx; Bit8u free = Mem_Lodsb(data); if (!free) break; Bit8u read = 0; Bit8u c; Bit16u n = 1; for(;;) { DOS_ReadFile(STDIN, &c ,&n); if (c == 8) { // Backspace if (read) { // Something to backspace. DOS_WriteFile(STDOUT, &c, &n); // STDOUT treats backspace as non-destructive. c = ' '; DOS_WriteFile(STDOUT, &c, &n); c = 8; DOS_WriteFile(STDOUT, &c, &n); --read; } continue; } if (read >= free) // Keyboard buffer full { Beep(1750, 300); continue; } DOS_WriteFile(STDOUT, &c, &n); Mem_Stosb(data+read+2, c); if (c == 13) break; read++; } Mem_Stosb(data+1, read); } break; case 0x0b: // Get STDIN Status reg_al = DOS_GetSTDINStatus() ? 0xff : 0x00; break; case 0x0c: // Flush Buffer and read STDIN call { Bit8u handle = RealHandle(STDIN); if (handle != 0xFF && Files[handle] && Files[handle]->IsName("CON")) // flush buffer if STDIN is CON { Bit8u c; Bit16u n; while (DOS_GetSTDINStatus()) { n = 1; DOS_ReadFile(STDIN, &c, &n); } } if (reg_al == 0x1 || reg_al == 0x6 || reg_al == 0x7 || reg_al == 0x8 || reg_al == 0xa) { Bit8u oldah = reg_ah; reg_ah = reg_al; DOS_21Handler(); reg_ah = oldah; } else reg_al = 0; } break; case 0x0d: // Disk Reset break; case 0x0e: // Select Default Drive DOS_SetDefaultDrive(reg_dl); reg_al = DOS_DRIVES; break; case 0x0f: // Open File using FCB reg_al = DOS_FCBOpen(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x10: // Close File using FCB reg_al = DOS_FCBClose(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x11: // Find First Matching File using FCB reg_al = DOS_FCBFindFirst(SegValue(ds), reg_dx) ? 0 : 0xFF; // No test for C:\COMMAND.COM! break; case 0x12: // Find Next Matching File using FCB reg_al = DOS_FCBFindNext(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x13: // Delete File using FCB reg_al = DOS_FCBDeleteFile(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x14: // Sequential read from FCB reg_al = DOS_FCBRead(SegValue(ds), reg_dx, 0); break; case 0x15: // Sequential write to FCB reg_al = DOS_FCBWrite(SegValue(ds), reg_dx, 0); break; case 0x16: // Create or truncate file using FCB reg_al = DOS_FCBCreate(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x17: // Rename file using FCB reg_al = DOS_FCBRenameFile(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x1b: // Get allocation info for default drive if (!DOS_GetAllocationInfo(0, ®_cx, ®_al, ®_dx)) reg_al = 0xFF; break; case 0x1c: // Get allocation info for specific drive if (!DOS_GetAllocationInfo(reg_dl, ®_cx, ®_al, ®_dx)) reg_al = 0xFF; break; case 0x21: // Read random record from FCB reg_al = DOS_FCBRandomRead(SegValue(ds), reg_dx, 1, true); break; case 0x22: // Write random record to FCB reg_al = DOS_FCBRandomWrite(SegValue(ds), reg_dx, 1, true); break; case 0x23: // Get file size for FCB reg_al = DOS_FCBGetFileSize(SegValue(ds), reg_dx) ? 0 : 0xFF; break; case 0x24: // Set Random Record number for FCB DOS_FCBSetRandomRecord(SegValue(ds), reg_dx); break; case 0x27: // Random block read from FCB reg_al = DOS_FCBRandomRead(SegValue(ds), reg_dx, reg_cx, false); break; case 0x28: // Random Block write to FCB reg_al = DOS_FCBRandomWrite(SegValue(ds), reg_dx, reg_cx, false); break; case 0x29: // Parse filename into FCB { Bit8u difference; char string[1024]; Mem_StrnCopyFrom(string, SegPhys(ds)+reg_si, 1023); // 1024 toasts the stack reg_al = FCB_Parsename(SegValue(es), reg_di, reg_al, string, &difference); reg_si += difference; } break; case 0x19: // Get current default drive reg_al = DOS_GetDefaultDrive(); break; case 0x1a: // Set Disk Transfer Area Address dos.dta(RealMakeSeg(ds, reg_dx)); break; case 0x25: // Set Interrupt Vector RealSetVec(reg_al, RealMakeSeg(ds, reg_dx)); break; case 0x26: // Create new PSP DOS_NewPSP(reg_dx, DOS_PSP(dos.psp()).GetSize()); break; case 0x2a: // Get System Date { _SYSTEMTIME systime; // Return the Windows localdate GetLocalTime(&systime); reg_al = (Bit8u)systime.wDayOfWeek; // NB Sunday = 0, despite of MSDN documentation reg_cx = systime.wYear; reg_dx = (systime.wMonth<<8)+systime.wDay; } break; case 0x2b: // Set System Date (we don't!) reg_al = 0xff; daysInMonth[2] = reg_cx&3 ? 28 : 29; // Year is from 1980 to 2099, it is this simple if (reg_cx >= 1980 && reg_cx <= 2099) if (reg_dh > 0 && reg_dh <= 12) if (reg_dl > 0 && reg_dl <= daysInMonth[reg_dh]) reg_al = 0; // Date is valid, fake set break; case 0x2c: // Get System Time { _SYSTEMTIME systime; // Return the Windows localtime GetLocalTime(&systime); reg_cx = (systime.wHour<<8) + systime.wMinute; reg_dx = (systime.wSecond<<8) + systime.wMilliseconds/10; } break; case 0x2d: // Set System Time (we don't!) if (reg_ch < 24 && reg_cl < 60 && reg_dh < 60 && reg_dl < 100) reg_al = 0; // Time is valid, fake set else reg_al = 0xff; break; case 0x2e: // Set Verify flag dos.verify = (reg_al == 1); break; case 0x2f: // Get Disk Transfer Area SegSet16(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 else if (reg_al == 1) reg_bh = 0; // DOS is NOT in HMA reg_al = dos.version.major; reg_ah = dos.version.minor; reg_bl = 0; // Serialnumber reg_cx = 0; break; case 0x31: // Terminate and stay resident // Important: This service does not set the carry flag! DOS_ResizeMemory(dos.psp(), ®_dx); DOS_Terminate(dos.psp(), true, reg_al); break; case 0x1f: // Get drive parameter block for default drive case 0x32: // Get drive parameter block for specific drive { // Officially a dpb should be returned as well. The disk detection part is implemented Bit8u drive = reg_dl; if (!drive || reg_ah == 0x1f) drive = DOS_GetDefaultDrive(); else drive--; if (Drives[drive]) { reg_al = 0; SegSet16(ds, dos.tables.dpb); reg_bx = drive; // Faking only the first entry (that is the driveletter) } else reg_al = 0xff; } break; case 0x33: // Extended Break Checking switch (reg_al) { case 0: // Get the breakcheck flag reg_dl = dos.breakcheck; break; case 1: // Set the breakcheck flag dos.breakcheck = (reg_dl > 0); break; case 2: { bool old = dos.breakcheck; dos.breakcheck = (reg_dl > 0); reg_dl = old; } break; case 3: // Get cpsw case 4: // Set cpsw, both not used really break; case 5: reg_dl = 3; // Boot drive = C: break; case 6: // Get true version number reg_bx = (dos.version.minor<<8) + dos.version.major; reg_dx = 0x1000; // Dos in ROM break; default: reg_al = 0xff; } break; case 0x34: // Get address of INDos Flag SegSet16(es, DOS_SDA_SEG); reg_bx = DOS_SDA_OFS + 0x01; break; case 0x35: // Get interrupt vector reg_bx = Mem_Lodsw(0, ((Bit16u)reg_al)*4); SegSet16(es, Mem_Lodsw(0, ((Bit16u)reg_al)*4+2)); break; case 0x36: // Get Free Disk Space { Bit16u bytes, clusters, free; Bit8u sectors; 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; // invalid drive specified } break; case 0x37: // Get/Set Switch char Get/Set Availdev thing switch (reg_al) { case 0: reg_dl = 0x2f; // Always return '/' like dos 5.0+ break; case 1: reg_al = 0; break; case 2: reg_al = 0; reg_dl = 0x2f; break; case 3: reg_al = 0; break; } break; case 0x38: // Get/set Country Code if (reg_al == 0) // Get country specidic information { PhysPt dest = SegPhys(ds)+reg_dx; Mem_CopyTo(dest, dos.tables.country, 0x18); reg_ax = reg_bx = 1; CALLBACK_SCF(false); } else // Set country code CALLBACK_SCF(true); break; case 0x39: // MKDIR Create directory Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_MakeDir(name1)) { reg_ax = 0xffff; // AX destroyed CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3a: // RMDIR Remove directory Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_RemoveDir(name1)) { reg_ax = 0xffff; // AX destroyed CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3b: // CHDIR Set current directory Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); if (DOS_ChangeDir(name1)) { reg_ax = 0xff00; // AX destroyed CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } break; case 0x3c: // CREATE Create or truncate file Mem_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); 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_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); 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; dos.echo = true; if (DOS_ReadFile(reg_bx, dos_copybuf, &toread)) { Mem_CopyTo(SegPhys(ds)+reg_dx, dos_copybuf, toread); reg_ax = toread; CALLBACK_SCF(false); } else { reg_ax = dos.errorcode; CALLBACK_SCF(true); } dos.echo = false; } break; case 0x40: // WRITE Write to file or device { Bit16u towrite = reg_cx; Mem_CopyFrom(SegPhys(ds)+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_StrnCopyFrom(name1, SegPhys(ds)+reg_dx, DOSNAMEBUF); rSpTrim(name1); 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); } }
static bool DOS_MultiplexFunctions(void) { char name[256]; switch (reg_ax) { case 0x1216: /* GET ADDRESS OF SYSTEM FILE TABLE ENTRY */ // reg_bx is a system file table entry, should coincide with // the file handle so just use that LOG(LOG_DOSMISC,LOG_ERROR)("Some BAD filetable call used bx=%X",reg_bx); if(reg_bx <= DOS_FILES) CALLBACK_SCF(false); else CALLBACK_SCF(true); if (reg_bx<16) { RealPt sftrealpt=mem_readd(Real2Phys(dos_infoblock.GetPointer())+4); PhysPt sftptr=Real2Phys(sftrealpt); Bitu sftofs=0x06+reg_bx*0x3b; if (Files[reg_bx]) mem_writeb(sftptr+sftofs,Files[reg_bx]->refCtr); else mem_writeb(sftptr+sftofs,0); if (!Files[reg_bx]) return true; Bit32u handle=RealHandle(reg_bx); if (handle>=DOS_FILES) { mem_writew(sftptr+sftofs+0x02,0x02); // file open mode mem_writeb(sftptr+sftofs+0x04,0x00); // file attribute mem_writew(sftptr+sftofs+0x05,Files[reg_bx]->GetInformation()); // device info word mem_writed(sftptr+sftofs+0x07,0); // device driver header mem_writew(sftptr+sftofs+0x0d,0); // packed time mem_writew(sftptr+sftofs+0x0f,0); // packed date mem_writew(sftptr+sftofs+0x11,0); // size mem_writew(sftptr+sftofs+0x15,0); // current position } else { Bit8u drive=Files[reg_bx]->GetDrive(); mem_writew(sftptr+sftofs+0x02,(Bit16u)(Files[reg_bx]->flags&3)); // file open mode mem_writeb(sftptr+sftofs+0x04,(Bit8u)(Files[reg_bx]->attr)); // file attribute mem_writew(sftptr+sftofs+0x05,0x40|drive); // device info word mem_writed(sftptr+sftofs+0x07,RealMake(dos.tables.dpb,drive)); // dpb of the drive mem_writew(sftptr+sftofs+0x0d,Files[reg_bx]->time); // packed file time mem_writew(sftptr+sftofs+0x0f,Files[reg_bx]->date); // packed file date Bit32u curpos=0; Files[reg_bx]->Seek(&curpos,DOS_SEEK_CUR); Bit32u endpos=0; Files[reg_bx]->Seek(&endpos,DOS_SEEK_END); mem_writed(sftptr+sftofs+0x11,endpos); // size mem_writed(sftptr+sftofs+0x15,curpos); // current position Files[reg_bx]->Seek(&curpos,DOS_SEEK_SET); } // fill in filename in fcb style // (space-padded name (8 chars)+space-padded extension (3 chars)) const char* filename=(const char*)Files[reg_bx]->GetName(); if (strrchr(filename,'\\')) filename=strrchr(filename,'\\')+1; if (strrchr(filename,'/')) filename=strrchr(filename,'/')+1; if (!filename) return true; const char* dotpos=strrchr(filename,'.'); if (dotpos) { dotpos++; size_t nlen=strlen(filename); size_t extlen=strlen(dotpos); Bits nmelen=(Bits)nlen-(Bits)extlen; if (nmelen<1) return true; nlen-=(extlen+1); if (nlen>8) nlen=8; size_t i; for (i=0; i<nlen; i++) mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),filename[i]); for (i=nlen; i<8; i++) mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),' '); if (extlen>3) extlen=3; for (i=0; i<extlen; i++) mem_writeb((PhysPt)(sftptr+sftofs+0x28+i),dotpos[i]); for (i=extlen; i<3; i++) mem_writeb((PhysPt)(sftptr+sftofs+0x28+i),' '); } else { size_t i; size_t nlen=strlen(filename); if (nlen>8) nlen=8; for (i=0; i<nlen; i++) mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),filename[i]); for (i=nlen; i<11; i++) mem_writeb((PhysPt)(sftptr+sftofs+0x20+i),' '); } SegSet16(es,RealSeg(sftrealpt)); reg_di=RealOff(sftrealpt+sftofs); reg_ax=0xc000; } return true; case 0x1607: if (reg_bx == 0x15) { switch (reg_cx) { case 0x0000: // query instance reg_cx = 0x0001; reg_dx = 0x50; // dos driver segment SegSet16(es,0x50); // patch table seg reg_bx = 0x60; // patch table ofs return true; case 0x0001: // set patches reg_ax = 0xb97c; reg_bx = (reg_dx & 0x16); reg_dx = 0xa2ab; return true; case 0x0003: // get size of data struc if (reg_dx==0x0001) { // CDS size requested reg_ax = 0xb97c; reg_dx = 0xa2ab; reg_cx = 0x000e; // size } return true; case 0x0004: // instanced data reg_dx = 0; // none return true; case 0x0005: // get device driver size reg_ax = 0; reg_dx = 0; return true; default: return false; } } else if (reg_bx == 0x18) return true; // idle callout else return false; case 0x1680: /* RELEASE CURRENT VIRTUAL MACHINE TIME-SLICE */ //TODO Maybe do some idling but could screw up other systems :) return true; //So no warning in the debugger anymore case 0x1689: /* Kernel IDLE CALL */ case 0x168f: /* Close awareness crap */ /* Removing warning */ return true; case 0x4a01: /* Query free hma space */ case 0x4a02: /* ALLOCATE HMA SPACE */ LOG(LOG_DOSMISC,LOG_WARN)("INT 2f:4a HMA. DOSBox reports none available."); reg_bx=0; //number of bytes available in HMA or amount successfully allocated //ESDI=ffff:ffff Location of HMA/Allocated memory SegSet16(es,0xffff); reg_di=0xffff; return true; case 0x1300: case 0x1302: reg_ax=0; return true; case 0x1605: return true; case 0x1612: reg_ax=0; name[0]=1; name[1]=0; MEM_BlockWrite(SegPhys(es)+reg_bx,name,0x20); return true; case 0x1613: /* Get SYSTEM.DAT path */ strcpy(name,"C:\\WINDOWS\\SYSTEM.DAT"); MEM_BlockWrite(SegPhys(es)+reg_di,name,(Bitu)(strlen(name)+1)); reg_ax=0; reg_cx=strlen(name); return true; case 0x4a16: /* Open bootlog */ return true; case 0x4a17: /* Write bootlog */ MEM_StrCopy(SegPhys(ds)+reg_dx,name,255); LOG(LOG_DOSMISC,LOG_NORMAL)("BOOTLOG: %s\n",name); return true; case 0x4a33: /* Check MS-DOS Version 7 */ reg_ax=0; return true; } return false; }