DOSBox-X
|
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 }