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; }
static bool DOS_MultiplexFunctions(void) { switch (reg_ax) { /* ert, 20100711: Locking extensions */ case 0x1000: /* SHARE.EXE installation check */ if (enable_share_exe_fake) { reg_ax=0xffff; /* Pretend that share.exe is installed.. Of course it's a bloody LIE! */ } else { return false; /* pass it on */ } break; 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 0x1605: /* Windows init broadcast */ if (enable_a20_on_windows_init) { /* This hack exists because Windows 3.1 doesn't seem to enable A20 first during an * initial critical period where it assumes it's on, prior to checking and enabling/disabling it. * * Note that Windows 3.1 also makes this mistake in Standard/286 mode, but it doesn't even * make this callout, so this hack is useless unless you are using Enhanced/386 mode. * If you want to run Windows 3.1 Standard mode with a20=mask you will have to run builtin * command "a20gate on" to turn on the A20 gate prior to starting Windows. */ LOG_MSG("Enabling A20 gate for Windows in response to INIT broadcast"); XMS_EnableA20(true); } /* TODO: Maybe future parts of DOSBox-X will do something with this */ /* TODO: Don't show this by default. Show if the user wants it by a) setting something to "true" in dosbox.conf or b) running a builtin command in Z:\ */ LOG_MSG("DEBUG: INT 2Fh Windows 286/386 DOSX init broadcast issued (ES:BX=%04x:%04x DS:SI=%04x:%04x CX=%04x DX=%04x DI=%04x(aka version %u.%u))", SegValue(es),reg_bx, SegValue(ds),reg_si, reg_cx,reg_dx,reg_di, reg_di>>8,reg_di&0xFF); if (reg_dx & 0x0001) LOG_MSG(" [286 DOS extender]"); else LOG_MSG(" [Enhanced mode]"); LOG_MSG("\n"); /* NTS: The way this protocol works, is that when you (the program hooking this call) receive it, * you first pass the call down to the previous INT 2Fh handler with registers unmodified, * and then when the call unwinds back up the chain, THEN you modify the results to notify * yourself to Windows. So logically, since we're the DOS kernel at the end of the chain, * we should still see ES:BX=0000:0000 and DS:SI=0000:0000 and CX=0000 unmodified from the * way the Windows kernel issued the call. If that's not the case, then we need to issue * a warning because some bastard on the call chain is ruining it for all of us. */ if (SegValue(es) != 0 || reg_bx != 0 || SegValue(ds) != 0 || reg_si != 0 || reg_cx != 0) { LOG_MSG("WARNING: Some registers at this point (the top of the call chain) are nonzero.\n"); LOG_MSG(" That means a TSR or other entity has modified registers on the way down\n"); LOG_MSG(" the call chain. The Windows init broadcast is supposed to be handled\n"); LOG_MSG(" going down the chain by calling the previous INT 2Fh handler with registers\n"); LOG_MSG(" unmodified, and only modify registers on the way back up the chain!\n"); } return false; /* pass it on to other INT 2F handlers */ case 0x1606: /* Windows exit broadcast */ /* TODO: Maybe future parts of DOSBox-X will do something with this */ /* TODO: Don't show this by default. Show if the user wants it by a) setting something to "true" in dosbox.conf or b) running a builtin command in Z:\ */ LOG_MSG("DEBUG: INT 2Fh Windows 286/386 DOSX exit broadcast issued (DX=0x%04x)",reg_dx); if (reg_dx & 0x0001) LOG_MSG(" [286 DOS extender]"); else LOG_MSG(" [Enhanced mode]"); LOG_MSG("\n"); return false; /* pass it on to other INT 2F handlers */ case 0x1607: /* TODO: Don't show this by default. Show if the user wants it by a) setting something to "true" in dosbox.conf or b) running a builtin command in Z:\ * Additionally, if the user WANTS to see every invocation of the IDLE call, then allow them to enable that too */ if (reg_bx != 0x18) { /* don't show the idle call. it's used too often */ const char *str = Win_NameThatVXD(reg_bx); if (str == NULL) str = "??"; LOG_MSG("DEBUG: INT 2Fh Windows virtual device '%s' callout (BX(deviceID)=0x%04x CX(function)=0x%04x)\n", str,reg_bx,reg_cx); } if (reg_bx == 0x15) { /* DOSMGR */ 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) { /* VMPoll (idle) */ return true; } 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 */ Bit32u limit = DOS_HMA_LIMIT(); if (limit == 0) { /* TODO: What does MS-DOS prior to v5.0? */ reg_bx = 0; reg_di = 0xFFFF; SegSet16(es,0xFFFF); LOG(LOG_MISC,LOG_DEBUG)("HMA query: rejected"); return true; } Bit32u start = DOS_HMA_FREE_START(); reg_bx = limit - start; /* free space in bytes */ SegSet16(es,0xffff); reg_di = (start + 0x10) & 0xFFFF; LOG(LOG_MISC,LOG_DEBUG)("HMA query: start=0x%06x limit=0x%06x free=0x%06x -> bx=%u %04x:%04x", start,limit,DOS_HMA_GET_FREE_SPACE(),(int)reg_bx,(int)SegValue(es),(int)reg_di); } return true; case 0x4a02: { /* ALLOCATE HMA SPACE */ Bit32u limit = DOS_HMA_LIMIT(); if (limit == 0) { /* TODO: What does MS-DOS prior to v5.0? */ reg_bx = 0; reg_di = 0xFFFF; SegSet16(es,0xFFFF); LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: rejected"); return true; } /* NTS: According to RBIL, Windows 95 adds a deallocate function and changes HMA allocation up to follow a * MCB chain structure. Which is something we're probably not going to add for awhile. */ /* FIXME: So, according to Ralph Brown Interrupt List, MS-DOS 5 and 6 liked to round up to the next paragraph? */ if (dos.version.major < 7 && (reg_bx & 0xF) != 0) reg_bx = (reg_bx + 0xF) & (~0xF); Bit32u start = DOS_HMA_FREE_START(); if ((start+reg_bx) > limit) { LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: rejected (not enough room) for %u bytes",reg_bx); reg_bx = 0; reg_di = 0xFFFF; SegSet16(es,0xFFFF); return true; } /* convert the start to segment:offset, normalized to FFFF:offset */ reg_di = (start - 0x10) & 0xFFFF; SegSet16(es,0xFFFF); /* let HMA emulation know what was claimed */ LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: %u bytes at FFFF:%04x",reg_bx,reg_di); DOS_HMA_CLAIMED(reg_bx); } return true; } return false; }