DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_tables.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 "mem.h"
00022 #include "dos_inc.h"
00023 #include "callback.h"
00024 #include "control.h"
00025 #include <assert.h>
00026 
00027 extern int maxfcb;
00028 extern Bitu DOS_PRIVATE_SEGMENT_Size;
00029 
00030 void CALLBACK_DeAllocate(Bitu in);
00031 
00032 std::list<DOS_GetMemLog_Entry> DOS_GetMemLog;
00033 
00034 #ifdef _MSC_VER
00035 #pragma pack(1)
00036 #endif
00037 struct DOS_TableCase {  
00038         Bit16u size;
00039         Bit8u chars[256];
00040 }
00041 GCC_ATTRIBUTE (packed);
00042 #ifdef _MSC_VER
00043 #pragma pack ()
00044 #endif
00045 
00046 RealPt DOS_DriveDataListHead=0;       // INT 2Fh AX=0803h DRIVER.SYS drive data table list
00047 RealPt DOS_TableUpCase;
00048 RealPt DOS_TableLowCase;
00049 
00050 static Bitu call_casemap = 0;
00051 
00052 void DOS_Casemap_Free(void) {
00053     if (call_casemap != 0) {
00054         CALLBACK_DeAllocate(call_casemap);
00055         call_casemap = 0;
00056     }
00057 }
00058 
00059 static Bit16u dos_memseg=0;//DOS_PRIVATE_SEGMENT;
00060 
00061 extern Bitu VGA_BIOS_SEG_END;
00062 bool DOS_GetMemory_unmapped = false;
00063 
00064 void DOS_GetMemory_reset() {
00065         dos_memseg = 0;
00066 }
00067 
00068 void DOS_GetMemory_reinit() {
00069     DOS_GetMemory_unmapped = false;
00070     DOS_GetMemory_reset();
00071 }
00072 
00073 void DOS_GetMemory_unmap() {
00074         if (DOS_PRIVATE_SEGMENT != 0) {
00075                 LOG(LOG_MISC,LOG_DEBUG)("Unmapping DOS private segment 0x%04x-0x%04x",DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1u);
00076                 if (DOS_PRIVATE_SEGMENT >= 0xA000u) MEM_unmap_physmem((Bitu)DOS_PRIVATE_SEGMENT<<4u,((Bitu)DOS_PRIVATE_SEGMENT_END<<4u)-1u);
00077                 DOS_GetMemory_unmapped = true;
00078                 DOS_PRIVATE_SEGMENT_END = 0;
00079                 DOS_PRIVATE_SEGMENT = 0;
00080                 dos_memseg = 0;
00081         }
00082 }
00083 
00084 bool DOS_User_Wants_UMBs() {
00085     const Section_prop* section = static_cast<Section_prop*>(control->GetSection("dos"));
00086     return section->Get_bool("umb");
00087 }
00088 
00089 void DOS_GetMemory_Choose() {
00090         if (DOS_PRIVATE_SEGMENT == 0) {
00091         /* DOSBox-X non-compatible: Position ourself just past the VGA BIOS */
00092         /* NTS: Code has been arranged so that DOS kernel init follows BIOS INT10h init */
00093         DOS_PRIVATE_SEGMENT=(Bit16u)VGA_BIOS_SEG_END;
00094         DOS_PRIVATE_SEGMENT_END= (Bit16u)(DOS_PRIVATE_SEGMENT + DOS_PRIVATE_SEGMENT_Size);
00095 
00096         if (IS_PC98_ARCH) {
00097             bool PC98_FM_SoundBios_Enabled(void);
00098 
00099             /* Do not let the private segment overlap with anything else after segment C800:0000 including the SOUND ROM at CC00:0000.
00100              * Limiting to 32KB also leaves room for UMBs if enabled between C800:0000 and the EMS page frame at (usually) D000:0000 */
00101             unsigned int limit = 0xD000;
00102 
00103             if (PC98_FM_SoundBios_Enabled()) {
00104                 // TODO: What about sound BIOSes larger than 16KB?
00105                 if (limit > 0xCC00)
00106                     limit = 0xCC00;
00107             }
00108 
00109             if (DOS_User_Wants_UMBs()) {
00110                 // leave room for UMBs, things are cramped a bit in PC-98 mode
00111                 if (limit > 0xC600)
00112                     limit = 0xC600;
00113             }
00114 
00115             if (DOS_PRIVATE_SEGMENT_END > limit)
00116                 DOS_PRIVATE_SEGMENT_END = limit;
00117 
00118             if (DOS_PRIVATE_SEGMENT >= DOS_PRIVATE_SEGMENT_END)
00119                 E_Exit("Insufficient room in upper memory area for private area");
00120         }
00121 
00122                 if (DOS_PRIVATE_SEGMENT >= 0xA000) {
00123                         memset(GetMemBase()+((Bitu)DOS_PRIVATE_SEGMENT<<4u),0x00,(Bitu)(DOS_PRIVATE_SEGMENT_END-DOS_PRIVATE_SEGMENT)<<4u);
00124                         MEM_map_RAM_physmem((Bitu)DOS_PRIVATE_SEGMENT<<4u,((Bitu)DOS_PRIVATE_SEGMENT_END<<4u)-1u);
00125                 }
00126 
00127                 LOG(LOG_MISC,LOG_DEBUG)("DOS private segment set to 0x%04x-0x%04x",DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1);
00128         }
00129 }
00130 
00131 Bit16u DOS_GetMemory(Bit16u pages,const char *who) {
00132         if (who == NULL) who = "";
00133         if (dos_memseg == 0) {
00134                 if (DOS_GetMemory_unmapped) E_Exit("DOS:Attempt to use DOS_GetMemory() when private area was unmapped by BOOT");
00135                 if (DOS_PRIVATE_SEGMENT == 0) DOS_GetMemory_Choose();
00136                 dos_memseg = DOS_PRIVATE_SEGMENT;
00137                 if (dos_memseg == 0) E_Exit("DOS:DOS_GetMemory() before private area has been initialized");
00138         }
00139 
00140         if (((Bitu)pages+(Bitu)dos_memseg) > DOS_PRIVATE_SEGMENT_END) {
00141                 LOG(LOG_DOSMISC,LOG_ERROR)("DOS_GetMemory(%u) failed for '%s' (alloc=0x%04x segment=0x%04x end=0x%04x)",
00142                         pages,who,dos_memseg,DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END);
00143                 E_Exit("DOS:Not enough memory for internal tables");
00144         }
00145         Bit16u page=dos_memseg;
00146         LOG(LOG_DOSMISC,LOG_DEBUG)("DOS_GetMemory(0x%04x pages,\"%s\") = 0x%04x",pages,who,page);
00147 
00148     {
00149         DOS_GetMemLog_Entry ent;
00150 
00151         ent.segbase = page;
00152         ent.pages = pages;
00153         ent.who = who;
00154 
00155         DOS_GetMemLog.push_back(ent);
00156     }
00157 
00158         dos_memseg+=pages;
00159         return page;
00160 }
00161 
00162 static Bitu DOS_CaseMapFunc(void) {
00163         //LOG(LOG_DOSMISC,LOG_ERROR)("Case map routine called : %c",reg_al);
00164         return CBRET_NONE;
00165 }
00166 
00167 static Bit8u country_info[0x22] = {
00168 /* Date format      */  0x00, 0x00,
00169 /* Currencystring   */  0x24, 0x00, 0x00, 0x00, 0x00,
00170 /* Thousands sep    */  0x2c, 0x00,
00171 /* Decimal sep      */  0x2e, 0x00,
00172 /* Date sep         */  0x2d, 0x00,
00173 /* time sep         */  0x3a, 0x00,
00174 /* currency form    */  0x00,
00175 /* digits after dec */  0x02,
00176 /* Time format      */  0x00,
00177 /* Casemap          */  0x00, 0x00, 0x00, 0x00,
00178 /* Data sep         */  0x2c, 0x00,
00179 /* Reservered 5     */  0x00, 0x00, 0x00, 0x00, 0x00,
00180 /* Reservered 5     */  0x00, 0x00, 0x00, 0x00, 0x00
00181 };
00182 
00183 static Bit8u country_info_pc98[0x22] = {
00184 /* Date format      */  0x02, 0x00,
00185 /* Currencystring   */  0x5C, 0x00, 0x00, 0x00, 0x00,
00186 /* Thousands sep    */  0x2c, 0x00,
00187 /* Decimal sep      */  0x2e, 0x00,
00188 /* Date sep         */  0x2d, 0x00,
00189 /* time sep         */  0x3a, 0x00,
00190 /* currency form    */  0x00,
00191 /* digits after dec */  0x00,
00192 /* Time format      */  0x01,
00193 /* Casemap          */  0x00, 0x00, 0x00, 0x00,
00194 /* Data sep         */  0x2c, 0x00,
00195 /* Reservered 5     */  0x00, 0x00, 0x00, 0x00, 0x00,
00196 /* Reservered 5     */  0x00, 0x00, 0x00, 0x00, 0x00
00197 };
00198 
00199 extern bool enable_dbcs_tables;
00200 extern bool enable_filenamechar;
00201 extern bool enable_collating_uppercase;
00202 
00203 PhysPt DOS_Get_DPB(unsigned int dos_drive) {
00204     if (dos_drive >= DOS_DRIVES)
00205         return 0;
00206 
00207     return PhysMake(dos.tables.dpb,dos_drive*dos.tables.dpb_size);
00208 }
00209 
00210 void DOS_SetupTables(void) {
00211         Bit16u seg;Bit16u i;
00212         dos.tables.tempdta=RealMake(DOS_GetMemory(4,"dos.tables.tempdta"),0);
00213         dos.tables.tempdta_fcbdelete=RealMake(DOS_GetMemory(4,"dos.tables.fcbdelete"),0);
00214         /* Create the DOS Info Block */
00215         dos_infoblock.SetLocation(DOS_INFOBLOCK_SEG); //c2woody
00216    
00217         /* create SDA */
00218         DOS_SDA(DOS_SDA_SEG,0).Init();
00219 
00220         /* Some weird files >20 detection routine */
00221         /* Possibly obselete when SFT is properly handled */
00222         real_writed(DOS_CONSTRING_SEG,0x0a,0x204e4f43);
00223         real_writed(DOS_CONSTRING_SEG,0x1a,0x204e4f43);
00224         real_writed(DOS_CONSTRING_SEG,0x2a,0x204e4f43);
00225 
00226         /* create a CON device driver */
00227         seg=DOS_CONDRV_SEG;
00228         real_writed(seg,0x00,0xffffffff);       // next ptr
00229         real_writew(seg,0x04,0x8013);           // attributes
00230         real_writed(seg,0x06,0xffffffff);       // strategy routine
00231         real_writed(seg,0x0a,0x204e4f43);       // driver name
00232         real_writed(seg,0x0e,0x20202020);       // driver name
00233         dos_infoblock.SetDeviceChainStart(RealMake(seg,0));
00234    
00235         /* Create a fake Current Directory Structure */
00236         seg=DOS_CDS_SEG;
00237         real_writed(seg,0x00,0x005c3a43);
00238         dos_infoblock.SetCurDirStruct(RealMake(seg,0));
00239 
00240 
00241 
00242     /* Allocate DCBS DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
00243     if (enable_dbcs_tables) {
00244         dos.tables.dbcs=RealMake(DOS_GetMemory(12,"dos.tables.dbcs"),0);
00245 
00246         if (IS_PC98_ARCH) {
00247             // write a valid table, or else Windows 3.1 is unhappy.
00248             // Values are copied from INT 21h AX=6300h as returned by an MS-DOS 6.22 boot disk
00249             mem_writeb(Real2Phys(dos.tables.dbcs)+0,0x81);  // low/high DBCS pair 1
00250             mem_writeb(Real2Phys(dos.tables.dbcs)+1,0x9F);
00251             mem_writeb(Real2Phys(dos.tables.dbcs)+2,0xE0);  // low/high DBCS pair 2
00252             mem_writeb(Real2Phys(dos.tables.dbcs)+3,0xFC);
00253             mem_writed(Real2Phys(dos.tables.dbcs)+4,0);
00254         }
00255         else {
00256             mem_writed(Real2Phys(dos.tables.dbcs),0); //empty table
00257         }
00258     }
00259     else {
00260         dos.tables.dbcs=0;
00261     }
00262         /* FILENAME CHARACTER TABLE */
00263         if (enable_filenamechar) {
00264                 dos.tables.filenamechar=RealMake(DOS_GetMemory(2,"dos.tables.filenamechar"),0);
00265                 mem_writew(Real2Phys(dos.tables.filenamechar)+0x00,0x16);
00266                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x02,0x01);
00267                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x03,0x00);       // allowed chars from
00268                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x04,0xff);       // ...to
00269                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x05,0x00);
00270                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x06,0x00);       // excluded chars from
00271                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x07,0x20);       // ...to
00272                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x08,0x02);
00273                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x09,0x0e);       // number of illegal separators
00274                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0a,0x2e);
00275                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0b,0x22);
00276                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0c,0x2f);
00277                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0d,0x5c);
00278                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0e,0x5b);
00279                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x0f,0x5d);
00280                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x10,0x3a);
00281                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x11,0x7c);
00282                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x12,0x3c);
00283                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x13,0x3e);
00284                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x14,0x2b);
00285                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x15,0x3d);
00286                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x16,0x3b);
00287                 mem_writeb(Real2Phys(dos.tables.filenamechar)+0x17,0x2c);
00288         }
00289         else {
00290                 dos.tables.filenamechar = 0;
00291         }
00292         /* COLLATING SEQUENCE TABLE + UPCASE TABLE*/
00293         // 256 bytes for col table, 128 for upcase, 4 for number of entries
00294         if (enable_collating_uppercase) {
00295                 dos.tables.collatingseq=RealMake(DOS_GetMemory(25,"dos.tables.collatingseq"),0);
00296                 mem_writew(Real2Phys(dos.tables.collatingseq),0x100);
00297                 for (i=0; i<256; i++) mem_writeb(Real2Phys(dos.tables.collatingseq)+i+2,(Bit8u)i);
00298                 dos.tables.upcase=dos.tables.collatingseq+258;
00299                 mem_writew(Real2Phys(dos.tables.upcase),0x80);
00300                 for (i=0; i<128; i++) mem_writeb(Real2Phys(dos.tables.upcase)+i+2,(Bit8u)0x80+i);
00301         }
00302         else {
00303                 dos.tables.collatingseq = 0;
00304                 dos.tables.upcase = 0;
00305         }
00306 
00307         /* Create a fake FCB SFT */
00308         seg=DOS_GetMemory(4,"Fake FCB SFT");
00309         real_writed(seg,0,0xffffffff);          //Last File Table
00310         real_writew(seg,4,maxfcb);                      //File Table supports 100 files
00311         dos_infoblock.SetFCBTable(RealMake(seg,0));
00312 
00313         /* Create a fake DPB */
00314         dos.tables.dpb=DOS_GetMemory(((DOS_DRIVES*dos.tables.dpb_size)+15u)/16u,"dos.tables.dpb");
00315     dos.tables.mediaid_offset=0x17;     //Media ID offset in DPB (MS-DOS 4.x-6.x)
00316         dos.tables.mediaid=RealMake(dos.tables.dpb,dos.tables.mediaid_offset);
00317         for (i=0;i<DOS_DRIVES;i++) {
00318         real_writeb(dos.tables.dpb,i*dos.tables.dpb_size,(Bit8u)i);             // drive number
00319         real_writeb(dos.tables.dpb,i*dos.tables.dpb_size+1,(Bit8u)i);           // unit number
00320         real_writew(dos.tables.dpb,i*dos.tables.dpb_size+2,0x0200);     // bytes per sector
00321         real_writew(dos.tables.dpb,i*dos.tables.dpb_size+6,0x0001);     // reserved sectors at the beginning of the drive
00322         mem_writew(Real2Phys(dos.tables.mediaid)+i*dos.tables.dpb_size,0u);
00323         real_writew(dos.tables.dpb,i*dos.tables.dpb_size+0x1F,0xFFFF);      // number of free clusters or 0xFFFF if unknown
00324 
00325         // next DPB pointer
00326         if ((i+1) < DOS_DRIVES)
00327             real_writed(dos.tables.dpb,i*dos.tables.dpb_size+0x19,RealMake(dos.tables.dpb,(i+1)*dos.tables.dpb_size));
00328         else
00329             real_writed(dos.tables.dpb,i*dos.tables.dpb_size+0x19,0xFFFFFFFF); // ED4.EXE (provided by Yksoft1) expects this, or else loops forever
00330         }
00331     dos_infoblock.SetFirstDPB(RealMake(dos.tables.dpb,0));
00332 
00333         /* Create a fake disk buffer head */
00334         seg=DOS_GetMemory(6,"Fake disk buffer head");
00335         for (Bit8u ct=0; ct<0x20; ct++) real_writeb(seg,ct,0);
00336         real_writew(seg,0x00,0xffff);           // forward ptr
00337         real_writew(seg,0x02,0xffff);           // backward ptr
00338         real_writeb(seg,0x04,0xff);                     // not in use
00339         real_writeb(seg,0x0a,0x01);                     // number of FATs
00340         real_writed(seg,0x0d,0xffffffff);       // pointer to DPB
00341         dos_infoblock.SetDiskBufferHeadPt(RealMake(seg,0));
00342 
00343         /* Set buffers to a nice value */
00344         dos_infoblock.SetBuffers(50,50);
00345 
00346         /* case map routine INT 0x21 0x38 */
00347         call_casemap = CALLBACK_Allocate();
00348         CALLBACK_Setup(call_casemap,DOS_CaseMapFunc,CB_RETF,"DOS CaseMap");
00349         /* Add it to country structure */
00350     if (IS_PC98_ARCH) {
00351         host_writed(country_info_pc98 + 0x12, CALLBACK_RealPointer(call_casemap));
00352         dos.tables.country=country_info_pc98;
00353     }
00354     else {
00355         host_writed(country_info + 0x12, CALLBACK_RealPointer(call_casemap));
00356         dos.tables.country=country_info;
00357     }
00358 
00359     /* PC-98 INT 1Bh device list (60:6Ch-7Bh).
00360      * For now, just write a fake list to satisfy any PC-98 game that
00361      * requires a "master disk" to run even if running from an HDI.
00362      * See also: [http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/Undocumented%209801%2c%209821%20Volume%202%20%28webtech.co.jp%29/memdos%2etxt]
00363      * This is needed to run "Legend of Heroes III" */
00364     if (IS_PC98_ARCH) {
00365         // FIXME: This is just a fake list. At some point in the future, this
00366         //        list needs to reflect the state of all MOUNT/IMGMOUNT commands
00367         //        while in the DOS environment provided by this emulation.
00368         //
00369         //        The byte values seem to match drive letter assignment, as noted:
00370         //        [https://github.com/joncampbell123/dosbox-x/issues/1226]
00371         for (i=0;i < 0x10;i++) real_writeb(0x60,0x6C+i,0);
00372         real_writeb(0x60,0x6C,0xA0);    /* hard drive */
00373         real_writeb(0x60,0x6D,0x90);    /* floppy drive */
00374     }
00375 
00376     /* fake DRIVER.SYS data table list, to satisfy Windows 95 setup.
00377      * The list is supposed to be a linked list of drive BPBs, INT 13h info, etc.
00378      * terminated by offset 0xFFFF. For now, just point at a 0xFFFF.
00379      * Note that Windows 95 setup and SCANDISK.EXE have different criteria on
00380      * the return value of INT 2Fh AX=803h and returning without a pointer
00381      * really isn't an option. According to RBIL this interface is built into
00382      * MS-DOS. */
00383     DOS_DriveDataListHead = RealMake(DOS_GetMemory(1/*paragraph*/,"driver.sys.data.list"),0);
00384     mem_writew(Real2Phys(DOS_DriveDataListHead)+0x00,0xFFFF); /* list termination */
00385     mem_writew(Real2Phys(DOS_DriveDataListHead)+0x02,0xFFFF);
00386     mem_writew(Real2Phys(DOS_DriveDataListHead)+0x04,0x0000);
00387 }
00388 
00389 // save state support
00390 void POD_Save_DOS_Tables( std::ostream& stream )
00391 {
00392         // - pure data
00393         WRITE_POD( &DOS_TableUpCase, DOS_TableUpCase );
00394         WRITE_POD( &DOS_TableLowCase, DOS_TableLowCase );
00395 
00396         WRITE_POD( &dos_memseg, dos_memseg );
00397 }
00398 
00399 void POD_Load_DOS_Tables( std::istream& stream )
00400 {
00401         // - pure data
00402         READ_POD( &DOS_TableUpCase, DOS_TableUpCase );
00403         READ_POD( &DOS_TableLowCase, DOS_TableLowCase );
00404 
00405         READ_POD( &dos_memseg, dos_memseg );
00406 }