DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/dos/dos_misc.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <list>
00026 
00027 Bit32u DOS_HMA_LIMIT();
00028 Bit32u DOS_HMA_FREE_START();
00029 Bit32u DOS_HMA_GET_FREE_SPACE();
00030 void DOS_HMA_CLAIMED(Bitu bytes);
00031 
00032 extern bool enable_share_exe_fake;
00033 
00034 extern Bitu XMS_EnableA20(bool enable);
00035 
00036 bool enable_a20_on_windows_init = false;
00037 
00038 static Bitu call_int2f,call_int2a;
00039 
00040 static std::list<MultiplexHandler*> Multiplex;
00041 typedef std::list<MultiplexHandler*>::iterator Multiplex_it;
00042 
00043 const char *Win_NameThatVXD(Bit16u devid) {
00044         switch (devid) {
00045                 case 0x0006:    return "V86MMGR";
00046                 case 0x000C:    return "VMD";
00047                 case 0x000D:    return "VKD";
00048                 case 0x0010:    return "BLOCKDEV";
00049                 case 0x0014:    return "VNETBIOS";
00050                 case 0x0015:    return "DOSMGR";
00051                 case 0x0018:    return "VMPOLL";
00052                 case 0x0021:    return "PAGEFILE";
00053                 case 0x002D:    return "W32S";
00054                 case 0x0040:    return "IFSMGR";
00055                 case 0x0446:    return "VADLIBD";
00056                 case 0x0484:    return "IFSMGR";
00057                 case 0x0487:    return "NWSUP";
00058                 case 0x28A1:    return "PHARLAP";
00059                 case 0x7A5F:    return "SIWVID";
00060         };
00061 
00062         return NULL;
00063 }
00064 
00065 void DOS_AddMultiplexHandler(MultiplexHandler * handler) {
00066         Multiplex.push_front(handler);
00067 }
00068 
00069 void DOS_DelMultiplexHandler(MultiplexHandler * handler) {
00070         for(Multiplex_it it =Multiplex.begin();it != Multiplex.end();it++) {
00071                 if(*it == handler) {
00072                         Multiplex.erase(it);
00073                         return;
00074                 }
00075         }
00076 }
00077 
00078 static Bitu INT2F_Handler(void) {
00079         for(Multiplex_it it = Multiplex.begin();it != Multiplex.end();it++)
00080                 if( (*it)() ) return CBRET_NONE;
00081    
00082         LOG(LOG_DOSMISC,LOG_ERROR)("DOS:INT 2F Unhandled call AX=%4X",reg_ax);
00083         return CBRET_NONE;
00084 }
00085 
00086 
00087 static Bitu INT2A_Handler(void) {
00088         return CBRET_NONE;
00089 }
00090 
00091 // INT 2F
00092 static bool DOS_MultiplexFunctions(void) {
00093         switch (reg_ax) {
00094         /* ert, 20100711: Locking extensions */
00095         case 0x1000:    /* SHARE.EXE installation check */
00096                 if (enable_share_exe_fake) {
00097                         reg_ax=0xffff; /* Pretend that share.exe is installed.. Of course it's a bloody LIE! */
00098                 }
00099                 else {
00100                         return false; /* pass it on */
00101                 }
00102                 break;
00103         case 0x1216:    /* GET ADDRESS OF SYSTEM FILE TABLE ENTRY */
00104                 // reg_bx is a system file table entry, should coincide with
00105                 // the file handle so just use that
00106                 LOG(LOG_DOSMISC,LOG_ERROR)("Some BAD filetable call used bx=%X",reg_bx);
00107                 if(reg_bx <= DOS_FILES) CALLBACK_SCF(false);
00108                 else CALLBACK_SCF(true);
00109                 if (reg_bx<16) {
00110                         RealPt sftrealpt=mem_readd(Real2Phys(dos_infoblock.GetPointer())+4);
00111                         PhysPt sftptr=Real2Phys(sftrealpt);
00112                         Bitu sftofs=0x06u+reg_bx*0x3bu;
00113 
00114                         if (Files[reg_bx]) mem_writeb(sftptr+sftofs,Files[reg_bx]->refCtr);
00115                         else mem_writeb(sftptr+sftofs,0);
00116 
00117                         if (!Files[reg_bx]) return true;
00118 
00119                         Bit32u handle=RealHandle(reg_bx);
00120                         if (handle>=DOS_FILES) {
00121                                 mem_writew(sftptr+sftofs+0x02,0x02);    // file open mode
00122                                 mem_writeb(sftptr+sftofs+0x04,0x00);    // file attribute
00123                                 mem_writew(sftptr+sftofs+0x05,Files[reg_bx]->GetInformation()); // device info word
00124                                 mem_writed(sftptr+sftofs+0x07,0);               // device driver header
00125                                 mem_writew(sftptr+sftofs+0x0d,0);               // packed time
00126                                 mem_writew(sftptr+sftofs+0x0f,0);               // packed date
00127                                 mem_writew(sftptr+sftofs+0x11,0);               // size
00128                                 mem_writew(sftptr+sftofs+0x15,0);               // current position
00129                         } else {
00130                                 Bit8u drive=Files[reg_bx]->GetDrive();
00131 
00132                                 mem_writew(sftptr+sftofs+0x02,(Bit16u)(Files[reg_bx]->flags&3));        // file open mode
00133                                 mem_writeb(sftptr+sftofs+0x04,(Bit8u)(Files[reg_bx]->attr));            // file attribute
00134                                 mem_writew(sftptr+sftofs+0x05,0x40|drive);                                                      // device info word
00135                                 mem_writed(sftptr+sftofs+0x07,RealMake(dos.tables.dpb,drive));          // dpb of the drive
00136                                 mem_writew(sftptr+sftofs+0x0d,Files[reg_bx]->time);                                     // packed file time
00137                                 mem_writew(sftptr+sftofs+0x0f,Files[reg_bx]->date);                                     // packed file date
00138                                 Bit32u curpos=0;
00139                                 Files[reg_bx]->Seek(&curpos,DOS_SEEK_CUR);
00140                                 Bit32u endpos=0;
00141                                 Files[reg_bx]->Seek(&endpos,DOS_SEEK_END);
00142                                 mem_writed(sftptr+sftofs+0x11,endpos);          // size
00143                                 mem_writed(sftptr+sftofs+0x15,curpos);          // current position
00144                                 Files[reg_bx]->Seek(&curpos,DOS_SEEK_SET);
00145                         }
00146 
00147                         // fill in filename in fcb style
00148                         // (space-padded name (8 chars)+space-padded extension (3 chars))
00149                         const char* filename=(const char*)Files[reg_bx]->GetName();
00150                         if (strrchr(filename,'\\')) filename=strrchr(filename,'\\')+1;
00151                         if (strrchr(filename,'/')) filename=strrchr(filename,'/')+1;
00152                         if (!filename) return true;
00153                         const char* dotpos=strrchr(filename,'.');
00154                         if (dotpos) {
00155                                 dotpos++;
00156                                 size_t nlen=strlen(filename);
00157                                 size_t extlen=strlen(dotpos);
00158                                 Bits nmelen=(Bits)nlen-(Bits)extlen;
00159                                 if (nmelen<1) return true;
00160                                 nlen-=(extlen+1);
00161 
00162                                 if (nlen>8) nlen=8;
00163                                 size_t i;
00164 
00165                                 for (i=0; i<nlen; i++)
00166                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)filename[i]);
00167                                 for (i=nlen; i<8; i++)
00168                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)' ');
00169                                 
00170                                 if (extlen>3) extlen=3;
00171                                 for (i=0; i<extlen; i++)
00172                                         mem_writeb((PhysPt)(sftptr+sftofs+0x28u+i),(unsigned char)dotpos[i]);
00173                                 for (i=extlen; i<3; i++)
00174                                         mem_writeb((PhysPt)(sftptr+sftofs+0x28u+i),(unsigned char)' ');
00175                         } else {
00176                                 size_t i;
00177                                 size_t nlen=strlen(filename);
00178                                 if (nlen>8) nlen=8;
00179                                 for (i=0; i<nlen; i++)
00180                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)filename[i]);
00181                                 for (i=nlen; i<11; i++)
00182                                         mem_writeb((PhysPt)(sftptr+sftofs+0x20u+i),(unsigned char)' ');
00183                         }
00184 
00185                         SegSet16(es,RealSeg(sftrealpt));
00186                         reg_di=RealOff(sftrealpt+sftofs);
00187                         reg_ax=0xc000;
00188 
00189                 }
00190                 return true;
00191         case 0x1605:    /* Windows init broadcast */
00192                 if (enable_a20_on_windows_init) {
00193                         /* This hack exists because Windows 3.1 doesn't seem to enable A20 first during an
00194                          * initial critical period where it assumes it's on, prior to checking and enabling/disabling it.
00195                          *
00196                          * Note that Windows 3.1 also makes this mistake in Standard/286 mode, but it doesn't even
00197                          * make this callout, so this hack is useless unless you are using Enhanced/386 mode.
00198                          * If you want to run Windows 3.1 Standard mode with a20=mask you will have to run builtin
00199                          * command "a20gate on" to turn on the A20 gate prior to starting Windows. */
00200                         LOG_MSG("Enabling A20 gate for Windows in response to INIT broadcast");
00201                         XMS_EnableA20(true);
00202                 }
00203 
00204                 /* TODO: Maybe future parts of DOSBox-X will do something with this */
00205                 /* 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:\ */
00206                 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))",
00207                         SegValue(es),reg_bx,
00208                         SegValue(ds),reg_si,
00209                         reg_cx,reg_dx,reg_di,
00210                         reg_di>>8,reg_di&0xFF);
00211                 if (reg_dx & 0x0001)
00212                         LOG_MSG(" [286 DOS extender]");
00213                 else
00214                         LOG_MSG(" [Enhanced mode]");
00215                 LOG_MSG("\n");
00216 
00217                 /* NTS: The way this protocol works, is that when you (the program hooking this call) receive it,
00218                  *      you first pass the call down to the previous INT 2Fh handler with registers unmodified,
00219                  *      and then when the call unwinds back up the chain, THEN you modify the results to notify
00220                  *      yourself to Windows. So logically, since we're the DOS kernel at the end of the chain,
00221                  *      we should still see ES:BX=0000:0000 and DS:SI=0000:0000 and CX=0000 unmodified from the
00222                  *      way the Windows kernel issued the call. If that's not the case, then we need to issue
00223                  *      a warning because some bastard on the call chain is ruining it for all of us. */
00224                 if (SegValue(es) != 0 || reg_bx != 0 || SegValue(ds) != 0 || reg_si != 0 || reg_cx != 0) {
00225                         LOG_MSG("WARNING: Some registers at this point (the top of the call chain) are nonzero.\n");
00226                         LOG_MSG("         That means a TSR or other entity has modified registers on the way down\n");
00227                         LOG_MSG("         the call chain. The Windows init broadcast is supposed to be handled\n");
00228                         LOG_MSG("         going down the chain by calling the previous INT 2Fh handler with registers\n");
00229                         LOG_MSG("         unmodified, and only modify registers on the way back up the chain!\n");
00230                 }
00231 
00232                 return false; /* pass it on to other INT 2F handlers */
00233         case 0x1606:    /* Windows exit broadcast */
00234                 /* TODO: Maybe future parts of DOSBox-X will do something with this */
00235                 /* 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:\ */
00236                 LOG_MSG("DEBUG: INT 2Fh Windows 286/386 DOSX exit broadcast issued (DX=0x%04x)",reg_dx);
00237                 if (reg_dx & 0x0001)
00238                         LOG_MSG(" [286 DOS extender]");
00239                 else
00240                         LOG_MSG(" [Enhanced mode]");
00241                 LOG_MSG("\n");
00242                 return false; /* pass it on to other INT 2F handlers */
00243         case 0x1607:
00244                 /* 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:\
00245                  *       Additionally, if the user WANTS to see every invocation of the IDLE call, then allow them to enable that too */
00246                 if (reg_bx != 0x18) { /* don't show the idle call. it's used too often */
00247                         const char *str = Win_NameThatVXD(reg_bx);
00248 
00249                         if (str == NULL) str = "??";
00250                         LOG_MSG("DEBUG: INT 2Fh Windows virtual device '%s' callout (BX(deviceID)=0x%04x CX(function)=0x%04x)\n",
00251                                 str,reg_bx,reg_cx);
00252                 }
00253 
00254                 if (reg_bx == 0x15) { /* DOSMGR */
00255                         switch (reg_cx) {
00256                                 case 0x0000:            // query instance
00257                                         reg_cx = 0x0001;
00258                                         reg_dx = 0x50;          // dos driver segment
00259                                         SegSet16(es,0x50);      // patch table seg
00260                                         reg_bx = 0x60;          // patch table ofs
00261                                         return true;
00262                                 case 0x0001:            // set patches
00263                                         reg_ax = 0xb97c;
00264                                         reg_bx = (reg_dx & 0x16);
00265                                         reg_dx = 0xa2ab;
00266                                         return true;
00267                                 case 0x0003:            // get size of data struc
00268                                         if (reg_dx==0x0001) {
00269                                                 // CDS size requested
00270                                                 reg_ax = 0xb97c;
00271                                                 reg_dx = 0xa2ab;
00272                                                 reg_cx = 0x000e;        // size
00273                                         }
00274                                         return true;
00275                                 case 0x0004:            // instanced data
00276                                         reg_dx = 0;             // none
00277                                         return true;
00278                                 case 0x0005:            // get device driver size
00279                                         reg_ax = 0;
00280                                         reg_dx = 0;
00281                                         return true;
00282                                 default:
00283                                         return false;
00284                         }
00285                 }
00286                 else if (reg_bx == 0x18) { /* VMPoll (idle) */
00287                         return true;
00288                 }
00289                 else return false;
00290         case 0x1680:    /*  RELEASE CURRENT VIRTUAL MACHINE TIME-SLICE */
00291                 //TODO Maybe do some idling but could screw up other systems :)
00292                 return true; //So no warning in the debugger anymore
00293         case 0x1689:    /*  Kernel IDLE CALL */
00294         case 0x168f:    /*  Close awareness crap */
00295            /* Removing warning */
00296                 return true;
00297         case 0x4a01: {  /* Query free hma space */
00298                 Bit32u limit = DOS_HMA_LIMIT();
00299 
00300                 if (limit == 0) {
00301                         /* TODO: What does MS-DOS prior to v5.0? */
00302                         reg_bx = 0;
00303                         reg_di = 0xFFFF;
00304                         SegSet16(es,0xFFFF);
00305                         LOG(LOG_MISC,LOG_DEBUG)("HMA query: rejected");
00306                         return true;
00307                 }
00308 
00309                 Bit32u start = DOS_HMA_FREE_START();
00310                 reg_bx = limit - start; /* free space in bytes */
00311                 SegSet16(es,0xffff);
00312                 reg_di = (start + 0x10) & 0xFFFF;
00313                 LOG(LOG_MISC,LOG_DEBUG)("HMA query: start=0x%06x limit=0x%06x free=0x%06x -> bx=%u %04x:%04x",
00314                         start,limit,DOS_HMA_GET_FREE_SPACE(),(int)reg_bx,(int)SegValue(es),(int)reg_di);
00315                 } return true;
00316         case 0x4a02: {  /* ALLOCATE HMA SPACE */
00317                 Bit32u limit = DOS_HMA_LIMIT();
00318 
00319                 if (limit == 0) {
00320                         /* TODO: What does MS-DOS prior to v5.0? */
00321                         reg_bx = 0;
00322                         reg_di = 0xFFFF;
00323                         SegSet16(es,0xFFFF);
00324                         LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: rejected");
00325                         return true;
00326                 }
00327 
00328                 /* NTS: According to RBIL, Windows 95 adds a deallocate function and changes HMA allocation up to follow a
00329                  *      MCB chain structure. Which is something we're probably not going to add for awhile. */
00330                 /* FIXME: So, according to Ralph Brown Interrupt List, MS-DOS 5 and 6 liked to round up to the next paragraph? */
00331                 if (dos.version.major < 7 && (reg_bx & 0xF) != 0)
00332                         reg_bx = (reg_bx + 0xF) & (~0xF);
00333 
00334                 Bit32u start = DOS_HMA_FREE_START();
00335                 if ((start+reg_bx) > limit) {
00336                         LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: rejected (not enough room) for %u bytes (0x%x + 0x%x > 0x%x)",reg_bx,
00337                 (unsigned int)start,(unsigned int)reg_bx,(unsigned int)limit);
00338                         reg_bx = 0;
00339                         reg_di = 0xFFFF;
00340                         SegSet16(es,0xFFFF);
00341                         return true;
00342                 }
00343 
00344                 /* convert the start to segment:offset, normalized to FFFF:offset */
00345                 reg_di = (start + 0x10) & 0xFFFF;
00346                 SegSet16(es,0xFFFF);
00347 
00348                 /* let HMA emulation know what was claimed */
00349                 LOG(LOG_MISC,LOG_DEBUG)("HMA allocation: %u bytes at FFFF:%04x",reg_bx,reg_di);
00350                 DOS_HMA_CLAIMED(reg_bx);
00351                 } return true;
00352         }
00353 
00354         return false;
00355 }
00356 
00357 void DOS_SetupMisc(void) {
00358         /* Setup the dos multiplex interrupt */
00359         call_int2f=CALLBACK_Allocate();
00360         CALLBACK_Setup(call_int2f,&INT2F_Handler,CB_IRET,"DOS Int 2f");
00361         RealSetVec(0x2f,CALLBACK_RealPointer(call_int2f));
00362         DOS_AddMultiplexHandler(DOS_MultiplexFunctions);
00363         /* Setup the dos network interrupt */
00364         call_int2a=CALLBACK_Allocate();
00365         CALLBACK_Setup(call_int2a,&INT2A_Handler,CB_IRET,"DOS Int 2a");
00366         RealSetVec(0x2A,CALLBACK_RealPointer(call_int2a));
00367 }
00368 
00369 void CALLBACK_DeAllocate(Bitu in);
00370 
00371 void DOS_UninstallMisc(void) {
00372         /* these vectors shouldn't exist when booting a guest OS */
00373         if (call_int2a) {
00374                 RealSetVec(0x2a,0);
00375                 CALLBACK_DeAllocate(call_int2a);
00376                 call_int2a=0;
00377         }
00378         if (call_int2f) {
00379                 RealSetVec(0x2f,0);
00380                 CALLBACK_DeAllocate(call_int2f);
00381                 call_int2f=0;
00382         }
00383 }
00384