DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_misc.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 #include "callback.h"
00022 #include "mem.h"
00023 #include "regs.h"
00024 #include "dos_inc.h"
00025 #include "control.h"
00026 #include <list>
00027 
00028 Bit32u DOS_HMA_LIMIT();
00029 Bit32u DOS_HMA_FREE_START();
00030 Bit32u DOS_HMA_GET_FREE_SPACE();
00031 void DOS_HMA_CLAIMED(Bit16u bytes);
00032 bool ANSI_SYS_installed();
00033 
00034 extern bool enable_share_exe_fake;
00035 
00036 extern Bitu XMS_EnableA20(bool enable);
00037 
00038 bool enable_a20_on_windows_init = false;
00039 
00040 static Bitu call_int2f,call_int2a;
00041 
00042 static std::list<MultiplexHandler*> Multiplex;
00043 typedef std::list<MultiplexHandler*>::iterator Multiplex_it;
00044 
00045 const char *Win_NameThatVXD(Bit16u devid) {
00046         switch (devid) {
00047                 case 0x0006:    return "V86MMGR";
00048                 case 0x000C:    return "VMD";
00049                 case 0x000D:    return "VKD";
00050                 case 0x0010:    return "BLOCKDEV";
00051                 case 0x0014:    return "VNETBIOS";
00052                 case 0x0015:    return "DOSMGR";
00053                 case 0x0018:    return "VMPOLL";
00054                 case 0x0021:    return "PAGEFILE";
00055                 case 0x002D:    return "W32S";
00056                 case 0x0040:    return "IFSMGR";
00057                 case 0x0446:    return "VADLIBD";
00058                 case 0x0484:    return "IFSMGR";
00059                 case 0x0487:    return "NWSUP";
00060                 case 0x28A1:    return "PHARLAP";
00061                 case 0x7A5F:    return "SIWVID";
00062         }
00063 
00064         return NULL;
00065 }
00066 
00067 void DOS_AddMultiplexHandler(MultiplexHandler * handler) {
00068         Multiplex.push_front(handler);
00069 }
00070 
00071 void DOS_DelMultiplexHandler(MultiplexHandler * handler) {
00072         for(Multiplex_it it =Multiplex.begin();it != Multiplex.end();++it) {
00073                 if(*it == handler) {
00074                         Multiplex.erase(it);
00075                         return;
00076                 }
00077         }
00078 }
00079 
00080 static Bitu INT2F_Handler(void) {
00081         for(Multiplex_it it = Multiplex.begin();it != Multiplex.end();++it)
00082                 if( (*it)() ) return CBRET_NONE;
00083    
00084         LOG(LOG_DOSMISC,LOG_ERROR)("DOS:INT 2F Unhandled call AX=%4X",reg_ax);
00085         return CBRET_NONE;
00086 }
00087 
00088 
00089 static Bitu INT2A_Handler(void) {
00090         return CBRET_NONE;
00091 }
00092 
00093 extern RealPt DOS_DriveDataListHead;       // INT 2Fh AX=0803h DRIVER.SYS drive data table list
00094 
00095 // INT 2F
00096 char regpath[CROSS_LEN+1]="C:\\WINDOWS\\SYSTEM.DAT";
00097 static bool DOS_MultiplexFunctions(void) {
00098     char name[256];
00099         switch (reg_ax) {
00100     case 0x0800:    /* DRIVER.SYS function */
00101     case 0x0801:    /* DRIVER.SYS function */
00102     case 0x0802:    /* DRIVER.SYS function */
00103         LOG(LOG_MISC,LOG_DEBUG)("Unhandled DRIVER.SYS call AX=%04x BX=%04x CX=%04x DX=%04x BP=%04x",reg_ax,reg_bx,reg_cx,reg_dx,reg_bp);
00104         break;
00105     case 0x0803:    /* DRIVER.SYS function */
00106         LOG(LOG_MISC,LOG_DEBUG)("Unhandled DRIVER.SYS call AX=%04x BX=%04x CX=%04x DX=%04x BP=%04x",reg_ax,reg_bx,reg_cx,reg_dx,reg_bp);
00107         // FIXME: Windows 95 SCANDISK.EXE relies on the drive data table list pointer provided by this call.
00108         //        Returning DS:DI unmodified or set to 0:0 will only send it off into the weeds chasing random data
00109         //        as a linked list. However looking at the code DI=0xFFFF is sufficient to prevent that until
00110         //        DOSBox-X emulates DRIVER.SYS functions and provides the information it expects according to RBIL.
00111         //        BUT, Windows 95 setup checks if the pointer is NULL, and considers 0:FFFF valid >_<.
00112         //        It's just easier to return a pointer to a dummy table.
00113         //        [http://www.ctyme.com/intr/rb-4283.htm]
00114         SegSet16(ds,DOS_DriveDataListHead >> 16);
00115         reg_di = DOS_DriveDataListHead;
00116         break;
00117         /* ert, 20100711: Locking extensions */
00118     case 0x1000:    /* SHARE.EXE installation check */
00119         if (enable_share_exe_fake) {
00120             reg_al = 0xff; /* Pretend that share.exe is installed.. Of course it's a bloody LIE! */
00121         }
00122         return true;
00123         case 0x1216:    /* GET ADDRESS OF SYSTEM FILE TABLE ENTRY */
00124                 // reg_bx is a system file table entry, should coincide with
00125                 // the file handle so just use that
00126                 LOG(LOG_DOSMISC,LOG_ERROR)("Some BAD filetable call used bx=%X",reg_bx);
00127                 if(reg_bx <= DOS_FILES) CALLBACK_SCF(false);
00128                 else CALLBACK_SCF(true);
00129                 if (reg_bx<16) {
00130                         RealPt sftrealpt=mem_readd(Real2Phys(dos_infoblock.GetPointer())+4);
00131                         PhysPt sftptr=Real2Phys(sftrealpt);
00132                         Bit32u sftofs=0x06u+reg_bx*0x3bu;
00133 
00134                         if (Files[reg_bx]) mem_writeb(sftptr+sftofs, (Bit8u)(Files[reg_bx]->refCtr));
00135                         else mem_writeb(sftptr+sftofs,0);
00136 
00137                         if (!Files[reg_bx]) return true;
00138 
00139                         Bit32u handle=RealHandle(reg_bx);
00140                         if (handle>=DOS_FILES) {
00141                                 mem_writew(sftptr+sftofs+0x02,0x02);    // file open mode
00142                                 mem_writeb(sftptr+sftofs+0x04,0x00);    // file attribute
00143                                 mem_writew(sftptr+sftofs+0x05,Files[reg_bx]->GetInformation()); // device info word
00144                                 mem_writed(sftptr+sftofs+0x07,0);               // device driver header
00145                                 mem_writew(sftptr+sftofs+0x0d,0);               // packed time
00146                                 mem_writew(sftptr+sftofs+0x0f,0);               // packed date
00147                                 mem_writew(sftptr+sftofs+0x11,0);               // size
00148                                 mem_writew(sftptr+sftofs+0x15,0);               // current position
00149                         } else {
00150                                 Bit8u drive=Files[reg_bx]->GetDrive();
00151 
00152                                 mem_writew(sftptr+sftofs+0x02,(Bit16u)(Files[reg_bx]->flags&3));        // file open mode
00153                                 mem_writeb(sftptr+sftofs+0x04,(Bit8u)(Files[reg_bx]->attr));            // file attribute
00154                                 mem_writew(sftptr+sftofs+0x05,0x40|drive);                                                      // device info word
00155                                 mem_writed(sftptr+sftofs+0x07,RealMake(dos.tables.dpb,drive*dos.tables.dpb_size));      // dpb of the drive
00156                                 mem_writew(sftptr+sftofs+0x0d,Files[reg_bx]->time);                                     // packed file time
00157                                 mem_writew(sftptr+sftofs+0x0f,Files[reg_bx]->date);                                     // packed file date
00158                                 Bit32u curpos=0;
00159                                 Files[reg_bx]->Seek(&curpos,DOS_SEEK_CUR);
00160                                 Bit32u endpos=0;
00161                                 Files[reg_bx]->Seek(&endpos,DOS_SEEK_END);
00162                                 mem_writed(sftptr+sftofs+0x11,endpos);          // size
00163                                 mem_writed(sftptr+sftofs+0x15,curpos);          // current position
00164                                 Files[reg_bx]->Seek(&curpos,DOS_SEEK_SET);
00165                         }
00166 
00167                         // fill in filename in fcb style
00168                         // (space-padded name (8 chars)+space-padded extension (3 chars))
00169                         const char* filename=(const char*)Files[reg_bx]->GetName();
00170                         if (strrchr(filename,'\\')) filename=strrchr(filename,'\\')+1;
00171                         if (strrchr(filename,'/')) filename=strrchr(filename,'/')+1;
00172                         if (!filename) return true;
00173                         const char* dotpos=strrchr(filename,'.');
00174                         if (dotpos) {
00175                                 dotpos++;
00176                                 size_t nlen=strlen(filename);
00177                                 size_t extlen=strlen(dotpos);
00178                                 Bits nmelen=(Bits)nlen-(Bits)extlen;
00179                                 if (nmelen<1) return true;
00180                                 nlen-=(extlen+1);
00181 
00182                                 if (nlen>8) nlen=8;
00183                                 size_t i;
00184 
00185                                 for (i=0; i<nlen; i++)
00186                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)filename[i]);
00187                                 for (i=nlen; i<8; i++)
00188                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)' ');
00189                                 
00190                                 if (extlen>3) extlen=3;
00191                                 for (i=0; i<extlen; i++)
00192                                         mem_writeb((PhysPt)(sftptr+sftofs+0x28u+i),(unsigned char)dotpos[i]);
00193                                 for (i=extlen; i<3; i++)
00194                                         mem_writeb((PhysPt)(sftptr+sftofs+0x28u+i),(unsigned char)' ');
00195                         } else {
00196                                 size_t i;
00197                                 size_t nlen=strlen(filename);
00198                                 if (nlen>8) nlen=8;
00199                                 for (i=0; i<nlen; i++)
00200                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)filename[i]);
00201                                 for (i=nlen; i<11; i++)
00202                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)' ');
00203                         }
00204 
00205                         SegSet16(es,RealSeg(sftrealpt));
00206                         reg_di=RealOff(sftrealpt+sftofs);
00207                         reg_ax=0xc000;
00208 
00209                 }
00210                 return true;
00211     case 0x1300:
00212     case 0x1302:
00213         reg_ax=0;
00214         return true;
00215     case 0x1611:    /* Get shell parameters */
00216                 {
00217                         if (dos.version.major < 7) return false;
00218                         char psp_name[9];
00219                         DOS_MCB psp_mcb(dos.psp()-1);
00220                         psp_mcb.GetFileName(psp_name);
00221                         if (strcmp(psp_name, "DOSSETUP") == 0) {
00222                                 /* Hack for Windows 98 SETUP.EXE (Wengier) */
00223                                 return false;
00224                         }
00225                         strcpy(name,"COMMAND.COM");
00226                         MEM_BlockWrite(SegPhys(ds)+reg_dx,name,(Bitu)(strlen(name)+1));
00227                         strcpy(name+1,"/P /D /K AUTOEXEC");
00228                         name[0]=(char)strlen(name+1);
00229                         MEM_BlockWrite(SegPhys(ds)+reg_si,name,(Bitu)(strlen(name+1)+2));
00230                         reg_ax=0;
00231                         reg_bx=0;
00232                         return true;
00233                 }
00234     case 0x1612:
00235                 if (dos.version.major < 7) return false;
00236         reg_ax=0;
00237         name[0]=1;
00238         name[1]=0;
00239         MEM_BlockWrite(SegPhys(es)+reg_bx,name,0x20);
00240         return true;
00241     case 0x1613:    /* Get SYSTEM.DAT path */
00242                 if (dos.version.major < 7) return false;
00243         strcpy(name,regpath);
00244         MEM_BlockWrite(SegPhys(es)+reg_di,name,(Bitu)(strlen(name)+1));
00245         reg_ax=0;
00246         reg_cx=(Bit16u)strlen(name);
00247         return true;
00248     case 0x1614:    /* Set SYSTEM.DAT path */
00249                 if (dos.version.major < 7) return false;
00250         MEM_StrCopy(SegPhys(es)+reg_di,regpath,CROSS_LEN+1);
00251         reg_ax=0;
00252         return true;
00253     case 0x1600:    /* Windows enhanced mode installation check */
00254         // Leave AX as 0x1600, indicating that neither Windows 3.x enhanced mode, Windows/386 2.x
00255         // nor Windows 95 are running, nor is XMS version 1 driver installed
00256 #ifdef WIN32
00257                 if (!control->SecureMode() && (reg_sp == 0xFFF6 && mem_readw(SegPhys(ss)+reg_sp) == 0x142A || reg_sp >= 0xFF7A && reg_sp <= 0xFF8F && mem_readw(SegPhys(ss)+reg_sp) == reg_sp + 21))
00258                         reg_ax = 0x301;
00259 #endif
00260         return true;
00261         case 0x1605:    /* Windows init broadcast */
00262                 if (enable_a20_on_windows_init) {
00263                         /* This hack exists because Windows 3.1 doesn't seem to enable A20 first during an
00264                          * initial critical period where it assumes it's on, prior to checking and enabling/disabling it.
00265                          *
00266                          * Note that Windows 3.1 also makes this mistake in Standard/286 mode, but it doesn't even
00267                          * make this callout, so this hack is useless unless you are using Enhanced/386 mode.
00268                          * If you want to run Windows 3.1 Standard mode with a20=mask you will have to run builtin
00269                          * command "a20gate on" to turn on the A20 gate prior to starting Windows. */
00270                         LOG_MSG("Enabling A20 gate for Windows in response to INIT broadcast");
00271                         XMS_EnableA20(true);
00272                 }
00273 
00274                 /* TODO: Maybe future parts of DOSBox-X will do something with this */
00275                 /* 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:\ */
00276                 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))",
00277                         SegValue(es),reg_bx,
00278                         SegValue(ds),reg_si,
00279                         reg_cx,reg_dx,reg_di,
00280                         reg_di>>8,reg_di&0xFF);
00281                 if (reg_dx & 0x0001)
00282                         LOG_MSG(" [286 DOS extender]");
00283                 else
00284                         LOG_MSG(" [Enhanced mode]");
00285                 LOG_MSG("\n");
00286 
00287                 /* NTS: The way this protocol works, is that when you (the program hooking this call) receive it,
00288                  *      you first pass the call down to the previous INT 2Fh handler with registers unmodified,
00289                  *      and then when the call unwinds back up the chain, THEN you modify the results to notify
00290                  *      yourself to Windows. So logically, since we're the DOS kernel at the end of the chain,
00291                  *      we should still see ES:BX=0000:0000 and DS:SI=0000:0000 and CX=0000 unmodified from the
00292                  *      way the Windows kernel issued the call. If that's not the case, then we need to issue
00293                  *      a warning because some bastard on the call chain is ruining it for all of us. */
00294                 if (SegValue(es) != 0 || reg_bx != 0 || SegValue(ds) != 0 || reg_si != 0 || reg_cx != 0) {
00295                         LOG_MSG("WARNING: Some registers at this point (the top of the call chain) are nonzero.\n");
00296                         LOG_MSG("         That means a TSR or other entity has modified registers on the way down\n");
00297                         LOG_MSG("         the call chain. The Windows init broadcast is supposed to be handled\n");
00298                         LOG_MSG("         going down the chain by calling the previous INT 2Fh handler with registers\n");
00299                         LOG_MSG("         unmodified, and only modify registers on the way back up the chain!\n");
00300                 }
00301 
00302                 return false; /* pass it on to other INT 2F handlers */
00303         case 0x1606:    /* Windows exit broadcast */
00304                 /* TODO: Maybe future parts of DOSBox-X will do something with this */
00305                 /* 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:\ */
00306                 LOG_MSG("DEBUG: INT 2Fh Windows 286/386 DOSX exit broadcast issued (DX=0x%04x)",reg_dx);
00307                 if (reg_dx & 0x0001)
00308                         LOG_MSG(" [286 DOS extender]");
00309                 else
00310                         LOG_MSG(" [Enhanced mode]");
00311                 LOG_MSG("\n");
00312                 return false; /* pass it on to other INT 2F handlers */
00313         case 0x1607:
00314                 /* 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:\
00315                  *       Additionally, if the user WANTS to see every invocation of the IDLE call, then allow them to enable that too */
00316                 if (reg_bx != 0x18) { /* don't show the idle call. it's used too often */
00317                         const char *str = Win_NameThatVXD(reg_bx);
00318 
00319                         if (str == NULL) str = "??";
00320                         LOG_MSG("DEBUG: INT 2Fh Windows virtual device '%s' callout (BX(deviceID)=0x%04x CX(function)=0x%04x)\n",
00321                                 str,reg_bx,reg_cx);
00322                 }
00323 
00324                 if (reg_bx == 0x15) { /* DOSMGR */
00325                         switch (reg_cx) {
00326                                 case 0x0000:            // query instance
00327                                         reg_cx = 0x0001;
00328                                         reg_dx = 0x50;          // dos driver segment
00329                                         SegSet16(es,0x50);      // patch table seg
00330                                         reg_bx = 0x60;          // patch table ofs
00331                                         return true;
00332                                 case 0x0001:            // set patches
00333                                         reg_ax = 0xb97c;
00334                                         reg_bx = (reg_dx & 0x16);
00335                                         reg_dx = 0xa2ab;
00336                                         return true;
00337                                 case 0x0003:            // get size of data struc
00338                                         if (reg_dx==0x0001) {
00339                                                 // CDS size requested
00340                                                 reg_ax = 0xb97c;
00341                                                 reg_dx = 0xa2ab;
00342                                                 reg_cx = 0x000e;        // size
00343                                         }
00344                                         return true;
00345                                 case 0x0004:            // instanced data
00346                                         reg_dx = 0;             // none
00347                                         return true;
00348                                 case 0x0005:            // get device driver size
00349                                         reg_ax = 0;
00350                                         reg_dx = 0;
00351                                         return true;
00352                                 default:
00353                                         return false;
00354                         }
00355                 }
00356                 else if (reg_bx == 0x18) { /* VMPoll (idle) */
00357                         return true;
00358                 }
00359                 else return false;
00360         case 0x160A:
00361         {
00362                 char psp_name[9];
00363                 DOS_MCB psp_mcb(dos.psp()-1);
00364                 psp_mcb.GetFileName(psp_name);
00365                 // Report Windows version 4.0 (95) to NESTICLE x.xx so that it uses LFN when available
00366                 if (uselfn && (!strcmp(psp_name, "NESTICLE") || reg_sp == 0x220A && mem_readw(SegPhys(ss)+reg_sp)/0x100 == 0x1F)) {
00367                         reg_ax = 0;
00368                         reg_bx = 0x400;
00369                         reg_cx = 2;
00370                         return true;
00371                 }
00372                 return false;
00373         }
00374         case 0x1680:    /*  RELEASE CURRENT VIRTUAL MACHINE TIME-SLICE */
00375                 //TODO Maybe do some idling but could screw up other systems :)
00376                 return true; //So no warning in the debugger anymore
00377         case 0x1689:    /*  Kernel IDLE CALL */
00378         case 0x168f:    /*  Close awareness crap */
00379            /* Removing warning */
00380                 return true;
00381 #ifdef WIN32
00382         case 0x1700:
00383                 if(control->SecureMode()) return false;
00384                 reg_al = 1;
00385                 reg_ah = 1;
00386                 return true;
00387         case 0x1701:
00388                 if(control->SecureMode()) return false;
00389                 reg_ax=0;
00390                 if (OpenClipboard(NULL)) {
00391                         reg_ax=1;
00392                         CloseClipboard();
00393                 }
00394                 return true;
00395         case 0x1702:
00396                 if(control->SecureMode()) return false;
00397                 reg_ax=0;
00398                 if (OpenClipboard(NULL))
00399                         {
00400                         reg_ax=EmptyClipboard()?1:0;
00401                         CloseClipboard();
00402                         }
00403                 return true;
00404         case 0x1703:
00405                 if(control->SecureMode()) return false;
00406                 reg_ax=0;
00407                 if ((reg_dx==1||reg_dx==7)&&OpenClipboard(NULL))
00408                         {
00409                         char *text, *buffer;
00410                         text = new char[reg_cx];
00411                         MEM_StrCopy(SegPhys(es)+reg_bx,text,reg_cx);
00412                         *(text+reg_cx-1)=0;
00413                         HGLOBAL clipbuffer;
00414                         EmptyClipboard();
00415                         clipbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(text)+1);
00416                         buffer = (char*)GlobalLock(clipbuffer);
00417                         strcpy(buffer, text);
00418                         delete[] text;
00419                         GlobalUnlock(clipbuffer);
00420                         SetClipboardData(reg_dx==1?CF_TEXT:CF_OEMTEXT,clipbuffer);
00421                         reg_ax++;
00422                         CloseClipboard();
00423                         }
00424                 return true;
00425         case 0x1704:
00426                 if(control->SecureMode()) return false;
00427                 reg_ax=0;
00428                 if ((reg_dx==1||reg_dx==7)&&OpenClipboard(NULL))
00429                         {
00430                         if (HANDLE text = GetClipboardData(reg_dx==1?CF_TEXT:CF_OEMTEXT))
00431                                 {
00432                                 reg_ax=(Bit16u)strlen((char *)text)+1;
00433                                 reg_dx=(Bit16u)((strlen((char *)text)+1)/65536);
00434                                 }
00435                         else
00436                                 reg_dx=0;
00437                         CloseClipboard();
00438                         }
00439                 return true;
00440         case 0x1705:
00441                 if(control->SecureMode()) return false;
00442                 reg_ax=0;
00443                 if ((reg_dx==1||reg_dx==7)&&OpenClipboard(NULL))
00444                         {
00445                         if (HANDLE text = GetClipboardData(reg_dx==1?CF_TEXT:CF_OEMTEXT))
00446                                 {
00447                                 MEM_BlockWrite(SegPhys(es)+reg_bx,text,(Bitu)(strlen((char *)text)+1));
00448                                 reg_ax++;
00449                                 }
00450                         CloseClipboard();
00451                         }
00452                 return true;
00453         case 0x1708:
00454                 if(control->SecureMode()) return false;
00455                 reg_ax=1;
00456                 CloseClipboard();
00457                 return true;
00458 #endif
00459     case 0x1a00:    /* ANSI.SYS installation check (MS-DOS 4.0 or higher) */
00460         if (IS_PC98_ARCH) {
00461             /* NTS: PC-98 MS-DOS has ANSI handling directly within the kernel HOWEVER it does NOT
00462              *      respond to this INT 2Fh call. */
00463             return true;
00464         }
00465         else if (ANSI_SYS_installed()) {
00466             /* See also: [http://www.delorie.com/djgpp/doc/rbinter/id/71/46.html] */
00467             /* Reported behavior was confirmed with ANSI.SYS loaded on a Windows 95 MS-DOS boot disk, result AX=1AFF */
00468             reg_al = 0xFF; /* DOSBox/DOSBox-X console device emulates ANSI.SYS, so respond like it's installed */
00469             return true;
00470         }
00471         else {
00472             /* MS-DOS without ANSI.SYS loaded doesn't modify any registers in response to this call. */
00473             return true;
00474         }
00475     case 0x4680:    /* Windows v3.0 check */
00476         // Leave AX as 0x4680, indicating that Windows 3.0 is not running in real (/R) or standard (/S) mode,
00477         // nor is DOS 5 DOSSHELL active
00478         return true;
00479         case 0x4a01: {  /* Query free hma space */
00480                 Bit32u limit = DOS_HMA_LIMIT();
00481 
00482                 if (limit == 0) {
00483                         /* TODO: What does MS-DOS prior to v5.0? */
00484                         reg_bx = 0;
00485                         reg_di = 0xFFFF;
00486                         SegSet16(es,0xFFFF);
00487                         LOG(LOG_MISC,LOG_DEBUG)("HMA query: rejected");
00488                         return true;
00489                 }
00490 
00491                 Bit32u start = DOS_HMA_FREE_START();
00492                 reg_bx = limit - start; /* free space in bytes */
00493                 SegSet16(es,0xffff);
00494                 reg_di = (start + 0x10) & 0xFFFF;
00495                 LOG(LOG_MISC,LOG_DEBUG)("HMA query: start=0x%06x limit=0x%06x free=0x%06x -> bx=%u %04x:%04x",
00496                         start,limit,DOS_HMA_GET_FREE_SPACE(),(int)reg_bx,(int)SegValue(es),(int)reg_di);
00497                 } return true;
00498         case 0x4a02: {  /* ALLOCATE HMA SPACE */
00499                 Bit32u limit = DOS_HMA_LIMIT();
00500 
00501                 if (limit == 0) {
00502                         /* TODO: What does MS-DOS prior to v5.0? */
00503                         reg_bx = 0;
00504                         reg_di = 0xFFFF;
00505                         SegSet16(es,0xFFFF);
00506                         LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: rejected");
00507                         return true;
00508                 }
00509 
00510                 /* NTS: According to RBIL, Windows 95 adds a deallocate function and changes HMA allocation up to follow a
00511                  *      MCB chain structure. Which is something we're probably not going to add for awhile. */
00512                 /* FIXME: So, according to Ralph Brown Interrupt List, MS-DOS 5 and 6 liked to round up to the next paragraph? */
00513                 if (dos.version.major < 7 && (reg_bx & 0xF) != 0)
00514                         reg_bx = (reg_bx + 0xF) & (~0xF);
00515 
00516                 Bit32u start = DOS_HMA_FREE_START();
00517                 if ((start+reg_bx) > limit) {
00518                         LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: rejected (not enough room) for %u bytes (0x%x + 0x%x > 0x%x)",reg_bx,
00519                 (unsigned int)start,(unsigned int)reg_bx,(unsigned int)limit);
00520                         reg_bx = 0;
00521                         reg_di = 0xFFFF;
00522                         SegSet16(es,0xFFFF);
00523                         return true;
00524                 }
00525 
00526                 /* convert the start to segment:offset, normalized to FFFF:offset */
00527                 reg_di = (start + 0x10) & 0xFFFF;
00528                 SegSet16(es,0xFFFF);
00529 
00530                 /* let HMA emulation know what was claimed */
00531                 LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: %u bytes at FFFF:%04x",reg_bx,reg_di);
00532                 DOS_HMA_CLAIMED(reg_bx);
00533                 } return true;
00534     case 0x4a10: { /* Microsoft SmartDrive (SMARTDRV) API */
00535         LOG(LOG_MISC,LOG_DEBUG)("Unhandled SMARTDRV call AX=%04x BX=%04x CX=%04x DX=%04x BP=%04x",reg_ax,reg_bx,reg_cx,reg_dx,reg_bp);
00536             } return true;
00537     case 0x4a11: { /* Microsoft DoubleSpace (DBLSPACE.BIN) API */
00538         LOG(LOG_MISC,LOG_DEBUG)("Unhandled DBLSPACE call AX=%04x BX=%04x CX=%04x DX=%04x BP=%04x",reg_ax,reg_bx,reg_cx,reg_dx,reg_bp);
00539             } return true;
00540     case 0x4a16:    /* Open bootlog */
00541         return true;
00542     case 0x4a17:    /* Write bootlog */
00543         MEM_StrCopy(SegPhys(ds)+reg_dx,name,255);
00544         LOG_MSG("BOOTLOG: %s\n",name);
00545         return true;
00546     case 0x4a18:    /* Close bootlog */
00547         return true;
00548         case 0x4a33:    /* Check MS-DOS Version 7 */
00549                 if (dos.version.major > 6) {
00550                         reg_ax=0;
00551                         return true;
00552                 }
00553     }
00554 
00555         return false;
00556 }
00557 
00558 void DOS_SetupMisc(void) {
00559         /* Setup the dos multiplex interrupt */
00560         call_int2f=CALLBACK_Allocate();
00561         CALLBACK_Setup(call_int2f,&INT2F_Handler,CB_IRET,"DOS Int 2f");
00562         RealSetVec(0x2f,CALLBACK_RealPointer(call_int2f));
00563         DOS_AddMultiplexHandler(DOS_MultiplexFunctions);
00564         /* Setup the dos network interrupt */
00565         call_int2a=CALLBACK_Allocate();
00566         CALLBACK_Setup(call_int2a,&INT2A_Handler,CB_IRET,"DOS Int 2a");
00567         RealSetVec(0x2A,CALLBACK_RealPointer(call_int2a));
00568 }
00569 
00570 void CALLBACK_DeAllocate(Bitu in);
00571 
00572 void DOS_UninstallMisc(void) {
00573         /* these vectors shouldn't exist when booting a guest OS */
00574         if (call_int2a) {
00575                 RealSetVec(0x2a,0);
00576                 CALLBACK_DeAllocate(call_int2a);
00577                 call_int2a=0;
00578         }
00579         if (call_int2f) {
00580                 RealSetVec(0x2f,0);
00581                 CALLBACK_DeAllocate(call_int2f);
00582                 call_int2f=0;
00583         }
00584 }
00585