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 * Heavy improvements like PC-98 and LFN support by the DOSBox-X Team 00019 * With major works from joncampbell123 and Wengier 00020 */ 00021 00022 00023 #include <stdlib.h> 00024 #include <string.h> 00025 #include <ctype.h> 00026 #include "dosbox.h" 00027 #include "dos_inc.h" 00028 #include "bios.h" 00029 #include "mem.h" 00030 #include "paging.h" 00031 #include "callback.h" 00032 #include "regs.h" 00033 #include "menu.h" 00034 #include "mapper.h" 00035 #include "drives.h" 00036 #include "setup.h" 00037 #include "support.h" 00038 #include "parport.h" 00039 #include "serialport.h" 00040 #include "dos_network.h" 00041 00042 extern bool log_int21, log_fileio; 00043 extern int lfn_filefind_handle; 00044 unsigned long totalc, freec; 00045 Bit16u countryNo = 0; 00046 Bitu INT29_HANDLER(void); 00047 Bit32u BIOS_get_PC98_INT_STUB(void); 00048 00049 int ascii_toupper(int c) { 00050 if (c >= 'a' && c <= 'z') 00051 return c + 'A' - 'a'; 00052 00053 return c; 00054 } 00055 00056 bool shiftjis_lead_byte(int c) { 00057 if ((((unsigned char)c & 0xE0) == 0x80) || 00058 (((unsigned char)c & 0xE0) == 0xE0)) 00059 return true; 00060 00061 return false; 00062 } 00063 00064 char * shiftjis_upcase(char * str) { 00065 for (char* idx = str; *idx ; ) { 00066 if (shiftjis_lead_byte(*idx)) { 00067 /* Shift-JIS is NOT ASCII and should not be converted to uppercase like ASCII. 00068 * The trailing byte can be mistaken for ASCII */ 00069 idx++; 00070 if (*idx != 0) idx++; 00071 } 00072 else { 00073 *idx = ascii_toupper(*reinterpret_cast<unsigned char*>(idx)); 00074 idx++; 00075 } 00076 } 00077 00078 return str; 00079 } 00080 00081 static Bitu DOS_25Handler_Actual(bool fat32); 00082 static Bitu DOS_26Handler_Actual(bool fat32); 00083 00084 unsigned char cpm_compat_mode = CPM_COMPAT_MSDOS5; 00085 00086 bool dos_in_hma = true; 00087 bool dos_umb = true; 00088 bool DOS_BreakFlag = false; 00089 bool enable_dbcs_tables = true; 00090 bool enable_filenamechar = true; 00091 bool enable_share_exe_fake = true; 00092 bool rsize = false; 00093 bool packerr = false; 00094 int dos_initial_hma_free = 34*1024; 00095 int dos_sda_size = 0x560; 00096 int dos_clipboard_device_access; 00097 char *dos_clipboard_device_name; 00098 const char dos_clipboard_device_default[]="CLIP$"; 00099 00100 int maxfcb=100; 00101 int maxdrive=1; 00102 int enablelfn=-1; 00103 bool uselfn; 00104 extern bool infix; 00105 extern bool int15_wait_force_unmask_irq; 00106 extern bool startup_state_numlock; 00107 00108 Bit32u dos_hma_allocator = 0; /* physical memory addr */ 00109 00110 Bitu XMS_EnableA20(bool enable); 00111 Bitu XMS_GetEnabledA20(void); 00112 bool XMS_IS_ACTIVE(); 00113 bool XMS_HMA_EXISTS(); 00114 void SetNumLock(void); 00115 00116 bool DOS_IS_IN_HMA() { 00117 if (dos_in_hma && XMS_IS_ACTIVE() && XMS_HMA_EXISTS()) 00118 return true; 00119 00120 return false; 00121 } 00122 00123 Bit32u DOS_HMA_LIMIT() { 00124 if (dos.version.major < 5) return 0; /* MS-DOS 5.0+ only */ 00125 if (!DOS_IS_IN_HMA()) return 0; 00126 return (0x110000 - 16); /* 1MB + 64KB - 16 bytes == (FFFF:FFFF + 1) == (0xFFFF0 + 0xFFFF + 1) == 0x10FFF0 */ 00127 } 00128 00129 Bit32u DOS_HMA_FREE_START() { 00130 if (dos.version.major < 5) return 0; /* MS-DOS 5.0+ only */ 00131 if (!DOS_IS_IN_HMA()) return 0; 00132 00133 if (dos_hma_allocator == 0) { 00134 dos_hma_allocator = 0x110000u - 16u - (unsigned int)dos_initial_hma_free; 00135 LOG(LOG_MISC,LOG_DEBUG)("Starting HMA allocation from physical address 0x%06x (FFFF:%04x)", 00136 dos_hma_allocator,(dos_hma_allocator+0x10u)&0xFFFFu); 00137 } 00138 00139 return dos_hma_allocator; 00140 } 00141 00142 Bit32u DOS_HMA_GET_FREE_SPACE() { 00143 Bit32u start; 00144 00145 if (dos.version.major < 5) return 0; /* MS-DOS 5.0+ only */ 00146 if (!DOS_IS_IN_HMA()) return 0; 00147 start = DOS_HMA_FREE_START(); 00148 if (start == 0) return 0; 00149 return (DOS_HMA_LIMIT() - start); 00150 } 00151 00152 void DOS_HMA_CLAIMED(Bit16u bytes) { 00153 Bit32u limit = DOS_HMA_LIMIT(); 00154 00155 if (limit == 0) E_Exit("HMA allocatiom bug: Claim function called when HMA allocation is not enabled"); 00156 if (dos_hma_allocator == 0) E_Exit("HMA allocatiom bug: Claim function called without having determined start"); 00157 dos_hma_allocator += bytes; 00158 if (dos_hma_allocator > limit) E_Exit("HMA allocation bug: Exceeded limit"); 00159 } 00160 00161 Bit16u DOS_INFOBLOCK_SEG=0x80; // sysvars (list of lists) 00162 Bit16u DOS_CONDRV_SEG=0xa0; 00163 Bit16u DOS_CONSTRING_SEG=0xa8; 00164 Bit16u DOS_SDA_SEG=0xb2; // dos swappable area 00165 Bit16u DOS_SDA_SEG_SIZE=0x560; // WordPerfect 5.1 consideration (emendelson) 00166 Bit16u DOS_SDA_OFS=0; 00167 Bit16u DOS_CDS_SEG=0x108; 00168 Bit16u DOS_MEM_START=0x158; // regression to r3437 fixes nascar 2 colors 00169 Bit16u minimum_mcb_segment=0x70; 00170 Bit16u minimum_mcb_free=0x70; 00171 Bit16u minimum_dos_initial_private_segment=0x70; 00172 00173 Bit16u DOS_PRIVATE_SEGMENT=0;//0xc800; 00174 Bit16u DOS_PRIVATE_SEGMENT_END=0;//0xd000; 00175 00176 Bitu DOS_PRIVATE_SEGMENT_Size=0x800; // 32KB (0x800 pages), mainline DOSBox behavior 00177 00178 bool enable_dummy_device_mcb = true; 00179 00180 extern unsigned int MAXENV;// = 32768u; 00181 extern unsigned int ENV_KEEPFREE;// = 83; 00182 00183 DOS_Block dos; 00184 DOS_InfoBlock dos_infoblock; 00185 00186 extern bool force_sfn, dos_kernel_disabled; 00187 00188 Bit16u DOS_Block::psp() { 00189 if (dos_kernel_disabled) { 00190 LOG_MSG("BUG: DOS kernel is disabled (booting a guest OS), and yet somebody is still asking for DOS's current PSP segment\n"); 00191 return 0x0000; 00192 } 00193 00194 return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetPSP(); 00195 } 00196 00197 void DOS_Block::psp(Bit16u _seg) { 00198 if (dos_kernel_disabled) { 00199 LOG_MSG("BUG: DOS kernel is disabled (booting a guest OS), and yet somebody is still attempting to change DOS's current PSP segment\n"); 00200 return; 00201 } 00202 00203 DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetPSP(_seg); 00204 } 00205 00206 RealPt DOS_Block::dta() { 00207 if (dos_kernel_disabled) { 00208 LOG_MSG("BUG: DOS kernel is disabled (booting a guest OS), and yet somebody is still asking for DOS's DTA (disk transfer address)\n"); 00209 return 0; 00210 } 00211 00212 return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDTA(); 00213 } 00214 00215 void DOS_Block::dta(RealPt _dta) { 00216 if (dos_kernel_disabled) { 00217 LOG_MSG("BUG: DOS kernel is disabled (booting a guest OS), and yet somebody is still attempting to change DOS's DTA (disk transfer address)\n"); 00218 return; 00219 } 00220 00221 DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDTA(_dta); 00222 } 00223 00224 #define DOS_COPYBUFSIZE 0x10000 00225 Bit8u dos_copybuf[DOS_COPYBUFSIZE]; 00226 #ifdef WIN32 00227 Bit16u NetworkHandleList[127];Bit8u dos_copybuf_second[DOS_COPYBUFSIZE]; 00228 #endif 00229 00230 void DOS_SetError(Bit16u code) { 00231 dos.errorcode=code; 00232 } 00233 00234 void DOS_SetCountry(Bit16u countryNo) { 00235 if (dos.tables.country==NULL) return; 00236 *(dos.tables.country+17)=countryNo==1||countryNo==3||countryNo==61?0:1; 00237 switch (countryNo) { 00238 case 1: 00239 *dos.tables.country=0; 00240 break; 00241 case 2: 00242 case 36: 00243 case 38: 00244 case 40: 00245 case 42: 00246 case 46: 00247 case 48: 00248 case 81: 00249 case 82: 00250 case 86: 00251 case 88: 00252 case 354: 00253 case 886: 00254 *dos.tables.country=2; 00255 break; 00256 default: 00257 *dos.tables.country=1; 00258 break; 00259 } 00260 switch (countryNo) { 00261 case 3: 00262 case 30: 00263 case 32: 00264 case 34: 00265 case 39: 00266 case 44: 00267 case 55: 00268 case 88: 00269 case 90: 00270 case 785: 00271 case 886: 00272 case 972: 00273 *(dos.tables.country+11)=0x2f; 00274 break; 00275 case 7: 00276 case 33: 00277 case 41: 00278 case 43: 00279 case 47: 00280 case 49: 00281 case 86: 00282 case 358: 00283 *(dos.tables.country+11)=0x2e; 00284 break; 00285 default: 00286 *(dos.tables.country+11)=0x2d; 00287 break; 00288 } 00289 switch (countryNo) { 00290 case 41: 00291 *(dos.tables.country+13)=0x2c; 00292 break; 00293 case 39: 00294 case 45: 00295 case 46: 00296 case 358: 00297 *(dos.tables.country+13)=0x2e; 00298 break; 00299 default: 00300 *(dos.tables.country+13)=0x3a; 00301 break; 00302 } 00303 } 00304 00305 const Bit8u DOS_DATE_months[] = { 00306 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 00307 }; 00308 00309 static void DOS_AddDays(Bit8u days) { 00310 dos.date.day += days; 00311 Bit8u monthlimit = DOS_DATE_months[dos.date.month]; 00312 00313 if(dos.date.day > monthlimit) { 00314 if((dos.date.year %4 == 0) && (dos.date.month==2)) { 00315 // leap year 00316 if(dos.date.day > 29) { 00317 dos.date.month++; 00318 dos.date.day -= 29; 00319 } 00320 } else { 00321 //not leap year 00322 dos.date.month++; 00323 dos.date.day -= monthlimit; 00324 } 00325 if(dos.date.month > 12) { 00326 // year over 00327 dos.date.month = 1; 00328 dos.date.year++; 00329 } 00330 } 00331 } 00332 00333 #define DATA_TRANSFERS_TAKE_CYCLES 1 00334 #define DOS_OVERHEAD 1 00335 00336 #ifndef DOSBOX_CPU_H 00337 #include "cpu.h" 00338 #endif 00339 00340 // TODO: Make this configurable. 00341 // Additionally, allow this to vary per-drive so that 00342 // Drive D: can be as slow as a 2X IDE CD-ROM drive in PIO mode 00343 // Drive C: can be as slow as a IDE drive in PIO mode and 00344 // Drive A: can be as slow as a 3.5" 1.44MB floppy disk 00345 // 00346 // This fixes MS-DOS games that crash or malfunction if the disk I/O is too fast. 00347 // This also fixes "380 volt" and prevents the "city animation" from loading too fast for it's music timing (and getting stuck) 00348 int disk_data_rate = 2100000; // 2.1MBytes/sec mid 1990s IDE PIO hard drive without SMARTDRV 00349 00350 void diskio_delay(Bits value/*bytes*/) { 00351 if (disk_data_rate != 0) { 00352 double scalar = (double)value / disk_data_rate; 00353 double endtime = PIC_FullIndex() + (scalar * 1000); 00354 00355 /* MS-DOS will most likely enable interrupts in the course of 00356 * performing disk I/O */ 00357 CPU_STI(); 00358 00359 do { 00360 CALLBACK_Idle(); 00361 } while (PIC_FullIndex() < endtime); 00362 } 00363 } 00364 00365 static inline void overhead() { 00366 reg_ip += 2; 00367 } 00368 00369 #define BCD2BIN(x) ((((unsigned int)(x) >> 4u) * 10u) + ((x) & 0x0fu)) 00370 #define BIN2BCD(x) ((((x) / 10u) << 4u) + (x) % 10u) 00371 extern bool date_host_forced; 00372 00373 static Bitu DOS_21Handler(void); 00374 void DOS_Int21_7139(char *name1, const char *name2); 00375 void DOS_Int21_713a(char *name1, const char *name2); 00376 void DOS_Int21_713b(char *name1, const char *name2); 00377 void DOS_Int21_7141(char *name1, const char *name2); 00378 void DOS_Int21_7143(char *name1, const char *name2); 00379 void DOS_Int21_7147(char *name1, const char *name2); 00380 void DOS_Int21_714e(char *name1, char *name2); 00381 void DOS_Int21_714f(const char *name1, const char *name2); 00382 void DOS_Int21_7156(char *name1, char *name2); 00383 void DOS_Int21_7160(char *name1, char *name2); 00384 void DOS_Int21_716c(char *name1, const char *name2); 00385 void DOS_Int21_71a0(char *name1, char *name2); 00386 void DOS_Int21_71a1(const char *name1, const char *name2); 00387 void DOS_Int21_71a6(const char *name1, const char *name2); 00388 void DOS_Int21_71a7(const char *name1, const char *name2); 00389 void DOS_Int21_71a8(char* name1, const char* name2); 00390 void DOS_Int21_71aa(char* name1, const char* name2); 00391 Bitu DEBUG_EnableDebugger(void); 00392 void CALLBACK_RunRealInt_retcsip(Bit8u intnum,Bitu &cs,Bitu &ip); 00393 00394 bool DOS_BreakINT23InProgress = false; 00395 00396 void DOS_PrintCBreak() { 00397 /* print ^C <newline> */ 00398 Bit16u n = 4; 00399 const char *nl = "^C\r\n"; 00400 DOS_WriteFile(STDOUT,(Bit8u*)nl,&n); 00401 } 00402 00403 bool DOS_BreakTest() { 00404 if (DOS_BreakFlag) { 00405 bool terminate = true; 00406 bool terminint23 = false; 00407 Bitu segv,offv; 00408 00409 /* print ^C on the console */ 00410 DOS_PrintCBreak(); 00411 00412 DOS_BreakFlag = false; 00413 00414 offv = mem_readw((0x23*4)+0); 00415 segv = mem_readw((0x23*4)+2); 00416 if (offv != 0 && segv != 0) { /* HACK: DOSBox's shell currently does not assign INT 23h */ 00417 /* NTS: DOS calls are allowed within INT 23h! */ 00418 Bitu save_sp = reg_sp; 00419 00420 /* set carry flag */ 00421 reg_flags |= 1; 00422 00423 /* invoke INT 23h */ 00424 /* NTS: Some DOS programs provide their own INT 23h which then calls INT 21h AH=0x4C 00425 * inside the handler! Set a flag so that if that happens, the termination 00426 * handler will throw us an exception to force our way back here after 00427 * termination completes! 00428 * 00429 * This fixes: PC Mix compiler PCL.EXE 00430 * 00431 * FIXME: This is an ugly hack! */ 00432 try { 00433 DOS_BreakINT23InProgress = true; 00434 CALLBACK_RunRealInt(0x23); 00435 DOS_BreakINT23InProgress = false; 00436 } 00437 catch (int x) { 00438 if (x == 0) { 00439 DOS_BreakINT23InProgress = false; 00440 terminint23 = true; 00441 } 00442 else { 00443 LOG_MSG("Unexpected code in INT 23h termination exception\n"); 00444 abort(); 00445 } 00446 } 00447 00448 /* if the INT 23h handler did not already terminate itself... */ 00449 if (!terminint23) { 00450 /* if it returned with IRET, or with RETF and CF=0, don't terminate */ 00451 if (reg_sp == save_sp || (reg_flags & 1) == 0) { 00452 terminate = false; 00453 LOG_MSG("Note: DOS handler does not wish to terminate\n"); 00454 } 00455 else { 00456 /* program does not wish to continue. it used RETF. pop the remaining flags off */ 00457 LOG_MSG("Note: DOS handler does wish to terminate\n"); 00458 } 00459 00460 if (reg_sp != save_sp) reg_sp += 2; 00461 } 00462 } 00463 00464 if (terminate) { 00465 LOG_MSG("Note: DOS break terminating program\n"); 00466 DOS_Terminate(dos.psp(),false,0); 00467 return false; 00468 } 00469 else if (terminint23) { 00470 LOG_MSG("Note: DOS break handler terminated program for us.\n"); 00471 return false; 00472 } 00473 } 00474 00475 return true; 00476 } 00477 00478 void DOS_BreakAction() { 00479 DOS_BreakFlag = true; 00480 } 00481 00482 /* unmask IRQ 0 automatically on disk I/O functions. 00483 * there exist old DOS games and demos that rely on very selective IRQ masking, 00484 * but, their code also assumes that calling into DOS or the BIOS will unmask the IRQ. 00485 * 00486 * This fixes "Rebel by Arkham" which masks IRQ 0-7 (PIC port 21h) in a VERY stingy manner! 00487 * 00488 * Pseudocode (early in demo init): 00489 * 00490 * in al,21h 00491 * or al,3Bh ; mask IRQ 0, 1, 3, 4, and 5 00492 * out 21h,al 00493 * 00494 * Later: 00495 * 00496 * mov ah,3Dh ; open file 00497 * ... 00498 * int 21h 00499 * ... ; demo apparently assumes that INT 21h will unmask IRQ 0 when reading, because .... 00500 * in al,21h 00501 * or al,3Ah ; mask IRQ 1, 3, 4, and 5 00502 * out 21h,al 00503 * 00504 * The demo runs fine anyway, but if we do not unmask IRQ 0 at the INT 21h call, the timer never ticks and the 00505 * demo does not play any music (goldplay style, of course). 00506 * 00507 * This means several things. One is that a disk cache (which may provide the file without using INT 13h) could 00508 * mysteriously prevent the demo from playing music. Future OS changes, where IRQ unmasking during INT 21h could 00509 * not occur, would also prevent it from working. I don't know what the programmer was thinking, but side 00510 * effects like that are not to be relied on! 00511 * 00512 * On the other hand, perhaps masking the keyboard (IRQ 1) was intended as an anti-debugger trick? You can't break 00513 * into the demo if you can't trigger the debugger, after all! The demo can still poll the keyboard controller 00514 * for ESC or whatever. 00515 * 00516 * --J.C. */ 00517 bool disk_io_unmask_irq0 = true; 00518 00520 bool dos_program_running = false; 00521 00522 void XMS_DOS_LocalA20EnableIfNotEnabled(void); 00523 00524 typedef struct { 00525 UINT16 size_of_structure; 00526 UINT16 structure_version; 00527 UINT32 sectors_per_cluster; 00528 UINT32 bytes_per_sector; 00529 UINT32 available_clusters_on_drive; 00530 UINT32 total_clusters_on_drive; 00531 UINT32 available_sectors_on_drive; 00532 UINT32 total_sectors_on_drive; 00533 UINT32 available_allocation_units; 00534 UINT32 total_allocation_units; 00535 UINT8 reserved[8]; 00536 } ext_space_info_t; 00537 00538 #define DOSNAMEBUF 256 00539 static Bitu DOS_21Handler(void) { 00540 bool unmask_irq0 = false; 00541 00542 /* NTS to ognjenmi: Your INT 21h logging patch was modified to log ALL INT 21h calls (the original 00543 * placement put it after the ignore case below), and is now conditional on 00544 * whether INT 21h logging is enabled. Also removed unnecessary copying of reg_al 00545 * and reg_ah to auto type variables. */ 00546 if (log_int21) { 00547 LOG(LOG_CPU, LOG_DEBUG)("Executing interrupt 21, ah=%x, al=%x", reg_ah, reg_al); 00548 } 00549 00550 /* Real MS-DOS behavior: 00551 * If HIMEM.SYS is loaded and CONFIG.SYS says DOS=HIGH, DOS will load itself into the HMA area. 00552 * To prevent crashes, the INT 21h handler down below will enable the A20 gate before executing 00553 * the DOS kernel. */ 00554 if (DOS_IS_IN_HMA()) 00555 XMS_DOS_LocalA20EnableIfNotEnabled(); 00556 00557 if (((reg_ah != 0x50) && (reg_ah != 0x51) && (reg_ah != 0x62) && (reg_ah != 0x64)) && (reg_ah<0x6c)) { 00558 DOS_PSP psp(dos.psp()); 00559 psp.SetStack(RealMake(SegValue(ss),reg_sp-18)); 00560 } 00561 00562 if (((reg_ah >= 0x01 && reg_ah <= 0x0C) || (reg_ah != 0 && reg_ah != 0x4C && reg_ah != 0x31 && dos.breakcheck)) && !DOS_BreakTest()) return CBRET_NONE; 00563 00564 char name1[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; 00565 char name2[DOSNAMEBUF+2+DOS_NAMELENGTH_ASCII]; 00566 00567 static Bitu time_start = 0; //For emulating temporary time changes. 00568 if (reg_ah!=0x4c) packerr=false; 00569 switch (reg_ah) { 00570 case 0x00: /* Terminate Program */ 00571 /* HACK for demoscene prod parties/1995/wired95/surprisecode/w95spcod.zip/WINNERS/SURP-KLF 00572 * 00573 * This demo starts off by popping 3 words off the stack (the third into ES to get the top 00574 * of DOS memory which it then uses to draw into VGA memory). Since SP starts out at 0xFFFE, 00575 * that means SP wraps around to start popping values out of the PSP segment. 00576 * 00577 * Real MS-DOS will also start the demo with SP at 0xFFFE. 00578 * 00579 * The demo terminates with INT 20h. 00580 * 00581 * This code will fail since the stack pointer must wrap back around to read the segment, 00582 * unless we read by popping. */ 00583 if (reg_sp > 0xFFFA) { 00584 LOG(LOG_DOSMISC,LOG_WARN)("DOS:INT 20h/INT 21h AH=00h WARNING, process terminated where stack pointer wrapped around 64K"); 00585 00586 uint16_t f_ip = CPU_Pop16(); 00587 uint16_t f_cs = CPU_Pop16(); 00588 uint16_t f_flags = CPU_Pop16(); 00589 00590 (void)f_flags; 00591 (void)f_ip; 00592 00593 LOG(LOG_DOSMISC,LOG_DEBUG)("DOS:INT 20h/INT 21h AH=00h recovered CS segment %04x",f_cs); 00594 00595 DOS_Terminate(f_cs,false,0); 00596 } else if (dos.version.major >= 7 && uselfn && mem_readw(SegPhys(ss)+reg_sp) >=0x2700 && mem_readw(SegPhys(ss)+reg_sp+2)/0x100 == 0x90 && dos.psp()/0x100 >= 0xCC) 00597 /* Wengier: This case fixes the bug that DIR /S from MS-DOS 7+ could crash hard within DOSBox-X. With this change it should now work properly. */ 00598 DOS_Terminate(dos.psp(),false,0); 00599 else 00600 DOS_Terminate(mem_readw(SegPhys(ss)+reg_sp+2),false,0); 00601 00602 if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ 00603 dos_program_running = false; 00604 break; 00605 case 0x01: /* Read character from STDIN, with echo */ 00606 { 00607 Bit8u c;Bit16u n=1; 00608 dos.echo=true; 00609 DOS_ReadFile(STDIN,&c,&n); 00610 if (c == 3) { 00611 DOS_BreakAction(); 00612 if (!DOS_BreakTest()) return CBRET_NONE; 00613 } 00614 reg_al=c; 00615 dos.echo=false; 00616 } 00617 break; 00618 case 0x02: /* Write character to STDOUT */ 00619 { 00620 Bit8u c=reg_dl;Bit16u n=1; 00621 DOS_WriteFile(STDOUT,&c,&n); 00622 //Not in the official specs, but happens nonetheless. (last written character) 00623 reg_al=(c==9)?0x20:c; //strangely, tab conversion to spaces is reflected here 00624 } 00625 break; 00626 case 0x03: /* Read character from STDAUX */ 00627 { 00628 Bit16u port = real_readw(0x40,0); 00629 if(port!=0 && serialports[0]) { 00630 Bit8u status; 00631 // RTS/DTR on 00632 IO_WriteB((Bitu)port + 4u, 0x3u); 00633 serialports[0]->Getchar(®_al, &status, true, 0xFFFFFFFF); 00634 } 00635 } 00636 break; 00637 case 0x04: /* Write Character to STDAUX */ 00638 { 00639 Bit16u port = real_readw(0x40,0); 00640 if(port!=0 && serialports[0]) { 00641 // RTS/DTR on 00642 IO_WriteB((Bitu)port + 4u, 0x3u); 00643 serialports[0]->Putchar(reg_dl,true,true, 0xFFFFFFFF); 00644 // RTS off 00645 IO_WriteB((Bitu)port + 4u, 0x1u); 00646 } 00647 } 00648 break; 00649 case 0x05: /* Write Character to PRINTER */ 00650 { 00651 for(unsigned int i = 0; i < 3; i++) { 00652 // look up a parallel port 00653 if(parallelPortObjects[i] != NULL) { 00654 parallelPortObjects[i]->Putchar(reg_dl); 00655 break; 00656 } 00657 } 00658 break; 00659 } 00660 case 0x06: /* Direct Console Output / Input */ 00661 switch (reg_dl) { 00662 case 0xFF: /* Input */ 00663 { 00664 //Simulate DOS overhead for timing sensitive games 00665 //MM1 00666 overhead(); 00667 //TODO Make this better according to standards 00668 if (!DOS_GetSTDINStatus()) { 00669 reg_al=0; 00670 CALLBACK_SZF(true); 00671 break; 00672 } 00673 Bit8u c;Bit16u n=1; 00674 DOS_ReadFile(STDIN,&c,&n); 00675 reg_al=c; 00676 CALLBACK_SZF(false); 00677 break; 00678 } 00679 default: 00680 { 00681 Bit8u c = reg_dl;Bit16u n = 1; 00682 dos.direct_output=true; 00683 DOS_WriteFile(STDOUT,&c,&n); 00684 dos.direct_output=false; 00685 reg_al=c; 00686 } 00687 break; 00688 } 00689 break; 00690 case 0x07: /* Character Input, without echo */ 00691 { 00692 Bit8u c;Bit16u n=1; 00693 DOS_ReadFile (STDIN,&c,&n); 00694 reg_al=c; 00695 break; 00696 } 00697 case 0x08: /* Direct Character Input, without echo (checks for breaks officially :)*/ 00698 { 00699 Bit8u c;Bit16u n=1; 00700 DOS_ReadFile (STDIN,&c,&n); 00701 if (c == 3) { 00702 DOS_BreakAction(); 00703 if (!DOS_BreakTest()) return CBRET_NONE; 00704 } 00705 reg_al=c; 00706 break; 00707 } 00708 case 0x09: /* Write string to STDOUT */ 00709 { 00710 Bit8u c;Bit16u n=1; 00711 PhysPt buf=SegPhys(ds)+reg_dx; 00712 while ((c=mem_readb(buf++))!='$') { 00713 DOS_WriteFile(STDOUT,&c,&n); 00714 } 00715 reg_al=c; 00716 } 00717 break; 00718 case 0x0a: /* Buffered Input */ 00719 { 00720 //TODO ADD Break checkin in STDIN but can't care that much for it 00721 PhysPt data=SegPhys(ds)+reg_dx; 00722 Bit8u free=mem_readb(data); 00723 Bit8u read=0;Bit8u c;Bit16u n=1; 00724 if (!free) break; 00725 free--; 00726 for(;;) { 00727 if (!DOS_BreakTest()) return CBRET_NONE; 00728 DOS_ReadFile(STDIN,&c,&n); 00729 if (n == 0) // End of file 00730 E_Exit("DOS:0x0a:Redirected input reached EOF"); 00731 if (c == 10) // Line feed 00732 continue; 00733 if (c == 8) { // Backspace 00734 if (read) { //Something to backspace. 00735 // STDOUT treats backspace as non-destructive. 00736 DOS_WriteFile(STDOUT,&c,&n); 00737 c = ' '; DOS_WriteFile(STDOUT,&c,&n); 00738 c = 8; DOS_WriteFile(STDOUT,&c,&n); 00739 --read; 00740 } 00741 continue; 00742 } 00743 if (c == 3) { // CTRL+C 00744 DOS_BreakAction(); 00745 if (!DOS_BreakTest()) return CBRET_NONE; 00746 } 00747 if (read == free && c != 13) { // Keyboard buffer full 00748 Bit8u bell = 7; 00749 DOS_WriteFile(STDOUT, &bell, &n); 00750 continue; 00751 } 00752 DOS_WriteFile(STDOUT,&c,&n); 00753 mem_writeb(data+read+2,c); 00754 if (c==13) 00755 break; 00756 read++; 00757 } 00758 mem_writeb(data+1,read); 00759 break; 00760 } 00761 case 0x0b: /* Get STDIN Status */ 00762 if (!DOS_GetSTDINStatus()) {reg_al=0x00;} 00763 else {reg_al=0xFF;} 00764 //Simulate some overhead for timing issues 00765 //Tankwar menu (needs maybe even more) 00766 overhead(); 00767 break; 00768 case 0x0c: /* Flush Buffer and read STDIN call */ 00769 { 00770 /* flush buffer if STDIN is CON */ 00771 Bit8u handle=RealHandle(STDIN); 00772 if (handle!=0xFF && Files[handle] && Files[handle]->IsName("CON")) { 00773 Bit8u c;Bit16u n; 00774 while (DOS_GetSTDINStatus()) { 00775 n=1; DOS_ReadFile(STDIN,&c,&n); 00776 } 00777 } 00778 switch (reg_al) { 00779 case 0x1: 00780 case 0x6: 00781 case 0x7: 00782 case 0x8: 00783 case 0xa: 00784 { 00785 Bit8u oldah=reg_ah; 00786 reg_ah=reg_al; 00787 DOS_21Handler(); 00788 reg_ah=oldah; 00789 } 00790 break; 00791 default: 00792 // LOG_ERROR("DOS:0C:Illegal Flush STDIN Buffer call %d",reg_al); 00793 reg_al=0; 00794 break; 00795 } 00796 } 00797 break; 00798 //TODO Find out the values for when reg_al!=0 00799 //TODO Hope this doesn't do anything special 00800 case 0x0d: /* Disk Reset */ 00801 //Sure let's reset a virtual disk 00802 break; 00803 case 0x0e: /* Select Default Drive */ 00804 DOS_SetDefaultDrive(reg_dl); 00805 reg_al=DOS_DRIVES; 00806 break; 00807 case 0x0f: /* Open File using FCB */ 00808 if(DOS_FCBOpen(SegValue(ds),reg_dx)){ 00809 reg_al=0; 00810 }else{ 00811 reg_al=0xff; 00812 } 00813 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x0f FCB-fileopen used, result:al=%d",reg_al); 00814 break; 00815 case 0x10: /* Close File using FCB */ 00816 if(DOS_FCBClose(SegValue(ds),reg_dx)){ 00817 reg_al=0; 00818 }else{ 00819 reg_al=0xff; 00820 } 00821 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x10 FCB-fileclose used, result:al=%d",reg_al); 00822 break; 00823 case 0x11: /* Find First Matching File using FCB */ 00824 if(DOS_FCBFindFirst(SegValue(ds),reg_dx)) reg_al = 0x00; 00825 else reg_al = 0xFF; 00826 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x11 FCB-FindFirst used, result:al=%d",reg_al); 00827 break; 00828 case 0x12: /* Find Next Matching File using FCB */ 00829 if(DOS_FCBFindNext(SegValue(ds),reg_dx)) reg_al = 0x00; 00830 else reg_al = 0xFF; 00831 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x12 FCB-FindNext used, result:al=%d",reg_al); 00832 break; 00833 case 0x13: /* Delete File using FCB */ 00834 if (DOS_FCBDeleteFile(SegValue(ds),reg_dx)) reg_al = 0x00; 00835 else reg_al = 0xFF; 00836 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Delete used, result:al=%d",reg_al); 00837 break; 00838 case 0x14: /* Sequential read from FCB */ 00839 reg_al = DOS_FCBRead(SegValue(ds),reg_dx,0); 00840 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x14 FCB-Read used, result:al=%d",reg_al); 00841 break; 00842 case 0x15: /* Sequential write to FCB */ 00843 reg_al=DOS_FCBWrite(SegValue(ds),reg_dx,0); 00844 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x15 FCB-Write used, result:al=%d",reg_al); 00845 break; 00846 case 0x16: /* Create or truncate file using FCB */ 00847 if (DOS_FCBCreate(SegValue(ds),reg_dx)) reg_al = 0x00; 00848 else reg_al = 0xFF; 00849 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x16 FCB-Create used, result:al=%d",reg_al); 00850 break; 00851 case 0x17: /* Rename file using FCB */ 00852 if (DOS_FCBRenameFile(SegValue(ds),reg_dx)) reg_al = 0x00; 00853 else reg_al = 0xFF; 00854 break; 00855 case 0x18: /* NULL Function for CP/M compatibility or Extended rename FCB */ 00856 goto default_fallthrough; 00857 case 0x19: /* Get current default drive */ 00858 reg_al = DOS_GetDefaultDrive(); 00859 break; 00860 case 0x1a: /* Set Disk Transfer Area Address */ 00861 dos.dta(RealMakeSeg(ds, reg_dx)); 00862 break; 00863 case 0x1b: /* Get allocation info for default drive */ 00864 if (!DOS_GetAllocationInfo(0,®_cx,®_al,®_dx)) reg_al=0xff; 00865 break; 00866 case 0x1c: /* Get allocation info for specific drive */ 00867 if (!DOS_GetAllocationInfo(reg_dl,®_cx,®_al,®_dx)) reg_al=0xff; 00868 break; 00869 case 0x1d: /* NULL Function for CP/M compatibility or Extended rename FCB */ 00870 goto default_fallthrough; 00871 case 0x1e: /* NULL Function for CP/M compatibility or Extended rename FCB */ 00872 goto default_fallthrough; 00873 case 0x1f: /* Get drive parameter block for default drive */ 00874 goto case_0x32_fallthrough; 00875 case 0x20: /* NULL Function for CP/M compatibility or Extended rename FCB */ 00876 goto default_fallthrough; 00877 case 0x21: /* Read random record from FCB */ 00878 { 00879 Bit16u toread=1; 00880 reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,&toread,true); 00881 } 00882 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x21 FCB-Random read used, result:al=%d",reg_al); 00883 break; 00884 case 0x22: /* Write random record to FCB */ 00885 { 00886 Bit16u towrite=1; 00887 reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,&towrite,true); 00888 } 00889 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x22 FCB-Random write used, result:al=%d",reg_al); 00890 break; 00891 case 0x23: /* Get file size for FCB */ 00892 if (DOS_FCBGetFileSize(SegValue(ds),reg_dx)) reg_al = 0x00; 00893 else reg_al = 0xFF; 00894 break; 00895 case 0x24: /* Set Random Record number for FCB */ 00896 DOS_FCBSetRandomRecord(SegValue(ds),reg_dx); 00897 break; 00898 case 0x25: /* Set Interrupt Vector */ 00899 RealSetVec(reg_al, RealMakeSeg(ds, reg_dx)); 00900 break; 00901 case 0x26: /* Create new PSP */ 00902 /* TODO: DEBUG.EXE/DEBUG.COM as shipped with MS-DOS seems to reveal a bug where, 00903 * when DEBUG.EXE calls this function and you're NOT loading a program to debug, 00904 * the CP/M CALL FAR instruction's offset field will be off by 2. When does 00905 * that happen, and how do we emulate that? */ 00906 DOS_NewPSP(reg_dx, DOS_PSP(dos.psp()).GetSize()); 00907 reg_al = 0xf0; /* al destroyed */ 00908 break; 00909 case 0x27: /* Random block read from FCB */ 00910 reg_al = DOS_FCBRandomRead(SegValue(ds),reg_dx,®_cx,false); 00911 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x27 FCB-Random(block) read used, result:al=%d",reg_al); 00912 break; 00913 case 0x28: /* Random Block write to FCB */ 00914 reg_al=DOS_FCBRandomWrite(SegValue(ds),reg_dx,®_cx,false); 00915 LOG(LOG_FCB,LOG_NORMAL)("DOS:0x28 FCB-Random(block) write used, result:al=%d",reg_al); 00916 break; 00917 case 0x29: /* Parse filename into FCB */ 00918 { 00919 Bit8u difference; 00920 char string[1024]; 00921 MEM_StrCopy(SegPhys(ds)+reg_si,string,1023); // 1024 toasts the stack 00922 reg_al=FCB_Parsename(SegValue(es),reg_di,reg_al ,string, &difference); 00923 reg_si+=difference; 00924 } 00925 LOG(LOG_FCB,LOG_NORMAL)("DOS:29:FCB Parse Filename, result:al=%d",reg_al); 00926 break; 00927 case 0x2a: /* Get System Date */ 00928 { 00929 if(date_host_forced || IS_PC98_ARCH) { 00930 // use BIOS to get system date 00931 if (IS_PC98_ARCH) { 00932 CPU_Push16(reg_ax); 00933 CPU_Push16(reg_bx); 00934 CPU_Push16(SegValue(es)); 00935 reg_sp -= 6; 00936 00937 reg_ah = 0; // get time 00938 reg_bx = reg_sp; 00939 SegSet16(es,SegValue(ss)); 00940 CALLBACK_RunRealInt(0x1c); 00941 00942 Bit32u memaddr = ((Bit32u)SegValue(es) << 4u) + reg_bx; 00943 00944 reg_sp += 6; 00945 SegSet16(es,CPU_Pop16()); 00946 reg_bx = CPU_Pop16(); 00947 reg_ax = CPU_Pop16(); 00948 00949 reg_cx = 1900u + BCD2BIN(mem_readb(memaddr+0u)); // year 00950 if (reg_cx < 1980u) reg_cx += 100u; 00951 reg_dh = BCD2BIN((unsigned int)mem_readb(memaddr+1) >> 4u); 00952 reg_dl = BCD2BIN(mem_readb(memaddr+2)); 00953 reg_al = BCD2BIN(mem_readb(memaddr+1) & 0xFu); 00954 } 00955 else { 00956 CPU_Push16(reg_ax); 00957 reg_ah = 4; // get RTC date 00958 CALLBACK_RunRealInt(0x1a); 00959 reg_ax = CPU_Pop16(); 00960 00961 reg_ch = BCD2BIN(reg_ch); // century 00962 reg_cl = BCD2BIN(reg_cl); // year 00963 reg_cx = reg_ch * 100u + reg_cl; // compose century + year 00964 reg_dh = BCD2BIN(reg_dh); // month 00965 reg_dl = BCD2BIN(reg_dl); // day 00966 00967 // calculate day of week (we could of course read it from CMOS, but never mind) 00968 unsigned int a = (14u - reg_dh) / 12u; 00969 unsigned int y = reg_cl - a; 00970 unsigned int m = reg_dh + 12u * a - 2u; 00971 reg_al = (reg_dl + y + (y / 4u) - (y / 100u) + (y / 400u) + (31u * m) / 12u) % 7u; 00972 } 00973 } else { 00974 reg_ax=0; // get time 00975 CALLBACK_RunRealInt(0x1a); 00976 if(reg_al) DOS_AddDays(reg_al); 00977 int a = (14 - dos.date.month)/12; 00978 int y = dos.date.year - a; 00979 int m = dos.date.month + 12*a - 2; 00980 reg_al=(dos.date.day+y+(y/4)-(y/100)+(y/400)+(31*m)/12) % 7; 00981 reg_cx=dos.date.year; 00982 reg_dh=dos.date.month; 00983 reg_dl=dos.date.day; 00984 } 00985 } 00986 break; 00987 case 0x2b: /* Set System Date */ 00988 if(date_host_forced) { 00989 // unfortunately, BIOS does not return whether succeeded 00990 // or not, so do a sanity check first 00991 00992 int maxday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 00993 00994 if (reg_cx % 4 == 0 && (reg_cx % 100 != 0 || reg_cx % 400 == 0)) 00995 maxday[1]++; 00996 00997 if (reg_cx < 1980 || reg_cx > 9999 || reg_dh < 1 || reg_dh > 12 || 00998 reg_dl < 1 || reg_dl > maxday[reg_dh]) 00999 { 01000 reg_al = 0xff; // error! 01001 break; // done 01002 } 01003 01004 Bit16u cx = reg_cx; 01005 01006 CPU_Push16(reg_ax); 01007 CPU_Push16(reg_cx); 01008 CPU_Push16(reg_dx); 01009 01010 reg_al = 5; 01011 reg_ch = BIN2BCD(cx / 100); // century 01012 reg_cl = BIN2BCD(cx % 100); // year 01013 reg_dh = BIN2BCD(reg_dh); // month 01014 reg_dl = BIN2BCD(reg_dl); // day 01015 01016 CALLBACK_RunRealInt(0x1a); 01017 01018 reg_dx = CPU_Pop16(); 01019 reg_cx = CPU_Pop16(); 01020 reg_ax = CPU_Pop16(); 01021 01022 reg_al = 0; // OK 01023 break; 01024 } 01025 if (reg_cx<1980) { reg_al=0xff;break;} 01026 if ((reg_dh>12) || (reg_dh==0)) { reg_al=0xff;break;} 01027 if (reg_dl==0) { reg_al=0xff;break;} 01028 if (reg_dl>DOS_DATE_months[reg_dh]) { 01029 if(!((reg_dh==2)&&(reg_cx%4 == 0)&&(reg_dl==29))) // february pass 01030 { reg_al=0xff;break; } 01031 } 01032 dos.date.year=reg_cx; 01033 dos.date.month=reg_dh; 01034 dos.date.day=reg_dl; 01035 reg_al=0; 01036 break; 01037 case 0x2c: { /* Get System Time */ 01038 if(date_host_forced || IS_PC98_ARCH) { 01039 // use BIOS to get RTC time 01040 if (IS_PC98_ARCH) { 01041 CPU_Push16(reg_ax); 01042 CPU_Push16(reg_bx); 01043 CPU_Push16(SegValue(es)); 01044 reg_sp -= 6; 01045 01046 reg_ah = 0; // get time 01047 reg_bx = reg_sp; 01048 SegSet16(es,SegValue(ss)); 01049 CALLBACK_RunRealInt(0x1c); 01050 01051 Bit32u memaddr = ((PhysPt)SegValue(es) << 4u) + reg_bx; 01052 01053 reg_sp += 6; 01054 SegSet16(es,CPU_Pop16()); 01055 reg_bx = CPU_Pop16(); 01056 reg_ax = CPU_Pop16(); 01057 01058 reg_ch = BCD2BIN(mem_readb(memaddr+3)); // hours 01059 reg_cl = BCD2BIN(mem_readb(memaddr+4)); // minutes 01060 reg_dh = BCD2BIN(mem_readb(memaddr+5)); // seconds 01061 01062 reg_dl = 0; 01063 } 01064 else { 01065 CPU_Push16(reg_ax); 01066 01067 reg_ah = 2; // get RTC time 01068 CALLBACK_RunRealInt(0x1a); 01069 01070 reg_ax = CPU_Pop16(); 01071 01072 reg_ch = BCD2BIN(reg_ch); // hours 01073 reg_cl = BCD2BIN(reg_cl); // minutes 01074 reg_dh = BCD2BIN(reg_dh); // seconds 01075 01076 // calculate milliseconds (% 20 to prevent overflow, .55ms has period of 20) 01077 // direcly read BIOS_TIMER, don't want to destroy regs by calling int 1a 01078 reg_dl = (Bit8u)((mem_readd(BIOS_TIMER) % 20) * 55 % 100); 01079 } 01080 break; 01081 } 01082 reg_ax=0; // get time 01083 CALLBACK_RunRealInt(0x1a); 01084 if(reg_al) DOS_AddDays(reg_al); 01085 reg_ah=0x2c; 01086 01087 Bitu ticks=((Bitu)reg_cx<<16)|reg_dx; 01088 if(time_start<=ticks) ticks-=time_start; 01089 Bitu time=(Bitu)((100.0/((double)PIT_TICK_RATE/65536.0)) * (double)ticks); 01090 01091 reg_dl=(Bit8u)((Bitu)time % 100); // 1/100 seconds 01092 time/=100; 01093 reg_dh=(Bit8u)((Bitu)time % 60); // seconds 01094 time/=60; 01095 reg_cl=(Bit8u)((Bitu)time % 60); // minutes 01096 time/=60; 01097 reg_ch=(Bit8u)((Bitu)time % 24); // hours 01098 01099 //Simulate DOS overhead for timing-sensitive games 01100 //Robomaze 2 01101 overhead(); 01102 break; 01103 } 01104 case 0x2d: /* Set System Time */ 01105 if(date_host_forced) { 01106 // unfortunately, BIOS does not return whether succeeded 01107 // or not, so do a sanity check first 01108 if (reg_ch > 23 || reg_cl > 59 || reg_dh > 59 || reg_dl > 99) 01109 { 01110 reg_al = 0xff; // error! 01111 break; // done 01112 } 01113 01114 // timer ticks every 55ms 01115 Bit32u ticks = ((((reg_ch * 60u + reg_cl) * 60u + reg_dh) * 100u) + reg_dl) * 10u / 55u; 01116 01117 CPU_Push16(reg_ax); 01118 CPU_Push16(reg_cx); 01119 CPU_Push16(reg_dx); 01120 01121 // use BIOS to set RTC time 01122 reg_ah = 3; // set RTC time 01123 reg_ch = BIN2BCD(reg_ch); // hours 01124 reg_cl = BIN2BCD(reg_cl); // minutes 01125 reg_dh = BIN2BCD(reg_dh); // seconds 01126 reg_dl = 0; // no DST 01127 01128 CALLBACK_RunRealInt(0x1a); 01129 01130 // use BIOS to update clock ticks to sync time 01131 // could set directly, but setting is safer to do via dedicated call (at least in theory) 01132 reg_ah = 1; // set system time 01133 reg_cx = (Bit16u)(ticks >> 16); 01134 reg_dx = (Bit16u)(ticks & 0xffff); 01135 01136 CALLBACK_RunRealInt(0x1a); 01137 01138 reg_dx = CPU_Pop16(); 01139 reg_cx = CPU_Pop16(); 01140 reg_ax = CPU_Pop16(); 01141 01142 reg_al = 0; // OK 01143 break; 01144 } 01145 //Check input parameters nonetheless 01146 if( reg_ch > 23 || reg_cl > 59 || reg_dh > 59 || reg_dl > 99 ) 01147 reg_al = 0xff; 01148 else { //Allow time to be set to zero. Restore the orginal time for all other parameters. (QuickBasic) 01149 if (reg_cx == 0 && reg_dx == 0) {time_start = mem_readd(BIOS_TIMER);LOG_MSG("Warning: game messes with DOS time!");} 01150 else time_start = 0; 01151 Bit32u ticks=(Bit32u)(((double)(reg_ch*3600+ 01152 reg_cl*60+ 01153 reg_dh))*18.206481481); 01154 mem_writed(BIOS_TIMER,ticks); 01155 reg_al = 0; 01156 } 01157 break; 01158 case 0x2e: /* Set Verify flag */ 01159 dos.verify=(reg_al==1); 01160 break; 01161 case 0x2f: /* Get Disk Transfer Area */ 01162 SegSet16(es,RealSeg(dos.dta())); 01163 reg_bx=RealOff(dos.dta()); 01164 break; 01165 case 0x30: /* Get DOS Version */ 01166 if (reg_al==0) reg_bh=0xFF; /* Fake Microsoft DOS */ 01167 if (reg_al==1 && DOS_IS_IN_HMA()) reg_bh=0x10; /* DOS is in HMA? */ 01168 reg_al=dos.version.major; 01169 reg_ah=dos.version.minor; 01170 /* Serialnumber */ 01171 reg_bl=0x00; 01172 reg_cx=0x0000; 01173 break; 01174 case 0x31: /* Terminate and stay resident */ 01175 // Important: This service does not set the carry flag! 01176 DOS_ResizeMemory(dos.psp(),®_dx); 01177 DOS_Terminate(dos.psp(),true,reg_al); 01178 if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ 01179 dos_program_running = false; 01180 break; 01181 case 0x32: /* Get drive parameter block for specific drive */ 01182 { /* Officially a dpb should be returned as well. The disk detection part is implemented */ 01183 case_0x32_fallthrough: 01184 Bit8u drive=reg_dl; 01185 if (!drive || reg_ah==0x1f) drive = DOS_GetDefaultDrive(); 01186 else drive--; 01187 if (drive < DOS_DRIVES && Drives[drive] && !Drives[drive]->isRemovable()) { 01188 reg_al = 0x00; 01189 SegSet16(ds,dos.tables.dpb); 01190 reg_bx = drive*dos.tables.dpb_size; 01191 LOG(LOG_DOSMISC,LOG_NORMAL)("Get drive parameter block."); 01192 } else { 01193 reg_al=0xff; 01194 } 01195 } 01196 break; 01197 case 0x33: /* Extended Break Checking */ 01198 switch (reg_al) { 01199 case 0:reg_dl=dos.breakcheck;break; /* Get the breakcheck flag */ 01200 case 1:dos.breakcheck=(reg_dl>0);break; /* Set the breakcheck flag */ 01201 case 2:{bool old=dos.breakcheck;dos.breakcheck=(reg_dl>0);reg_dl=old;}break; 01202 case 3: /* Get cpsw */ 01203 /* Fallthrough */ 01204 case 4: /* Set cpsw */ 01205 LOG(LOG_DOSMISC,LOG_ERROR)("Someone playing with cpsw %x",reg_ax); 01206 break; 01207 case 5:reg_dl=3;break;//TODO should be z /* Always boot from c: :) */ 01208 case 6: /* Get true version number */ 01209 reg_bl=dos.version.major; 01210 reg_bh=dos.version.minor; 01211 reg_dl=dos.version.revision; 01212 reg_dh=DOS_IS_IN_HMA()?0x10:0x00; /* Dos in HMA?? */ 01213 break; 01214 case 7: 01215 break; 01216 default: 01217 LOG(LOG_DOSMISC,LOG_ERROR)("Weird 0x33 call %2X",reg_al); 01218 reg_al =0xff; 01219 break; 01220 } 01221 break; 01222 case 0x34: /* Get INDos Flag */ 01223 SegSet16(es,DOS_SDA_SEG); 01224 reg_bx=DOS_SDA_OFS + 0x01; 01225 break; 01226 case 0x35: /* Get interrupt vector */ 01227 reg_bx=real_readw(0,((Bit16u)reg_al)*4); 01228 SegSet16(es,real_readw(0,((Bit16u)reg_al)*4+2)); 01229 break; 01230 case 0x36: /* Get Free Disk Space */ 01231 { 01232 Bit16u bytes,clusters,free; 01233 Bit8u sectors; 01234 if (DOS_GetFreeDiskSpace(reg_dl,&bytes,§ors,&clusters,&free)) { 01235 reg_ax=sectors; 01236 reg_bx=free; 01237 reg_cx=bytes; 01238 reg_dx=clusters; 01239 } else { 01240 Bit8u drive=reg_dl; 01241 if (drive==0) drive=DOS_GetDefaultDrive(); 01242 else drive--; 01243 if (drive<2) { 01244 // floppy drive, non-present drivesdisks issue floppy check through int24 01245 // (critical error handler); needed for Mixed up Mother Goose (hook) 01246 // CALLBACK_RunRealInt(0x24); 01247 } 01248 reg_ax=0xffff; // invalid drive specified 01249 } 01250 } 01251 break; 01252 case 0x37: /* Get/Set Switch char Get/Set Availdev thing */ 01253 //TODO Give errors for these functions to see if anyone actually uses this shit- 01254 switch (reg_al) { 01255 case 0: 01256 reg_al=0;reg_dl=0x2f;break; /* always return '/' like dos 5.0+ */ 01257 case 1: 01258 LOG(LOG_MISC,LOG_DEBUG)("DOS:0x37:Attempted to set switch char"); 01259 reg_al=0;break; 01260 case 2: 01261 reg_al=0;reg_dl=0xff;break; /* AVAILDEV \DEV\ prefix optional */ 01262 case 3: 01263 LOG(LOG_MISC,LOG_DEBUG)("DOS:0x37:Attempted to set AVAILDEV \\DEV\\ prefix use"); 01264 reg_al=0;break; 01265 } 01266 break; 01267 case 0x38: /* Set Country Code */ 01268 if (reg_al==0) { /* Get country specidic information */ 01269 PhysPt dest = SegPhys(ds)+reg_dx; 01270 MEM_BlockWrite(dest,dos.tables.country,0x18); 01271 reg_al = countryNo<0xff?countryNo:0xff; 01272 reg_bx = countryNo; 01273 CALLBACK_SCF(false); 01274 break; 01275 } else if (reg_dx == 0xffff) { /* Set country code */ 01276 countryNo = reg_al==0xff?reg_bx:reg_al; 01277 DOS_SetCountry(countryNo); 01278 reg_ax = 0; 01279 CALLBACK_SCF(false); 01280 break; 01281 } 01282 CALLBACK_SCF(true); 01283 break; 01284 case 0x39: /* MKDIR Create directory */ 01285 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01286 force_sfn = true; 01287 if (DOS_MakeDir(name1)) { 01288 reg_ax=0x05; /* ax destroyed */ 01289 CALLBACK_SCF(false); 01290 } else { 01291 reg_ax=dos.errorcode; 01292 CALLBACK_SCF(true); 01293 } 01294 force_sfn = false; 01295 break; 01296 case 0x3a: /* RMDIR Remove directory */ 01297 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01298 force_sfn = true; 01299 if (DOS_RemoveDir(name1)) { 01300 reg_ax=0x05; /* ax destroyed */ 01301 CALLBACK_SCF(false); 01302 } else { 01303 reg_ax=dos.errorcode; 01304 CALLBACK_SCF(true); 01305 LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode); 01306 } 01307 force_sfn = false; 01308 break; 01309 case 0x3b: /* CHDIR Set current directory */ 01310 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01311 if (DOS_ChangeDir(name1)) { 01312 reg_ax=0x00; /* ax destroyed */ 01313 CALLBACK_SCF(false); 01314 } else { 01315 reg_ax=dos.errorcode; 01316 CALLBACK_SCF(true); 01317 } 01318 break; 01319 case 0x3c: /* CREATE Create or truncate file */ 01320 force_sfn = true; 01321 unmask_irq0 |= disk_io_unmask_irq0; 01322 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01323 if (DOS_CreateFile(name1,reg_cx,®_ax)) { 01324 CALLBACK_SCF(false); 01325 } else { 01326 reg_ax=dos.errorcode; 01327 CALLBACK_SCF(true); 01328 } 01329 diskio_delay(2048); 01330 force_sfn = false; 01331 break; 01332 case 0x3d: /* OPEN Open existing file */ 01333 { 01334 unmask_irq0 |= disk_io_unmask_irq0; 01335 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01336 Bit8u oldal=reg_al; 01337 force_sfn = true; 01338 if (DOS_OpenFile(name1,reg_al,®_ax)) { 01339 force_sfn = false; 01340 CALLBACK_SCF(false); 01341 } else { 01342 force_sfn = false; 01343 if (uselfn&&DOS_OpenFile(name1,oldal,®_ax)) { 01344 CALLBACK_SCF(false); 01345 break; 01346 } 01347 reg_ax=dos.errorcode; 01348 CALLBACK_SCF(true); 01349 } 01350 diskio_delay(1024); 01351 force_sfn = false; 01352 break; 01353 } 01354 case 0x3e: /* CLOSE Close file */ 01355 unmask_irq0 |= disk_io_unmask_irq0; 01356 if (DOS_CloseFile(reg_bx)) { 01357 // reg_al=0x01; /* al destroyed. Refcount */ 01358 CALLBACK_SCF(false); 01359 } else { 01360 reg_ax=dos.errorcode; 01361 CALLBACK_SCF(true); 01362 } 01363 diskio_delay(512); 01364 break; 01365 case 0x3f: /* READ Read from file or device */ 01366 unmask_irq0 |= disk_io_unmask_irq0; 01367 /* TODO: If handle is STDIN and not binary do CTRL+C checking */ 01368 { 01369 Bit16u toread=reg_cx; 01370 01371 /* if the offset and size exceed the end of the 64KB segment, 01372 * truncate the read according to observed MS-DOS 5.0 behavior 01373 * where the actual byte count read is 64KB minus (reg_dx % 16). 01374 * 01375 * This is needed for "Dark Purpose" to read it's DAT file 01376 * correctly, which calls INT 21h AH=3Fh with DX=0004h and CX=FFFFh 01377 * and will mis-render it's fonts, images, and color palettes 01378 * if we do not do this. 01379 * 01380 * Ref: http://files.scene.org/get/mirrors/hornet/demos/1995/d/darkp.zip */ 01381 if (((uint32_t)toread+(uint32_t)reg_dx) > 0xFFFFUL && (reg_dx & 0xFU) != 0U) { 01382 Bit16u nuread = (Bit16u)(0x10000UL - (reg_dx & 0xF)); /* FIXME: If MS-DOS 5.0 truncates it any farther I need to know! */ 01383 01384 if (nuread > toread) nuread = toread; 01385 LOG_MSG("INT 21h READ warning: DX=%04xh CX=%04xh exceeds 64KB, truncating to %04xh",reg_dx,toread,nuread); 01386 toread = nuread; 01387 } 01388 01389 dos.echo=true; 01390 if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) { 01391 MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread); 01392 reg_ax=toread; 01393 CALLBACK_SCF(false); 01394 } else if (dos.errorcode==77) { 01395 DOS_BreakFlag = true; 01396 if (!DOS_BreakTest()) { 01397 dos.echo = false; 01398 return CBRET_NONE; 01399 } else { 01400 reg_ax=dos.errorcode; 01401 CALLBACK_SCF(true); 01402 } 01403 } 01404 diskio_delay(reg_ax); 01405 dos.echo=false; 01406 break; 01407 } 01408 case 0x40: /* WRITE Write to file or device */ 01409 unmask_irq0 |= disk_io_unmask_irq0; 01410 { 01411 Bit16u towrite=reg_cx; 01412 01413 /* if the offset and size exceed the end of the 64KB segment, 01414 * truncate the write according to observed MS-DOS 5.0 READ behavior 01415 * where the actual byte count written is 64KB minus (reg_dx % 16). 01416 * 01417 * This is copy-paste of AH=3Fh read handling because it's likely 01418 * that MS-DOS probably does the same with write as well, though 01419 * this has not yet been confirmed. --J.C. */ 01420 if (((uint32_t)towrite+(uint32_t)reg_dx) > 0xFFFFUL && (reg_dx & 0xFU) != 0U) { 01421 Bit16u nuwrite = (Bit16u)(0x10000UL - (reg_dx & 0xF)); /* FIXME: If MS-DOS 5.0 truncates it any farther I need to know! */ 01422 01423 if (nuwrite > towrite) nuwrite = towrite; 01424 LOG_MSG("INT 21h WRITE warning: DX=%04xh CX=%04xh exceeds 64KB, truncating to %04xh",reg_dx,towrite,nuwrite); 01425 towrite = nuwrite; 01426 } 01427 01428 MEM_BlockRead(SegPhys(ds)+reg_dx,dos_copybuf,towrite); 01429 if (reg_bx==2&&towrite==22&&!strncmp((char *)dos_copybuf,"Packed file is corrupt",towrite)) packerr=true; 01430 if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) { 01431 reg_ax=towrite; 01432 CALLBACK_SCF(false); 01433 } else { 01434 reg_ax=dos.errorcode; 01435 CALLBACK_SCF(true); 01436 } 01437 diskio_delay(reg_ax); 01438 break; 01439 } 01440 case 0x41: /* UNLINK Delete file */ 01441 unmask_irq0 |= disk_io_unmask_irq0; 01442 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01443 force_sfn = true; 01444 if (DOS_UnlinkFile(name1)) { 01445 CALLBACK_SCF(false); 01446 } else { 01447 reg_ax=dos.errorcode; 01448 CALLBACK_SCF(true); 01449 } 01450 force_sfn = false; 01451 diskio_delay(1024); 01452 break; 01453 case 0x42: /* LSEEK Set current file position */ 01454 unmask_irq0 |= disk_io_unmask_irq0; 01455 { 01456 Bit32u pos=((Bit32u)reg_cx << 16u) + reg_dx; 01457 if (DOS_SeekFile(reg_bx,&pos,reg_al)) { 01458 reg_dx=(Bit16u)((unsigned int)pos >> 16u); 01459 reg_ax=(Bit16u)(pos & 0xFFFF); 01460 CALLBACK_SCF(false); 01461 } else { 01462 reg_ax=dos.errorcode; 01463 CALLBACK_SCF(true); 01464 } 01465 diskio_delay(32); 01466 break; 01467 } 01468 case 0x43: /* Get/Set file attributes */ 01469 unmask_irq0 |= disk_io_unmask_irq0; 01470 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01471 switch (reg_al) { 01472 case 0x00: /* Get */ 01473 { 01474 Bit16u attr_val=reg_cx; 01475 if (DOS_GetFileAttr(name1,&attr_val)) { 01476 reg_cx=attr_val; 01477 reg_ax=attr_val; /* Undocumented */ 01478 CALLBACK_SCF(false); 01479 } else { 01480 CALLBACK_SCF(true); 01481 reg_ax=dos.errorcode; 01482 } 01483 break; 01484 } 01485 case 0x01: /* Set */ 01486 if (DOS_SetFileAttr(name1,reg_cx)) { 01487 reg_ax=0x202; /* ax destroyed */ 01488 CALLBACK_SCF(false); 01489 } else { 01490 CALLBACK_SCF(true); 01491 reg_ax=dos.errorcode; 01492 } 01493 break; 01494 default: 01495 LOG(LOG_MISC,LOG_ERROR)("DOS:0x43:Illegal subfunction %2X",reg_al); 01496 reg_ax=1; 01497 CALLBACK_SCF(true); 01498 break; 01499 } 01500 break; 01501 case 0x44: /* IOCTL Functions */ 01502 if (DOS_IOCTL()) { 01503 CALLBACK_SCF(false); 01504 } else { 01505 reg_ax=dos.errorcode; 01506 CALLBACK_SCF(true); 01507 } 01508 break; 01509 case 0x45: /* DUP Duplicate file handle */ 01510 if (DOS_DuplicateEntry(reg_bx,®_ax)) { 01511 CALLBACK_SCF(false); 01512 } else { 01513 reg_ax=dos.errorcode; 01514 CALLBACK_SCF(true); 01515 } 01516 break; 01517 case 0x46: /* DUP2,FORCEDUP Force duplicate file handle */ 01518 if (DOS_ForceDuplicateEntry(reg_bx,reg_cx)) { 01519 reg_ax=reg_cx; //Not all sources agree on it. 01520 CALLBACK_SCF(false); 01521 } else { 01522 reg_ax=dos.errorcode; 01523 CALLBACK_SCF(true); 01524 } 01525 break; 01526 case 0x47: /* CWD Get current directory */ 01527 if (DOS_GetCurrentDir(reg_dl,name1,false)) { 01528 MEM_BlockWrite(SegPhys(ds)+reg_si,name1,(Bitu)(strlen(name1)+1)); 01529 reg_ax=0x0100; 01530 CALLBACK_SCF(false); 01531 } else { 01532 reg_ax=dos.errorcode; 01533 CALLBACK_SCF(true); 01534 } 01535 break; 01536 case 0x48: /* Allocate memory */ 01537 { 01538 Bit16u size=reg_bx;Bit16u seg; 01539 if (DOS_AllocateMemory(&seg,&size)) { 01540 reg_ax=seg; 01541 CALLBACK_SCF(false); 01542 } else { 01543 reg_ax=dos.errorcode; 01544 reg_bx=size; 01545 CALLBACK_SCF(true); 01546 } 01547 break; 01548 } 01549 case 0x49: /* Free memory */ 01550 if (DOS_FreeMemory(SegValue(es))) { 01551 CALLBACK_SCF(false); 01552 } else { 01553 reg_ax=dos.errorcode; 01554 CALLBACK_SCF(true); 01555 } 01556 break; 01557 case 0x4a: /* Resize memory block */ 01558 { 01559 Bit16u size=reg_bx; 01560 if (DOS_ResizeMemory(SegValue(es),&size)) { 01561 reg_ax=SegValue(es); 01562 CALLBACK_SCF(false); 01563 } else { 01564 reg_ax=dos.errorcode; 01565 reg_bx=size; 01566 CALLBACK_SCF(true); 01567 } 01568 break; 01569 } 01570 case 0x4b: /* EXEC Load and/or execute program */ 01571 { 01572 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01573 LOG(LOG_EXEC,LOG_NORMAL)("Execute %s %d",name1,reg_al); 01574 if (!DOS_Execute(name1,SegPhys(es)+reg_bx,reg_al)) { 01575 reg_ax=dos.errorcode; 01576 CALLBACK_SCF(true); 01577 } 01578 dos_program_running = true; 01579 } 01580 break; 01581 //TODO Check for use of execution state AL=5 01582 case 0x4c: /* EXIT Terminate with return code */ 01583 DOS_Terminate(dos.psp(),false,reg_al); 01584 if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ 01585 dos_program_running = false; 01586 break; 01587 case 0x4d: /* Get Return code */ 01588 reg_al=dos.return_code;/* Officially read from SDA and clear when read */ 01589 reg_ah=dos.return_mode; 01590 break; 01591 case 0x4e: /* FINDFIRST Find first matching file */ 01592 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01593 lfn_filefind_handle=LFN_FILEFIND_NONE; 01594 if (DOS_FindFirst(name1,reg_cx)) { 01595 CALLBACK_SCF(false); 01596 reg_ax=0; /* Undocumented */ 01597 } else { 01598 reg_ax=dos.errorcode; 01599 CALLBACK_SCF(true); 01600 } 01601 break; 01602 case 0x4f: /* FINDNEXT Find next matching file */ 01603 if (DOS_FindNext()) { 01604 CALLBACK_SCF(false); 01605 /* reg_ax=0xffff;*/ /* Undocumented */ 01606 reg_ax=0; /* Undocumented:Qbix Willy beamish */ 01607 } else { 01608 reg_ax=dos.errorcode; 01609 CALLBACK_SCF(true); 01610 } 01611 break; 01612 case 0x50: /* Set current PSP */ 01613 dos.psp(reg_bx); 01614 break; 01615 case 0x51: /* Get current PSP */ 01616 reg_bx=dos.psp(); 01617 break; 01618 case 0x52: { /* Get list of lists */ 01619 Bit8u count=2; // floppy drives always counted 01620 while (count<DOS_DRIVES && Drives[count] && !Drives[count]->isRemovable()) count++; 01621 dos_infoblock.SetBlockDevices(count); 01622 RealPt addr=dos_infoblock.GetPointer(); 01623 SegSet16(es,RealSeg(addr)); 01624 reg_bx=RealOff(addr); 01625 LOG(LOG_DOSMISC,LOG_NORMAL)("Call is made for list of lists - let's hope for the best"); 01626 break; } 01627 //TODO Think hard how shit this is gonna be 01628 //And will any game ever use this :) 01629 case 0x53: /* Translate BIOS parameter block to drive parameter block */ 01630 E_Exit("Unhandled Dos 21 call %02X",reg_ah); 01631 break; 01632 case 0x54: /* Get verify flag */ 01633 reg_al=dos.verify?1:0; 01634 break; 01635 case 0x55: /* Create Child PSP*/ 01636 DOS_ChildPSP(reg_dx,reg_si); 01637 dos.psp(reg_dx); 01638 reg_al=0xf0; /* al destroyed */ 01639 break; 01640 case 0x56: /* RENAME Rename file */ 01641 force_sfn = true; 01642 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01643 MEM_StrCopy(SegPhys(es)+reg_di,name2,DOSNAMEBUF); 01644 if (DOS_Rename(name1,name2)) { 01645 CALLBACK_SCF(false); 01646 } else { 01647 reg_ax=dos.errorcode; 01648 CALLBACK_SCF(true); 01649 } 01650 force_sfn = false; 01651 break; 01652 case 0x57: /* Get/Set File's Date and Time */ 01653 if (reg_al==0x00) { 01654 if (DOS_GetFileDate(reg_bx,®_cx,®_dx)) { 01655 CALLBACK_SCF(false); 01656 } else { 01657 CALLBACK_SCF(true); 01658 } 01659 } else if (reg_al==0x01) { 01660 if (DOS_SetFileDate(reg_bx,reg_cx,reg_dx)) { 01661 CALLBACK_SCF(false); 01662 } else { 01663 CALLBACK_SCF(true); 01664 } 01665 } else { 01666 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:57:Unsupported subtion %X",reg_al); 01667 } 01668 break; 01669 case 0x58: /* Get/Set Memory allocation strategy */ 01670 switch (reg_al) { 01671 case 0: /* Get Strategy */ 01672 reg_ax=DOS_GetMemAllocStrategy(); 01673 break; 01674 case 1: /* Set Strategy */ 01675 if (DOS_SetMemAllocStrategy(reg_bx)) CALLBACK_SCF(false); 01676 else { 01677 reg_ax=1; 01678 CALLBACK_SCF(true); 01679 } 01680 break; 01681 case 2: /* Get UMB Link Status */ 01682 reg_al=dos_infoblock.GetUMBChainState()&1; 01683 CALLBACK_SCF(false); 01684 break; 01685 case 3: /* Set UMB Link Status */ 01686 if (DOS_LinkUMBsToMemChain(reg_bx)) CALLBACK_SCF(false); 01687 else { 01688 reg_ax=1; 01689 CALLBACK_SCF(true); 01690 } 01691 break; 01692 default: 01693 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:58:Not Supported Set//Get memory allocation call %X",reg_al); 01694 reg_ax=1; 01695 CALLBACK_SCF(true); 01696 } 01697 break; 01698 case 0x59: /* Get Extended error information */ 01699 reg_ax=dos.errorcode; 01700 if (dos.errorcode==DOSERR_FILE_NOT_FOUND || dos.errorcode==DOSERR_PATH_NOT_FOUND) { 01701 reg_bh=8; //Not Found error class (Road Hog) 01702 } else { 01703 reg_bh=0; //Unspecified error class 01704 } 01705 reg_bl=1; //Retry retry retry 01706 reg_ch=0; //Unkown error locus 01707 break; 01708 case 0x5a: /* Create temporary file */ 01709 { 01710 Bit16u handle; 01711 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01712 if (DOS_CreateTempFile(name1,&handle)) { 01713 reg_ax=handle; 01714 MEM_BlockWrite(SegPhys(ds)+reg_dx,name1,(Bitu)(strlen(name1)+1)); 01715 CALLBACK_SCF(false); 01716 } else { 01717 reg_ax=dos.errorcode; 01718 CALLBACK_SCF(true); 01719 } 01720 } 01721 break; 01722 case 0x5b: /* Create new file */ 01723 { 01724 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 01725 Bit16u handle; 01726 if (DOS_OpenFile(name1,0,&handle)) { 01727 DOS_CloseFile(handle); 01728 DOS_SetError(DOSERR_FILE_ALREADY_EXISTS); 01729 reg_ax=dos.errorcode; 01730 CALLBACK_SCF(true); 01731 break; 01732 } 01733 if (DOS_CreateFile(name1,reg_cx,&handle)) { 01734 reg_ax=handle; 01735 CALLBACK_SCF(false); 01736 } else { 01737 reg_ax=dos.errorcode; 01738 CALLBACK_SCF(true); 01739 } 01740 break; 01741 } 01742 case 0x5c: { /* FLOCK File region locking */ 01743 /* ert, 20100711: Locking extensions */ 01744 Bit32u pos=((unsigned int)reg_cx << 16u) + reg_dx; 01745 Bit32u size=((unsigned int)reg_si << 16u) + reg_di; 01746 //LOG_MSG("LockFile: BX=%d, AL=%d, POS=%d, size=%d", reg_bx, reg_al, pos, size); 01747 if (DOS_LockFile(reg_bx,reg_al,pos, size)) { 01748 reg_ax=0; 01749 CALLBACK_SCF(false); 01750 } else { 01751 reg_ax=dos.errorcode; 01752 CALLBACK_SCF(true); 01753 } 01754 break; } 01755 /* 01756 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 01757 reg_ax = dos.errorcode; 01758 CALLBACK_SCF(true); 01759 break; 01760 */ 01761 case 0x5d: /* Network Functions */ 01762 if(reg_al == 0x06) { 01763 /* FIXME: I'm still not certain, @emendelson, why this matters so much 01764 * to WordPerfect 5.1 and 6.2 and why it causes problems otherwise. 01765 * DOSBox and DOSBox-X only use the first 0x1A bytes anyway. */ 01766 SegSet16(ds,DOS_SDA_SEG); 01767 reg_si = DOS_SDA_OFS; 01768 reg_cx = DOS_SDA_SEG_SIZE; // swap if in dos 01769 reg_dx = 0x1a; // swap always (NTS: Size of DOS SDA structure in dos_inc) 01770 LOG(LOG_DOSMISC,LOG_NORMAL)("Get SDA, Let's hope for the best!"); 01771 } 01772 break; 01773 case 0x5e: /* Network and printer functions */ 01774 LOG(LOG_DOSMISC, LOG_ERROR)("DOS:5E Network and printer functions not implemented"); 01775 goto default_fallthrough; 01776 case 0x5f: /* Network redirection */ 01777 #if defined(WIN32) && !defined(HX_DOS) 01778 switch(reg_al) 01779 { 01780 case 0x34: //Set pipe state 01781 if(Network_SetNamedPipeState(reg_bx,reg_cx,reg_ax)) 01782 CALLBACK_SCF(false); 01783 else CALLBACK_SCF(true); 01784 break; 01785 case 0x35: //Peek pipe 01786 { 01787 Bit16u uTmpSI=reg_si; 01788 if(Network_PeekNamedPipe(reg_bx, 01789 dos_copybuf,reg_cx, 01790 reg_cx,reg_si,reg_dx, 01791 reg_di,reg_ax)) 01792 { 01793 MEM_BlockWrite(SegPhys(ds)+uTmpSI,dos_copybuf,reg_cx); 01794 CALLBACK_SCF(false); 01795 } 01796 else CALLBACK_SCF(true); 01797 } 01798 break; 01799 case 0x36: //Transcate pipe 01800 //Inbuffer:the buffer to be written to pipe 01801 MEM_BlockRead(SegPhys(ds)+reg_si,dos_copybuf_second,reg_cx); 01802 01803 if(Network_TranscateNamedPipe(reg_bx, 01804 dos_copybuf_second,reg_cx, 01805 dos_copybuf,reg_dx, 01806 reg_cx,reg_ax)) 01807 { 01808 //Outbuffer:the buffer to receive data from pipe 01809 MEM_BlockWrite(SegPhys(es)+reg_di,dos_copybuf,reg_cx); 01810 CALLBACK_SCF(false); 01811 } 01812 else CALLBACK_SCF(true); 01813 break; 01814 default: 01815 reg_ax=0x0001; //Failing it 01816 CALLBACK_SCF(true); 01817 break; 01818 } 01819 #else 01820 reg_ax=0x0001; //Failing it 01821 CALLBACK_SCF(true); 01822 #endif 01823 break; 01824 case 0x60: /* Canonicalize filename or path */ 01825 MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF); 01826 if (DOS_Canonicalize(name1,name2)) { 01827 MEM_BlockWrite(SegPhys(es)+reg_di,name2,(Bitu)(strlen(name2)+1)); 01828 CALLBACK_SCF(false); 01829 } else { 01830 reg_ax=dos.errorcode; 01831 CALLBACK_SCF(true); 01832 } 01833 break; 01834 case 0x61: /* Unused (reserved for network use) */ 01835 goto default_fallthrough; 01836 case 0x62: /* Get Current PSP Address */ 01837 reg_bx=dos.psp(); 01838 break; 01839 case 0x63: /* DOUBLE BYTE CHARACTER SET */ 01840 if(reg_al == 0 && dos.tables.dbcs != 0) { 01841 SegSet16(ds,RealSeg(dos.tables.dbcs)); 01842 reg_si=RealOff(dos.tables.dbcs); 01843 reg_al = 0; 01844 CALLBACK_SCF(false); //undocumented 01845 } else reg_al = 0xff; //Doesn't officially touch carry flag 01846 break; 01847 case 0x64: /* Set device driver lookahead flag */ 01848 LOG(LOG_DOSMISC,LOG_NORMAL)("set driver look ahead flag"); 01849 break; 01850 case 0x65: /* Get extented country information and a lot of other useless shit*/ 01851 { /* Todo maybe fully support this for now we set it standard for USA */ 01852 LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:65:Extended country information call %X",reg_ax); 01853 if((reg_al <= 0x07) && (reg_cx < 0x05)) { 01854 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); 01855 CALLBACK_SCF(true); 01856 break; 01857 } 01858 Bitu len = 0; /* For 0x21 and 0x22 */ 01859 PhysPt data=SegPhys(es)+reg_di; 01860 switch (reg_al) { 01861 case 0x01: 01862 mem_writeb(data + 0x00,reg_al); 01863 mem_writew(data + 0x01,0x26); 01864 if (!countryNo) { 01865 #ifdef WIN32 01866 char buffer[128]; 01867 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICOUNTRY, buffer, 128)) 01868 { 01869 countryNo = Bit16u(atoi(buffer)); 01870 DOS_SetCountry(countryNo); 01871 } 01872 else 01873 #endif 01874 countryNo = 1; // Defaults to 1 (US) if failed 01875 } 01876 mem_writew(data + 0x03, countryNo); 01877 if(reg_cx > 0x06 ) mem_writew(data+0x05,dos.loaded_codepage); 01878 if(reg_cx > 0x08 ) { 01879 Bitu amount = (reg_cx>=0x29u)?0x22u:(reg_cx-7u); 01880 MEM_BlockWrite(data + 0x07,dos.tables.country,amount); 01881 reg_cx=(reg_cx>=0x29)?0x29:reg_cx; 01882 } 01883 CALLBACK_SCF(false); 01884 break; 01885 case 0x05: // Get pointer to filename terminator table 01886 mem_writeb(data + 0x00, reg_al); 01887 mem_writed(data + 0x01, dos.tables.filenamechar); 01888 reg_cx = 5; 01889 CALLBACK_SCF(false); 01890 break; 01891 case 0x02: // Get pointer to uppercase table 01892 case 0x04: // Get pointer to filename uppercase table 01893 mem_writeb(data + 0x00, reg_al); 01894 mem_writed(data + 0x01, dos.tables.upcase); 01895 reg_cx = 5; 01896 CALLBACK_SCF(false); 01897 break; 01898 case 0x06: // Get pointer to collating sequence table 01899 mem_writeb(data + 0x00, reg_al); 01900 mem_writed(data + 0x01, dos.tables.collatingseq); 01901 reg_cx = 5; 01902 CALLBACK_SCF(false); 01903 break; 01904 case 0x03: // Get pointer to lowercase table 01905 case 0x07: // Get pointer to double byte char set table 01906 if (dos.tables.dbcs != 0) { 01907 mem_writeb(data + 0x00, reg_al); 01908 mem_writed(data + 0x01, dos.tables.dbcs); //used to be 0 01909 reg_cx = 5; 01910 CALLBACK_SCF(false); 01911 } 01912 break; 01913 case 0x20: /* Capitalize Character */ 01914 { 01915 int in = reg_dl; 01916 int out = toupper(in); 01917 reg_dl = (Bit8u)out; 01918 } 01919 CALLBACK_SCF(false); 01920 break; 01921 case 0x21: /* Capitalize String (cx=length) */ 01922 case 0x22: /* Capatilize ASCIZ string */ 01923 data = SegPhys(ds) + reg_dx; 01924 if(reg_al == 0x21) len = reg_cx; 01925 else len = mem_strlen(data); /* Is limited to 1024 */ 01926 01927 if(len > DOS_COPYBUFSIZE - 1) E_Exit("DOS:0x65 Buffer overflow"); 01928 if(len) { 01929 MEM_BlockRead(data,dos_copybuf,len); 01930 dos_copybuf[len] = 0; 01931 //No upcase as String(0x21) might be multiple asciz strings 01932 for (Bitu count = 0; count < len;count++) 01933 dos_copybuf[count] = (Bit8u)toupper(*reinterpret_cast<unsigned char*>(dos_copybuf+count)); 01934 MEM_BlockWrite(data,dos_copybuf,len); 01935 } 01936 CALLBACK_SCF(false); 01937 break; 01938 case 0x23: /* Determine if character represents yes/no response (MS-DOS 4.0+) */ 01939 /* DL = character 01940 * DH = second char of double-byte char if DBCS */ 01941 /* response: CF=1 if error (what error?) or CF=0 and AX=response 01942 * 01943 * response values 0=no 1=yes 2=neither */ 01944 /* FORMAT.COM and FDISK.EXE rely on this call after prompting the user */ 01945 { 01946 unsigned int c; 01947 01948 if (IS_PC98_ARCH) 01949 c = reg_dx; // DBCS 01950 else 01951 c = reg_dl; // SBCS 01952 01953 if (tolower(c) == 'y') 01954 reg_ax = 1;/*yes*/ 01955 else if (tolower(c) == 'n') 01956 reg_ax = 0;/*no*/ 01957 else 01958 reg_ax = 2;/*neither*/ 01959 } 01960 CALLBACK_SCF(false); 01961 break; 01962 default: 01963 E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al); 01964 } 01965 break; 01966 } 01967 case 0x66: /* Get/Set global code page table */ 01968 if (reg_al==1) { 01969 LOG(LOG_DOSMISC,LOG_NORMAL)("Getting global code page table"); 01970 reg_bx=reg_dx=dos.loaded_codepage; 01971 CALLBACK_SCF(false); 01972 break; 01973 } 01974 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Setting code page table is not supported"); 01975 break; 01976 case 0x67: /* Set handle count */ 01977 /* Weird call to increase amount of file handles needs to allocate memory if >20 */ 01978 { 01979 DOS_PSP psp(dos.psp()); 01980 psp.SetNumFiles(reg_bx); 01981 CALLBACK_SCF(false); 01982 break; 01983 } 01984 case 0x68: /* FFLUSH Commit file */ 01985 case_0x68_fallthrough: 01986 if(DOS_FlushFile(reg_bl)) { 01987 CALLBACK_SCF(false); 01988 } else { 01989 reg_ax = dos.errorcode; 01990 CALLBACK_SCF(true); 01991 } 01992 break; 01993 case 0x69: /* Get/Set disk serial number */ 01994 { 01995 Bit16u old_cx=reg_cx; 01996 switch(reg_al) { 01997 case 0x00: /* Get */ 01998 LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Get Disk serial number"); 01999 reg_cl=0x66;// IOCTL function 02000 break; 02001 case 0x01: /* Set */ 02002 LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:Set Disk serial number"); 02003 reg_cl=0x46;// IOCTL function 02004 break; 02005 default: 02006 E_Exit("DOS:Illegal Get Serial Number call %2X",reg_al); 02007 } 02008 reg_ch=0x08; // IOCTL category: disk drive 02009 reg_ax=0x440d; // Generic block device request 02010 DOS_21Handler(); 02011 reg_cx=old_cx; 02012 break; 02013 } 02014 case 0x6a: /* Commit file */ 02015 // Note: Identical to AH=68h in DOS 5.0-6.0; not known whether this is the case in DOS 4.x 02016 goto case_0x68_fallthrough; 02017 case 0x6b: /* NULL Function */ 02018 goto default_fallthrough; 02019 case 0x6c: /* Extended Open/Create */ 02020 MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF); 02021 if (DOS_OpenFileExtended(name1,reg_bx,reg_cx,reg_dx,®_ax,®_cx)) { 02022 CALLBACK_SCF(false); 02023 } else { 02024 reg_ax=dos.errorcode; 02025 CALLBACK_SCF(true); 02026 } 02027 break; 02028 case 0x6d: /* ROM - Find first ROM program */ 02029 LOG(LOG_DOSMISC, LOG_ERROR)("DOS:ROM - Find first ROM program not implemented"); 02030 goto default_fallthrough; 02031 case 0x6e: /* ROM - Find next ROM program */ 02032 LOG(LOG_DOSMISC, LOG_ERROR)("DOS:ROM - Find next ROM program not implemented"); 02033 goto default_fallthrough; 02034 case 0x6f: /* ROM functions */ 02035 LOG(LOG_DOSMISC, LOG_ERROR)("DOS:6F ROM functions not implemented"); 02036 goto default_fallthrough; 02037 case 0x71: /* Unknown probably 4dos detection */ 02038 LOG(LOG_DOSMISC,LOG_NORMAL)("DOS:MS-DOS 7+ long file name support call %2X",reg_al); 02039 if (!uselfn) { 02040 reg_ax=0x7100; 02041 CALLBACK_SCF(true); //Check this! What needs this ? See default case 02042 break; 02043 } 02044 switch(reg_al) { 02045 case 0x39: /* LFN MKDIR */ 02046 DOS_Int21_7139(name1, name2); 02047 break; 02048 case 0x3a: /* LFN RMDIR */ 02049 DOS_Int21_713a(name1, name2); 02050 break; 02051 case 0x3b: /* LFN CHDIR */ 02052 DOS_Int21_713b(name1, name2); 02053 break; 02054 case 0x41: /* LFN UNLINK */ 02055 DOS_Int21_7141(name1, name2); 02056 break; 02057 case 0x43: /* LFN ATTR */ 02058 DOS_Int21_7143(name1, name2); 02059 break; 02060 case 0x47: /* LFN PWD */ 02061 DOS_Int21_7147(name1, name2); 02062 break; 02063 case 0x4e: /* LFN FindFirst */ 02064 DOS_Int21_714e(name1, name2); 02065 break; 02066 case 0x4f: /* LFN FindNext */ 02067 DOS_Int21_714f(name1, name2); 02068 break; 02069 case 0x56: /* LFN Rename */ 02070 DOS_Int21_7156(name1, name2); 02071 break; 02072 case 0x60: /* LFN GetName */ 02073 DOS_Int21_7160(name1, name2); 02074 break; 02075 case 0x6c: /* LFN Create */ 02076 DOS_Int21_716c(name1, name2); 02077 break; 02078 case 0xa0: /* LFN VolInfo */ 02079 DOS_Int21_71a0(name1, name2); 02080 break; 02081 case 0xa1: /* LFN FileClose */ 02082 DOS_Int21_71a1(name1, name2); 02083 break; 02084 case 0xa6: /* LFN GetFileInfoByHandle */ 02085 DOS_Int21_71a6(name1, name2); 02086 break; 02087 case 0xa7: /* LFN TimeConv */ 02088 DOS_Int21_71a7(name1, name2); 02089 break; 02090 case 0xa8: /* LFN GenSFN */ 02091 DOS_Int21_71a8(name1, name2); 02092 break; 02093 case 0xaa: /* LFN Subst */ 02094 DOS_Int21_71aa(name1, name2); 02095 break; 02096 case 0xa9: /* LFN Server Create */ 02097 reg_ax=0x7100; // unimplemented (not very useful) 02098 default: 02099 reg_ax=0x7100; 02100 CALLBACK_SCF(true); //Check this! What needs this ? See default case 02101 } 02102 break; 02103 case 0x73: 02104 if (dos.version.major < 7) { // MS-DOS 7+ only for AX=73xxh 02105 CALLBACK_SCF(true); 02106 reg_ax=0x7300; 02107 } else if (reg_al==0 && reg_cl<2) { 02108 /* Drive locking and flushing */ 02109 reg_al = reg_cl; 02110 reg_ah = 0; 02111 CALLBACK_SCF(false); 02112 } else if (reg_al==2) { 02113 /* Get extended DPB */ 02114 Bit32u ptr = SegPhys(es)+reg_di; 02115 Bit8u drive; 02116 02117 /* AX=7302h 02118 * DL=drive 02119 * ES:DI=buffer to return data into 02120 * CX=length of buffer (Windows 9x uses 0x3F) 02121 * SI=??? */ 02122 02123 if (reg_dl != 0) /* 1=A: 2=B: ... */ 02124 drive = reg_dl - 1; 02125 else /* 0=default */ 02126 drive = DOS_GetDefaultDrive(); 02127 02128 if (drive < DOS_DRIVES && Drives[drive] && !Drives[drive]->isRemovable() && reg_cx >= 0x3F) { 02129 fatDrive *fdp; 02130 FAT_BootSector::bpb_union_t bpb; 02131 if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) { 02132 fdp = dynamic_cast<fatDrive*>(Drives[drive]); 02133 if (fdp != NULL) { 02134 bpb=fdp->GetBPB(); 02135 if (bpb.is_fat32()) { 02136 unsigned char tmp[24]; 02137 02138 mem_writew(ptr+0x00,0x3D); // length of data (Windows 98) 02139 /* first 24 bytes after len is DPB */ 02140 { 02141 const Bit32u srcptr = (dos.tables.dpb << 4) + (drive*dos.tables.dpb_size); 02142 MEM_BlockRead(srcptr,tmp,24); 02143 MEM_BlockWrite(ptr+0x02,tmp,24); 02144 } 02145 Bit32u bytes_per_sector,sectors_per_cluster,total_clusters,free_clusters,tfree; 02146 rsize=true; 02147 totalc=freec=0; 02148 if (DOS_GetFreeDiskSpace32(reg_dl,&bytes_per_sector,§ors_per_cluster,&total_clusters,&free_clusters)) 02149 tfree = freec?freec:free_clusters; 02150 else 02151 tfree=0xFFFFFFFF; 02152 rsize=false; 02153 mem_writeb(ptr+0x1A,0x00); // dpb flags 02154 mem_writed(ptr+0x1B,0xFFFFFFFF);// ptr to next DPB if Windows 95 magic SI signature (TODO) 02155 mem_writew(ptr+0x1F,2); // cluster to start searching when writing (FIXME) 02156 mem_writed(ptr+0x21,tfree);// number of free clusters 02157 mem_writew(ptr+0x25,bpb.v32.BPB_ExtFlags); 02158 mem_writew(ptr+0x27,bpb.v32.BPB_FSInfo); 02159 mem_writew(ptr+0x29,bpb.v32.BPB_BkBootSec); 02160 mem_writed(ptr+0x2B,fdp->GetFirstClusterOffset()); /* apparently cluster offset relative to the disk not volume */ 02161 mem_writed(ptr+0x2F,fdp->GetHighestClusterNumber()); 02162 mem_writed(ptr+0x33,bpb.v32.BPB_FATSz32); 02163 mem_writed(ptr+0x37,bpb.v32.BPB_RootClus); 02164 mem_writed(ptr+0x3B,2); // cluster to start searching when writing (FIXME) 02165 02166 CALLBACK_SCF(false); 02167 break; 02168 } 02169 } 02170 } 02171 02172 reg_ax=0x18;//FIXME 02173 CALLBACK_SCF(true); 02174 } else { 02175 reg_ax=0x18;//FIXME 02176 CALLBACK_SCF(true); 02177 } 02178 } else if (reg_al==3) { 02179 /* Get extended free disk space */ 02180 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,reg_cx); 02181 if (name1[1]==':'&&name1[2]=='\\') 02182 reg_dl=name1[0]-'A'+1; 02183 else { 02184 reg_ax=0xffff; 02185 CALLBACK_SCF(true); 02186 break; 02187 } 02188 Bit32u bytes_per_sector,sectors_per_cluster,total_clusters,free_clusters; 02189 rsize=true; 02190 totalc=freec=0; 02191 if (DOS_GetFreeDiskSpace32(reg_dl,&bytes_per_sector,§ors_per_cluster,&total_clusters,&free_clusters)) 02192 { 02193 ext_space_info_t *info = new ext_space_info_t; 02194 info->size_of_structure = sizeof(ext_space_info_t); 02195 info->structure_version = 0; 02196 info->sectors_per_cluster = sectors_per_cluster; 02197 info->bytes_per_sector = bytes_per_sector; 02198 info->available_clusters_on_drive = freec?freec:free_clusters; 02199 info->total_clusters_on_drive = totalc?totalc:total_clusters; 02200 info->available_sectors_on_drive = sectors_per_cluster * (freec?freec:free_clusters); 02201 info->total_sectors_on_drive = sectors_per_cluster * (totalc?totalc:total_clusters); 02202 info->available_allocation_units = freec?freec:free_clusters; 02203 info->total_allocation_units = totalc?totalc:total_clusters; 02204 MEM_BlockWrite(SegPhys(es)+reg_di,info,sizeof(ext_space_info_t)); 02205 delete(info); 02206 reg_ax=0; 02207 CALLBACK_SCF(false); 02208 } 02209 else 02210 { 02211 reg_ax=dos.errorcode; 02212 CALLBACK_SCF(true); 02213 } 02214 rsize=false; 02215 } else if (reg_al == 5 && reg_cx == 0xFFFF && (dos.version.major > 7 || dos.version.minor >= 10)) { 02216 /* MS-DOS 7.1+ (Windows 95 OSR2+) FAT32 extended disk read/write */ 02217 reg_al = reg_dl - 1; /* INT 25h AL 0=A: 1=B: This interface DL 1=A: 2=B: */ 02218 if (reg_si & 1) 02219 DOS_26Handler_Actual(true/*fat32*/); /* writing */ 02220 else 02221 DOS_25Handler_Actual(true/*fat32*/); /* reading */ 02222 02223 /* CF needs to be returned on stack or else it's lost */ 02224 CALLBACK_SCF(!!(reg_flags & FLAG_CF)); 02225 } else { 02226 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Unhandled call %02X al=%02X (MS-DOS 7.x function)",reg_ah,reg_al); 02227 CALLBACK_SCF(true); 02228 reg_ax=0xffff;//FIXME 02229 } 02230 break; 02231 case 0xE0: 02232 case 0xEF: /* Used in Ancient Art Of War CGA */ 02233 default: 02234 default_fallthrough: 02235 if (reg_ah < 0x6b) LOG(LOG_DOSMISC,LOG_ERROR)("DOS:Unhandled call %02X al=%02X. Set al to default of 0",reg_ah,reg_al); //Less errors. above 0x6c the functions are simply always skipped, only al is zeroed, all other registers untouched 02236 reg_al=0x00; /* default value */ 02237 break; 02238 } 02239 02240 /* if INT 21h involves any BIOS calls that need the timer, emulate the fact that tbe 02241 * BIOS might unmask IRQ 0 as part of the job (especially INT 13h disk I/O). 02242 * 02243 * Some DOS games & demos mask interrupts at the PIC level in a stingy manner that 02244 * apparently assumes DOS/BIOS will unmask some when called. 02245 * 02246 * Examples: 02247 * Rebel by Arkham (without this fix, timer interrupt will not fire during demo and therefore music will not play). */ 02248 if (unmask_irq0) 02249 PIC_SetIRQMask(0,false); /* Enable system timer */ 02250 02251 return CBRET_NONE; 02252 } 02253 02254 02255 static Bitu BIOS_1BHandler(void) { 02256 mem_writeb(BIOS_CTRL_BREAK_FLAG,0x00); 02257 02258 /* take note (set flag) and return */ 02259 /* FIXME: Don't forget that on "BOOT" this handler should be unassigned, though having it assigned 02260 * to the guest OS causes no harm. */ 02261 LOG_MSG("Note: default 1Bh handler invoked\n"); 02262 DOS_BreakFlag = true; 02263 return CBRET_NONE; 02264 } 02265 02266 static Bitu DOS_20Handler(void) { 02267 reg_ah=0x00; 02268 DOS_21Handler(); 02269 return CBRET_NONE; 02270 } 02271 02272 static Bitu DOS_CPMHandler(void) { 02273 // Convert a CPM-style call to a normal DOS call 02274 Bit16u flags=CPU_Pop16(); 02275 CPU_Pop16(); 02276 Bit16u caller_seg=CPU_Pop16(); 02277 Bit16u caller_off=CPU_Pop16(); 02278 CPU_Push16(flags); 02279 CPU_Push16(caller_seg); 02280 CPU_Push16(caller_off); 02281 if (reg_cl>0x24) { 02282 reg_al=0; 02283 return CBRET_NONE; 02284 } 02285 reg_ah=reg_cl; 02286 return DOS_21Handler(); 02287 } 02288 02289 static Bitu DOS_27Handler(void) { 02290 // Terminate & stay resident 02291 Bit16u para = (reg_dx/16)+((reg_dx % 16)>0); 02292 Bit16u psp = dos.psp(); //mem_readw(SegPhys(ss)+reg_sp+2); 02293 if (DOS_ResizeMemory(psp,¶)) { 02294 DOS_Terminate(psp,true,0); 02295 if (DOS_BreakINT23InProgress) throw int(0); /* HACK: Ick */ 02296 } 02297 return CBRET_NONE; 02298 } 02299 02300 static Bitu DOS_25Handler_Actual(bool fat32) { 02301 if (reg_al >= DOS_DRIVES || !Drives[reg_al] || Drives[reg_al]->isRemovable()) { 02302 reg_ax = 0x8002; 02303 SETFLAGBIT(CF,true); 02304 } else { 02305 DOS_Drive *drv = Drives[reg_al]; 02306 /* assume drv != NULL */ 02307 Bit32u sector_size = drv->GetSectorSize(); 02308 Bit32u sector_count = drv->GetSectorCount(); 02309 PhysPt ptr = PhysMake(SegValue(ds),reg_bx); 02310 Bit32u req_count = reg_cx; 02311 Bit32u sector_num = reg_dx; 02312 02313 /* For < 32MB drives. 02314 * AL = drive 02315 * CX = sector count (not 0xFFFF) 02316 * DX = sector number 02317 * DS:BX = pointer to disk transfer area 02318 * 02319 * For >= 32MB drives. 02320 * 02321 * AL = drive 02322 * CX = 0xFFFF 02323 * DS:BX = disk read packet 02324 * 02325 * Disk read packet: 02326 * +0 DWORD = sector number 02327 * +4 WORD = sector count 02328 * +6 DWORD = disk tranfer area 02329 */ 02330 if (sector_count != 0 && sector_size != 0) { 02331 unsigned char tmp[2048]; 02332 const char *method; 02333 02334 if (sector_size > sizeof(tmp)) { 02335 reg_ax = 0x8002; 02336 SETFLAGBIT(CF,true); 02337 return CBRET_NONE; 02338 } 02339 02340 if (sector_count > 0xFFFF && req_count != 0xFFFF) { 02341 reg_ax = 0x0207; // must use CX=0xFFFF API for > 64KB segment partitions 02342 SETFLAGBIT(CF,true); 02343 return CBRET_NONE; 02344 } 02345 02346 if (fat32) { 02347 sector_num = mem_readd(ptr+0); 02348 req_count = mem_readw(ptr+4); 02349 Bit32u p = mem_readd(ptr+6); 02350 ptr = PhysMake(p >> 16u,p & 0xFFFFu); 02351 method = "Win95/FAT32"; 02352 } 02353 else if (req_count == 0xFFFF) { 02354 sector_num = mem_readd(ptr+0); 02355 req_count = mem_readw(ptr+4); 02356 Bit32u p = mem_readd(ptr+6); 02357 ptr = PhysMake(p >> 16u,p & 0xFFFFu); 02358 method = ">=32MB"; 02359 } 02360 else { 02361 method = "<32MB"; 02362 } 02363 02364 if (fat32) { 02365 LOG(LOG_MISC,LOG_DEBUG)("INT 21h AX=7305h READ: sector=%lu count=%lu ptr=%lx method='%s'", 02366 (unsigned long)sector_num, 02367 (unsigned long)req_count, 02368 (unsigned long)ptr, 02369 method); 02370 } 02371 else { 02372 LOG(LOG_MISC,LOG_DEBUG)("INT 25h READ: sector=%lu count=%lu ptr=%lx method='%s'", 02373 (unsigned long)sector_num, 02374 (unsigned long)req_count, 02375 (unsigned long)ptr, 02376 method); 02377 } 02378 02379 SETFLAGBIT(CF,false); 02380 reg_ax = 0; 02381 02382 while (req_count > 0) { 02383 Bit8u res = drv->Read_AbsoluteSector_INT25(sector_num,tmp); 02384 if (res != 0) { 02385 reg_ax = 0x8002; 02386 SETFLAGBIT(CF,true); 02387 break; 02388 } 02389 02390 for (unsigned int i=0;i < (unsigned int)sector_size;i++) 02391 mem_writeb(ptr+i,tmp[i]); 02392 02393 req_count--; 02394 sector_num++; 02395 ptr += sector_size; 02396 } 02397 02398 return CBRET_NONE; 02399 } 02400 02401 /* MicroProse installer hack, inherited from DOSBox SVN, as a fallback if INT 25h emulation is not available for the drive. */ 02402 if (reg_cx == 1 && reg_dx == 0 && reg_al >= 2) { 02403 // write some BPB data into buffer for MicroProse installers 02404 mem_writew(ptr+0x1c,0x3f); // hidden sectors 02405 SETFLAGBIT(CF,false); 02406 reg_ax = 0; 02407 } else { 02408 LOG(LOG_DOSMISC,LOG_NORMAL)("int 25 called but not as disk detection drive %u",reg_al); 02409 reg_ax = 0x8002; 02410 SETFLAGBIT(CF,true); 02411 } 02412 } 02413 return CBRET_NONE; 02414 } 02415 02416 static Bitu DOS_25Handler(void) { 02417 return DOS_25Handler_Actual(false); 02418 } 02419 02420 static Bitu DOS_26Handler_Actual(bool fat32) { 02421 if (reg_al >= DOS_DRIVES || !Drives[reg_al] || Drives[reg_al]->isRemovable()) { 02422 reg_ax = 0x8002; 02423 SETFLAGBIT(CF,true); 02424 } else { 02425 DOS_Drive *drv = Drives[reg_al]; 02426 /* assume drv != NULL */ 02427 Bit32u sector_size = drv->GetSectorSize(); 02428 Bit32u sector_count = drv->GetSectorCount(); 02429 PhysPt ptr = PhysMake(SegValue(ds),reg_bx); 02430 Bit32u req_count = reg_cx; 02431 Bit32u sector_num = reg_dx; 02432 02433 /* For < 32MB drives. 02434 * AL = drive 02435 * CX = sector count (not 0xFFFF) 02436 * DX = sector number 02437 * DS:BX = pointer to disk transfer area 02438 * 02439 * For >= 32MB drives. 02440 * 02441 * AL = drive 02442 * CX = 0xFFFF 02443 * DS:BX = disk read packet 02444 * 02445 * Disk read packet: 02446 * +0 DWORD = sector number 02447 * +4 WORD = sector count 02448 * +6 DWORD = disk tranfer area 02449 */ 02450 if (sector_count != 0 && sector_size != 0) { 02451 unsigned char tmp[2048]; 02452 const char *method; 02453 02454 if (sector_size > sizeof(tmp)) { 02455 reg_ax = 0x8002; 02456 SETFLAGBIT(CF,true); 02457 return CBRET_NONE; 02458 } 02459 02460 if (sector_count > 0xFFFF && req_count != 0xFFFF) { 02461 reg_ax = 0x0207; // must use CX=0xFFFF API for > 64KB segment partitions 02462 SETFLAGBIT(CF,true); 02463 return CBRET_NONE; 02464 } 02465 02466 if (fat32) { 02467 sector_num = mem_readd(ptr+0); 02468 req_count = mem_readw(ptr+4); 02469 Bit32u p = mem_readd(ptr+6); 02470 ptr = PhysMake(p >> 16u,p & 0xFFFFu); 02471 method = "Win95/FAT32"; 02472 } 02473 else if (req_count == 0xFFFF) { 02474 sector_num = mem_readd(ptr+0); 02475 req_count = mem_readw(ptr+4); 02476 Bit32u p = mem_readd(ptr+6); 02477 ptr = PhysMake(p >> 16u,p & 0xFFFFu); 02478 method = ">=32MB"; 02479 } 02480 else { 02481 method = "<32MB"; 02482 } 02483 02484 if (fat32) { 02485 LOG(LOG_MISC,LOG_DEBUG)("INT 21h AX=7305h WRITE: sector=%lu count=%lu ptr=%lx method='%s'", 02486 (unsigned long)sector_num, 02487 (unsigned long)req_count, 02488 (unsigned long)ptr, 02489 method); 02490 } 02491 else { 02492 LOG(LOG_MISC,LOG_DEBUG)("INT 26h WRITE: sector=%lu count=%lu ptr=%lx method='%s'", 02493 (unsigned long)sector_num, 02494 (unsigned long)req_count, 02495 (unsigned long)ptr, 02496 method); 02497 } 02498 02499 SETFLAGBIT(CF,false); 02500 reg_ax = 0; 02501 02502 while (req_count > 0) { 02503 for (unsigned int i=0;i < (unsigned int)sector_size;i++) 02504 tmp[i] = mem_readb(ptr+i); 02505 02506 Bit8u res = drv->Write_AbsoluteSector_INT25(sector_num,tmp); 02507 if (res != 0) { 02508 reg_ax = 0x8002; 02509 SETFLAGBIT(CF,true); 02510 break; 02511 } 02512 02513 req_count--; 02514 sector_num++; 02515 ptr += sector_size; 02516 } 02517 02518 return CBRET_NONE; 02519 } 02520 02521 reg_ax = 0x8002; 02522 SETFLAGBIT(CF,true); 02523 } 02524 return CBRET_NONE; 02525 } 02526 02527 static Bitu DOS_26Handler(void) { 02528 return DOS_26Handler_Actual(false); 02529 } 02530 02531 bool enable_collating_uppercase = true; 02532 bool keep_private_area_on_boot = false; 02533 bool private_always_from_umb = false; 02534 bool private_segment_in_umb = true; 02535 Bit16u DOS_IHSEG = 0; 02536 02537 // NOTE about 0x70 and PC-98 emulation mode: 02538 // 02539 // I don't know exactly how things differ in NEC's PC-98 MS-DOS, but, 02540 // according to some strange code in Touhou Project that's responsible 02541 // for blanking the text layer, there's a "row count" variable at 0x70:0x12 02542 // that holds (number of rows - 1). Leaving that byte value at zero prevents 02543 // the game from clearing the screen (which also exposes the tile data and 02544 // overdraw of the graphics layer). A value of zero instead just causes the 02545 // first text character row to be filled in, not the whole visible text layer. 02546 // 02547 // Pseudocode of the routine: 02548 // 02549 // XOR AX,AX 02550 // MOV ES,AX 02551 // MOV AL,ES:[0712h] ; AX = BYTE [0x70:0x12] zero extend (ex. 0x18 == 24) 02552 // INC AX ; AX++ (ex. becomes 0x19 == 25) 02553 // MOV DX,AX 02554 // SHL DX,1 02555 // SHL DX,1 ; DX *= 4 02556 // ADD DX,AX ; DX += AX equiv. DX = AX * 5 02557 // MOV CL,4h 02558 // SHL DX,CL ; DX <<= 4 equiv. DX = AX * 0x50 or DX = AX * 80 02559 // ... 02560 // MOV AX,0A200h 02561 // MOV ES,AX 02562 // MOV AX,(solid black overlay block attribute) 02563 // MOV CX,DX 02564 // REP STOSW 02565 // 02566 // When the routine is done, the graphics layer is obscured by text character cells that 02567 // represent all black (filled in) so that the game can later "punch out" the regions 02568 // of the graphics layer it wants you to see. TH02 relies on this as well to flash the 02569 // screen and open from the center to show the title screen. During gameplay, the text 02570 // layer is used to obscure sprite overdraw when a sprite is partially off-screen as well 02571 // as hidden tile data on the right hand half of the screen that the game read/write 02572 // copies through the GDC pattern/tile registers to make the background. When the text 02573 // layer is not present it's immediately apparent that the sprite renderer makes no attempt 02574 // to clip sprites within the screen, but instead relies on the text overlay to hide the 02575 // overdraw. 02576 // 02577 // this means that on PC-98 one of two things are true. either: 02578 // - NEC's variation of MS-DOS loads the base kernel higher up (perhaps at 0x80:0x00?) 02579 // and the BIOS data area lies from 0x40:00 to 0x7F:00 02580 // 02581 // or 02582 // 02583 // - NEC's variation loads at 0x70:0x00 (same as IBM PC MS-DOS) and Touhou Project 02584 // is dead guilty of reaching directly into MS-DOS kernel memory to read 02585 // internal variables it shouldn't be reading directly! 02586 // 02587 // Ick... 02588 02589 void DOS_GetMemory_reset(); 02590 void DOS_GetMemory_Choose(); 02591 Bitu MEM_PageMask(void); 02592 02593 #include <assert.h> 02594 02595 extern bool dos_con_use_int16_to_detect_input; 02596 extern bool dbg_zero_on_dos_allocmem; 02597 extern bool log_dev_con; 02598 02599 class DOS:public Module_base{ 02600 private: 02601 CALLBACK_HandlerObject callback[9]; 02602 RealPt int30,int31; 02603 02604 public: 02605 void DOS_Write_HMA_CPM_jmp(void) { 02606 // HMA mirror of CP/M entry point. 02607 // this is needed for "F01D:FEF0" to be a valid jmp whether or not A20 is enabled 02608 if (dos_in_hma && 02609 cpm_compat_mode != CPM_COMPAT_OFF && 02610 cpm_compat_mode != CPM_COMPAT_DIRECT) { 02611 LOG(LOG_MISC,LOG_DEBUG)("Writing HMA mirror of CP/M entry point"); 02612 02613 Bitu was_a20 = XMS_GetEnabledA20(); 02614 02615 XMS_EnableA20(true); 02616 02617 mem_writeb(0x1000C0,(Bit8u)0xea); // jmpf 02618 mem_unalignedwrited(0x1000C0+1,callback[8].Get_RealPointer()); 02619 02620 if (!was_a20) XMS_EnableA20(false); 02621 } 02622 } 02623 02624 Bit32u DOS_Get_CPM_entry_direct(void) { 02625 return callback[8].Get_RealPointer(); 02626 } 02627 02628 DOS(Section* configuration):Module_base(configuration){ 02629 const Section_prop* section = static_cast<Section_prop*>(configuration); 02630 02631 ::disk_data_rate = section->Get_int("hard drive data rate limit"); 02632 if (::disk_data_rate < 0) { 02633 extern bool pcibus_enable; 02634 02635 if (pcibus_enable) 02636 ::disk_data_rate = 8333333; /* Probably an average IDE data rate for mid 1990s PCI IDE controllers in PIO mode */ 02637 else 02638 ::disk_data_rate = 3500000; /* Probably an average IDE data rate for early 1990s ISA IDE controllers in PIO mode */ 02639 } 02640 maxfcb=100; 02641 DOS_FILES=127; 02642 Section_prop *config_section = static_cast<Section_prop *>(control->GetSection("config")); 02643 if (config_section != NULL && !control->opt_noconfig && !control->opt_securemode && !control->SecureMode()) { 02644 DOS_FILES = (unsigned int)config_section->Get_int("files"); 02645 if (DOS_FILES<8) DOS_FILES=8; 02646 else if (DOS_FILES>255) DOS_FILES=255; 02647 maxfcb = (int)config_section->Get_int("fcbs"); 02648 if (maxfcb<1) maxfcb=1; 02649 else if (maxfcb>255) maxfcb=255; 02650 char *dosopt = (char *)config_section->Get_string("dos"), *r=strchr(dosopt, ','); 02651 if (r==NULL) { 02652 if (!strcasecmp(trim(dosopt), "high")) dos_in_hma=true; 02653 else if (!strcasecmp(trim(dosopt), "low")) dos_in_hma=false; 02654 if (!strcasecmp(trim(dosopt), "umb")) dos_umb=true; 02655 else if (!strcasecmp(trim(dosopt), "noumb")) dos_umb=false; 02656 } else { 02657 *r=0; 02658 if (!strcasecmp(trim(dosopt), "high")) dos_in_hma=true; 02659 else if (!strcasecmp(trim(dosopt), "low")) dos_in_hma=false; 02660 *r=','; 02661 if (!strcasecmp(trim(r+1), "umb")) dos_umb=true; 02662 else if (!strcasecmp(trim(r+1), "noumb")) dos_umb=false; 02663 } 02664 char *lastdrive = (char *)config_section->Get_string("lastdrive"); 02665 if (strlen(lastdrive)==1&&lastdrive[0]>='a'&&lastdrive[0]<='z') 02666 maxdrive=lastdrive[0]-'a'+1; 02667 char *dosbreak = (char *)config_section->Get_string("break"); 02668 if (!strcasecmp(dosbreak, "on")) 02669 dos.breakcheck=true; 02670 else if (!strcasecmp(dosbreak, "off")) 02671 dos.breakcheck=false; 02672 #ifdef WIN32 02673 char *numlock = (char *)config_section->Get_string("numlock"); 02674 if (!strcasecmp(numlock, "off")&&startup_state_numlock || !strcasecmp(numlock, "on")&&!startup_state_numlock) 02675 SetNumLock(); 02676 #endif 02677 } 02678 02679 dos_sda_size = section->Get_int("dos sda size"); 02680 log_dev_con = control->opt_log_con || section->Get_bool("log console"); 02681 enable_dbcs_tables = section->Get_bool("dbcs"); 02682 enable_share_exe_fake = section->Get_bool("share"); 02683 enable_filenamechar = section->Get_bool("filenamechar"); 02684 dos_initial_hma_free = section->Get_int("hma free space"); 02685 minimum_mcb_free = section->Get_hex("minimum mcb free"); 02686 minimum_mcb_segment = section->Get_hex("minimum mcb segment"); 02687 private_segment_in_umb = section->Get_bool("private area in umb"); 02688 enable_collating_uppercase = section->Get_bool("collating and uppercase"); 02689 private_always_from_umb = section->Get_bool("kernel allocation in umb"); 02690 minimum_dos_initial_private_segment = section->Get_hex("minimum dos initial private segment"); 02691 dos_con_use_int16_to_detect_input = section->Get_bool("con device use int 16h to detect keyboard input"); 02692 dbg_zero_on_dos_allocmem = section->Get_bool("zero memory on int 21h memory allocation"); 02693 MAXENV = (unsigned int)section->Get_int("maximum environment block size on exec"); 02694 ENV_KEEPFREE = (unsigned int)section->Get_int("additional environment block size on exec"); 02695 enable_dummy_device_mcb = section->Get_bool("enable dummy device mcb"); 02696 int15_wait_force_unmask_irq = section->Get_bool("int15 wait force unmask irq"); 02697 disk_io_unmask_irq0 = section->Get_bool("unmask timer on disk io"); 02698 #if defined (WIN32) 02699 char *dos_clipboard_device_enable = (char *)section->Get_string("dos clipboard device enable"); 02700 dos_clipboard_device_access = !strcasecmp(dos_clipboard_device_enable, "dummy")?1:(!strcasecmp(dos_clipboard_device_enable, "read")?2:(!strcasecmp(dos_clipboard_device_enable, "write")?3:(!strcasecmp(dos_clipboard_device_enable, "full")||!strcasecmp(dos_clipboard_device_enable, "true")?4:0))); 02701 dos_clipboard_device_name = (char *)section->Get_string("dos clipboard device name"); 02702 if (dos_clipboard_device_access) { 02703 bool valid=true; 02704 char ch[]="*? .|<>/\\\""; 02705 if (!*dos_clipboard_device_name||strlen(dos_clipboard_device_name)>8||!strcasecmp(dos_clipboard_device_name, "con")||!strcasecmp(dos_clipboard_device_name, "nul")||!strcasecmp(dos_clipboard_device_name, "prn")) 02706 valid=false; 02707 else for (int i=0; i<strlen(ch); i++) { 02708 if (strchr(dos_clipboard_device_name, *(ch+i))!=NULL) { 02709 valid=false; 02710 break; 02711 } 02712 } 02713 dos_clipboard_device_name=valid?upcase(dos_clipboard_device_name):(char *)dos_clipboard_device_default; 02714 LOG(LOG_DOSMISC,LOG_NORMAL)("DOS clipboard device (%s access) is enabled with the name %s\n", dos_clipboard_device_access==1?"dummy":(dos_clipboard_device_access==2?"read":(dos_clipboard_device_access==3?"write":"full")), dos_clipboard_device_name); 02715 } 02716 #else 02717 dos_clipboard_device_access = 0; 02718 dos_clipboard_device_name=(char *)dos_clipboard_device_default; 02719 #endif 02720 for (int i=0; i < DOS_DRIVES; i++) 02721 if (Drives[i]) DriveManager::UnmountDrive(i); 02722 02723 if (dos_initial_hma_free > 0x10000) 02724 dos_initial_hma_free = 0x10000; 02725 02726 std::string cpmcompat = section->Get_string("cpm compatibility mode"); 02727 02728 if (cpmcompat == "") 02729 cpmcompat = "auto"; 02730 02731 if (cpmcompat == "msdos2") 02732 cpm_compat_mode = CPM_COMPAT_MSDOS2; 02733 else if (cpmcompat == "msdos5") 02734 cpm_compat_mode = CPM_COMPAT_MSDOS5; 02735 else if (cpmcompat == "direct") 02736 cpm_compat_mode = CPM_COMPAT_DIRECT; 02737 else if (cpmcompat == "auto") 02738 cpm_compat_mode = CPM_COMPAT_MSDOS5; /* MS-DOS 5.x is default */ 02739 else 02740 cpm_compat_mode = CPM_COMPAT_OFF; 02741 02742 /* FIXME: Boot up an MS-DOS system and look at what INT 21h on Microsoft's MS-DOS returns 02743 * for SDA size and location, then use that here. 02744 * 02745 * Why does this value matter so much to WordPerfect 5.1? */ 02746 if (dos_sda_size == 0) 02747 DOS_SDA_SEG_SIZE = 0x560; 02748 else if (dos_sda_size < 0x1A) 02749 DOS_SDA_SEG_SIZE = 0x1A; 02750 else if (dos_sda_size > 32768) 02751 DOS_SDA_SEG_SIZE = 32768; 02752 else 02753 DOS_SDA_SEG_SIZE = (dos_sda_size + 0xF) & (~0xF); /* round up to paragraph */ 02754 02755 /* msdos 2.x and msdos 5.x modes, if HMA is involved, require us to take the first 256 bytes of HMA 02756 * in order for "F01D:FEF0" to work properly whether or not A20 is enabled. Our direct mode doesn't 02757 * jump through that address, and therefore doesn't need it. */ 02758 if (dos_in_hma && 02759 cpm_compat_mode != CPM_COMPAT_OFF && 02760 cpm_compat_mode != CPM_COMPAT_DIRECT) { 02761 LOG(LOG_MISC,LOG_DEBUG)("DOS: CP/M compatibility method with DOS in HMA requires mirror of entry point in HMA."); 02762 if (dos_initial_hma_free > 0xFF00) { 02763 dos_initial_hma_free = 0xFF00; 02764 LOG(LOG_MISC,LOG_DEBUG)("DOS: CP/M compatibility method requires reduction of HMA free space to accomodate."); 02765 } 02766 } 02767 02768 if ((int)MAXENV < 0) MAXENV = 65535; 02769 if ((int)ENV_KEEPFREE < 0) ENV_KEEPFREE = 1024; 02770 02771 LOG(LOG_MISC,LOG_DEBUG)("DOS: MAXENV=%u ENV_KEEPFREE=%u",MAXENV,ENV_KEEPFREE); 02772 02773 if (ENV_KEEPFREE < 83) 02774 LOG_MSG("DOS: ENV_KEEPFREE is below 83 bytes. DOS programs that rely on undocumented data following the environment block may break."); 02775 02776 if (dbg_zero_on_dos_allocmem) { 02777 LOG_MSG("Debug option enabled: INT 21h memory allocation will always clear memory block before returning\n"); 02778 } 02779 02780 if (minimum_mcb_segment > 0x8000) minimum_mcb_segment = 0x8000; /* FIXME: Clip against available memory */ 02781 02782 /* we make use of the DOS_GetMemory() function for the dynamic allocation */ 02783 if (private_always_from_umb) { 02784 DOS_GetMemory_Choose(); /* the pool starts in UMB */ 02785 if (minimum_mcb_segment == 0) 02786 DOS_MEM_START = IS_PC98_ARCH ? 0x80 : 0x70; /* funny behavior in some games suggests the MS-DOS kernel loads a bit higher on PC-98 */ 02787 else 02788 DOS_MEM_START = minimum_mcb_segment; 02789 02790 if (DOS_MEM_START < 0x40) 02791 LOG_MSG("DANGER, DANGER! DOS_MEM_START has been set to within the interrupt vector table! Proceed at your own risk!"); 02792 else if (DOS_MEM_START < 0x50) 02793 LOG_MSG("WARNING: DOS_MEM_START has been assigned to the BIOS data area! Proceed at your own risk!"); 02794 else if (DOS_MEM_START < 0x51) 02795 LOG_MSG("WARNING: DOS_MEM_START has been assigned to segment 0x50, which some programs may use as the Print Screen flag"); 02796 else if (DOS_MEM_START < 0x80 && IS_PC98_ARCH) 02797 LOG_MSG("CAUTION: DOS_MEM_START is less than 0x80 which may cause problems with some DOS games or applications relying on PC-98 BIOS state"); 02798 else if (DOS_MEM_START < 0x70) 02799 LOG_MSG("CAUTION: DOS_MEM_START is less than 0x70 which may cause problems with some DOS games or applications"); 02800 } 02801 else { 02802 if (minimum_dos_initial_private_segment == 0) 02803 DOS_PRIVATE_SEGMENT = IS_PC98_ARCH ? 0x80 : 0x70; /* funny behavior in some games suggests the MS-DOS kernel loads a bit higher on PC-98 */ 02804 else 02805 DOS_PRIVATE_SEGMENT = minimum_dos_initial_private_segment; 02806 02807 if (DOS_PRIVATE_SEGMENT < 0x50) 02808 LOG_MSG("DANGER, DANGER! DOS_PRIVATE_SEGMENT has been set too low!"); 02809 if (DOS_PRIVATE_SEGMENT < 0x80 && IS_PC98_ARCH) 02810 LOG_MSG("DANGER, DANGER! DOS_PRIVATE_SEGMENT has been set too low for PC-98 emulation!"); 02811 02812 if (MEM_TotalPages() > 0x9C) 02813 DOS_PRIVATE_SEGMENT_END = 0x9C00; 02814 else 02815 DOS_PRIVATE_SEGMENT_END = (Bit16u)((MEM_TotalPages() << (12 - 4)) - 1); /* NTS: Remember DOSBox's implementation reuses the last paragraph for UMB linkage */ 02816 } 02817 02818 LOG(LOG_MISC,LOG_DEBUG)("DOS kernel structures will be allocated from pool 0x%04x-0x%04x", 02819 DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1); 02820 02821 DOS_IHSEG = DOS_GetMemory(1,"DOS_IHSEG"); 02822 02823 /* DOS_INFOBLOCK_SEG contains the entire List of Lists, though the INT 21h call returns seg:offset with offset nonzero */ 02824 DOS_INFOBLOCK_SEG = DOS_GetMemory(0xC0,"DOS_INFOBLOCK_SEG"); // was 0x80 02825 02826 DOS_CONDRV_SEG = DOS_GetMemory(0x08,"DOS_CONDRV_SEG"); // was 0xA0 02827 DOS_CONSTRING_SEG = DOS_GetMemory(0x0A,"DOS_CONSTRING_SEG"); // was 0xA8 02828 DOS_SDA_SEG = DOS_GetMemory(DOS_SDA_SEG_SIZE>>4,"DOS_SDA_SEG"); // was 0xB2 (0xB2 + 0x56 = 0x108) 02829 DOS_SDA_OFS = 0; 02830 DOS_CDS_SEG = DOS_GetMemory(0x10,"DOS_CDA_SEG"); // was 0x108 02831 02832 LOG(LOG_MISC,LOG_DEBUG)("DOS kernel alloc:"); 02833 LOG(LOG_MISC,LOG_DEBUG)(" IHSEG: seg 0x%04x",DOS_IHSEG); 02834 LOG(LOG_MISC,LOG_DEBUG)(" infoblock: seg 0x%04x",DOS_INFOBLOCK_SEG); 02835 LOG(LOG_MISC,LOG_DEBUG)(" condrv: seg 0x%04x",DOS_CONDRV_SEG); 02836 LOG(LOG_MISC,LOG_DEBUG)(" constring: seg 0x%04x",DOS_CONSTRING_SEG); 02837 LOG(LOG_MISC,LOG_DEBUG)(" SDA: seg 0x%04x:0x%04x %u bytes",DOS_SDA_SEG,DOS_SDA_OFS,DOS_SDA_SEG_SIZE); 02838 LOG(LOG_MISC,LOG_DEBUG)(" CDS: seg 0x%04x",DOS_CDS_SEG); 02839 LOG(LOG_MISC,LOG_DEBUG)("[private segment @ this point 0x%04x-0x%04x mem=0x%04lx]", 02840 DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END, 02841 (unsigned long)(MEM_TotalPages() << (12 - 4))); 02842 02843 callback[0].Install(DOS_20Handler,CB_IRET,"DOS Int 20"); 02844 callback[0].Set_RealVec(0x20); 02845 02846 callback[1].Install(DOS_21Handler,CB_INT21,"DOS Int 21"); 02847 callback[1].Set_RealVec(0x21); 02848 //Pseudo code for int 21 02849 // sti 02850 // callback 02851 // iret 02852 // retf <- int 21 4c jumps here to mimic a retf Cyber 02853 02854 callback[2].Install(DOS_25Handler,CB_RETF_STI,"DOS Int 25"); 02855 callback[2].Set_RealVec(0x25); 02856 02857 callback[3].Install(DOS_26Handler,CB_RETF_STI,"DOS Int 26"); 02858 callback[3].Set_RealVec(0x26); 02859 02860 callback[4].Install(DOS_27Handler,CB_IRET,"DOS Int 27"); 02861 callback[4].Set_RealVec(0x27); 02862 02863 callback[5].Install(NULL,CB_IRET/*CB_INT28*/,"DOS idle"); 02864 callback[5].Set_RealVec(0x28); 02865 02866 if (IS_PC98_ARCH) { 02867 // PC-98 also has INT 29h but the behavior of some games suggest that it is handled 02868 // the same as CON device output. Apparently the reason Touhou Project has been unable 02869 // to clear the screen is that it uses INT 29h to directly send ANSI codes rather than 02870 // standard I/O calls to write to the CON device. 02871 callback[6].Install(INT29_HANDLER,CB_IRET,"CON Output Int 29"); 02872 callback[6].Set_RealVec(0x29); 02873 } 02874 else { 02875 // FIXME: Really? Considering the main CON device emulation has ANSI.SYS emulation 02876 // you'd think that this would route it through the same. 02877 callback[6].Install(NULL,CB_INT29,"CON Output Int 29"); 02878 callback[6].Set_RealVec(0x29); 02879 // pseudocode for CB_INT29: 02880 // push ax 02881 // mov ah, 0x0e 02882 // int 0x10 02883 // pop ax 02884 // iret 02885 } 02886 02887 if (!IS_PC98_ARCH) { 02888 /* DOS installs a handler for INT 1Bh */ 02889 callback[7].Install(BIOS_1BHandler,CB_IRET,"BIOS 1Bh"); 02890 callback[7].Set_RealVec(0x1B); 02891 } 02892 02893 callback[8].Install(DOS_CPMHandler,CB_CPM,"DOS/CPM Int 30-31"); 02894 int30=RealGetVec(0x30); 02895 int31=RealGetVec(0x31); 02896 mem_writeb(0x30*4,(Bit8u)0xea); // jmpf 02897 mem_unalignedwrited(0x30*4+1,callback[8].Get_RealPointer()); 02898 // pseudocode for CB_CPM: 02899 // pushf 02900 // ... the rest is like int 21 02901 02902 if (IS_PC98_ARCH) { 02903 /* Any interrupt vector pointing to the INT stub in the BIOS must be rewritten to point to a JMP to the stub 02904 * residing in the DOS segment (60h) because some PC-98 resident drivers use segment 60h as a check for 02905 * installed vs uninstalled (MUSIC.COM, Peret em Heru) */ 02906 Bit16u sg = DOS_GetMemory(1/*paragraph*/,"INT stub trampoline"); 02907 PhysPt sgp = (PhysPt)sg << (PhysPt)4u; 02908 02909 /* Re-base the pointer so the segment is 0x60 */ 02910 Bit32u veco = sgp - 0x600; 02911 if (veco >= 0xFFF0u) E_Exit("INT stub trampoline out of bounds"); 02912 Bit32u vecp = RealMake(0x60,(Bit16u)veco); 02913 02914 mem_writeb(sgp+0,0xEA); 02915 mem_writed(sgp+1,BIOS_get_PC98_INT_STUB()); 02916 02917 for (unsigned int i=0;i < 0x100;i++) { 02918 Bit32u vec = RealGetVec(i); 02919 02920 if (vec == BIOS_get_PC98_INT_STUB()) 02921 mem_writed(i*4,vecp); 02922 } 02923 } 02924 02925 /* NTS: HMA support requires XMS. EMS support may switch on A20 if VCPI emulation requires the odd megabyte */ 02926 if ((!dos_in_hma || !section->Get_bool("xms")) && (MEM_A20_Enabled() || strcmp(section->Get_string("ems"),"false") != 0) && 02927 cpm_compat_mode != CPM_COMPAT_OFF && cpm_compat_mode != CPM_COMPAT_DIRECT) { 02928 /* hold on, only if more than 1MB of RAM and memory access permits it */ 02929 if (MEM_TotalPages() > 0x100 && MEM_PageMask() > 0xff/*more than 20-bit decoding*/) { 02930 LOG(LOG_MISC,LOG_WARN)("DOS not in HMA or XMS is disabled. This may break programs using the CP/M compatibility call method if the A20 gate is switched on."); 02931 } 02932 } 02933 02934 DOS_SetupFiles(); /* Setup system File tables */ 02935 DOS_SetupDevices(); /* Setup dos devices */ 02936 DOS_SetupTables(); 02937 02938 /* move the private segment elsewhere to avoid conflict with the MCB structure. 02939 * either set to 0 to cause the decision making to choose an upper memory address, 02940 * or allocate an additional private area and start the MCB just after that */ 02941 if (!private_always_from_umb) { 02942 DOS_MEM_START = DOS_GetMemory(0,"DOS_MEM_START"); // was 0x158 (pass 0 to alloc nothing, get the pointer) 02943 02944 DOS_GetMemory_reset(); 02945 DOS_PRIVATE_SEGMENT = 0; 02946 DOS_PRIVATE_SEGMENT_END = 0; 02947 if (!private_segment_in_umb) { 02948 /* If private segment is not being placed in UMB, then it must follow the DOS kernel. */ 02949 unsigned int seg; 02950 unsigned int segend; 02951 02952 seg = DOS_MEM_START; 02953 DOS_MEM_START += (Bit16u)DOS_PRIVATE_SEGMENT_Size; 02954 segend = DOS_MEM_START; 02955 02956 if (segend >= (MEM_TotalPages() << (12 - 4))) 02957 E_Exit("Insufficient room for private area"); 02958 02959 DOS_PRIVATE_SEGMENT = seg; 02960 DOS_PRIVATE_SEGMENT_END = segend; 02961 DOS_MEM_START = DOS_PRIVATE_SEGMENT_END; 02962 DOS_GetMemory_reset(); 02963 LOG_MSG("Private area, not stored in UMB on request, occupies 0x%04x-0x%04x [dynamic]\n", 02964 DOS_PRIVATE_SEGMENT,DOS_PRIVATE_SEGMENT_END-1); 02965 } 02966 } 02967 02968 if (minimum_mcb_segment != 0) { 02969 if (DOS_MEM_START < minimum_mcb_segment) 02970 DOS_MEM_START = minimum_mcb_segment; 02971 } 02972 02973 LOG(LOG_MISC,LOG_DEBUG)(" mem start: seg 0x%04x",DOS_MEM_START); 02974 02975 /* carry on setup */ 02976 DOS_SetupMemory(); /* Setup first MCB */ 02977 02978 /* NTS: The reason PC-98 has a higher minimum free is that the MS-DOS kernel 02979 * has a larger footprint in memory, including fixed locations that 02980 * some PC-98 games will read directly, and an ANSI driver. 02981 * 02982 * Some PC-98 games will have problems if loaded below a certain 02983 * threshhold as well. 02984 * 02985 * Valkyrie: 0xE10 is not enough for the game to run. If a specific 02986 * FM music selection is chosen, the remaining memory is 02987 * insufficient for the game to start the battle. 02988 * 02989 * The default assumes a DOS kernel and lower memory region of 32KB, 02990 * which might be a reasonable compromise so far. 02991 * 02992 * NOTES: A minimum mcb free value of at least 0xE10 is needed for Windows 3.1 02993 * 386 enhanced to start, else it will complain about insufficient memory (?). 02994 * To get Windows 3.1 to run, either set "minimum mcb free=e10" or run 02995 * "LOADFIX" before starting Windows 3.1 */ 02996 02997 /* NTS: There is a mysterious memory corruption issue with some DOS games 02998 * and applications when they are loaded at or around segment 0x800. 02999 * This should be looked into. In the meantime, setting the MCB 03000 * start segment before or after 0x800 helps to resolve these issues. 03001 * It also puts DOSBox-X at parity with main DOSBox SVN behavior. */ 03002 if (minimum_mcb_free == 0) 03003 minimum_mcb_free = IS_PC98_ARCH ? 0x800 : 0x700; 03004 else if (minimum_mcb_free < minimum_mcb_segment) 03005 minimum_mcb_free = minimum_mcb_segment; 03006 03007 LOG(LOG_MISC,LOG_DEBUG)(" min free: seg 0x%04x",minimum_mcb_free); 03008 03009 if (DOS_MEM_START < minimum_mcb_free) { 03010 Bit16u sg=0,tmp; 03011 03012 dos.psp(8); // DOS ownership 03013 03014 tmp = 1; // start small 03015 if (DOS_AllocateMemory(&sg,&tmp)) { 03016 if (sg < minimum_mcb_free) { 03017 LOG(LOG_MISC,LOG_DEBUG)(" min free pad: seg 0x%04x",sg); 03018 } 03019 else { 03020 DOS_FreeMemory(sg); 03021 sg = 0; 03022 } 03023 } 03024 else { 03025 sg=0; 03026 } 03027 03028 if (sg != 0 && sg < minimum_mcb_free) { 03029 tmp = minimum_mcb_free - sg; 03030 if (!DOS_ResizeMemory(sg,&tmp)) { 03031 LOG(LOG_MISC,LOG_DEBUG)(" WARNING: cannot resize min free pad"); 03032 } 03033 } 03034 } 03035 03036 DOS_SetupPrograms(); 03037 DOS_SetupMisc(); /* Some additional dos interrupts */ 03038 DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDrive(25); /* Else the next call gives a warning. */ 03039 DOS_SetDefaultDrive(25); 03040 03041 keep_private_area_on_boot = section->Get_bool("keep private area on boot"); 03042 03043 dos.version.major=5; 03044 dos.version.minor=0; 03045 dos.direct_output=false; 03046 dos.internal_output=false; 03047 03048 if (!strcmp(section->Get_string("lfn"), "true")) enablelfn=1; 03049 else if (!strcmp(section->Get_string("lfn"), "false")) enablelfn=0; 03050 else if (!strcmp(section->Get_string("lfn"), "autostart")) enablelfn=-2; 03051 else enablelfn=-1; 03052 03053 mainMenu.get_item("dos_lfn_auto").check(enablelfn==-1).refresh_item(mainMenu); 03054 mainMenu.get_item("dos_lfn_enable").check(enablelfn==1).refresh_item(mainMenu); 03055 mainMenu.get_item("dos_lfn_disable").check(enablelfn==0).refresh_item(mainMenu); 03056 03057 std::string ver = section->Get_string("ver"); 03058 if (!ver.empty()) { 03059 const char *s = ver.c_str(); 03060 03061 if (isdigit(*s)) { 03062 dos.version.minor=0; 03063 dos.version.major=(int)strtoul(s,(char**)(&s),10); 03064 if (*s == '.' || *s == ' ') { 03065 s++; 03066 if (isdigit(*s)) 03067 dos.version.minor=(*(s-1)=='.'&&strlen(s)==1?10:1)*(int)strtoul(s,(char**)(&s),10); 03068 } 03069 03070 /* warn about unusual version numbers */ 03071 if (dos.version.major >= 10 && dos.version.major <= 30) { 03072 LOG_MSG("WARNING, DOS version %u.%u: the major version is set to a " 03073 "range that may cause some DOS programs to think they are " 03074 "running from within an OS/2 DOS box.", 03075 dos.version.major, dos.version.minor); 03076 } 03077 else if (dos.version.major == 0 || dos.version.major > 8 || dos.version.minor > 90) 03078 LOG_MSG("WARNING: DOS version %u.%u is unusual, may confuse DOS programs", 03079 dos.version.major, dos.version.minor); 03080 } 03081 } 03082 uselfn = enablelfn==1 || ((enablelfn == -1 || enablelfn == -2) && dos.version.major>6); 03083 03084 if (IS_PC98_ARCH) { 03085 void PC98_InitDefFuncRow(void); 03086 PC98_InitDefFuncRow(); 03087 03088 real_writeb(0x60,0x113,0x01); /* 25-line mode */ 03089 } 03090 } 03091 ~DOS(){ 03092 infix=false; 03093 /* NTS: We do NOT free the drives! The OS may use them later! */ 03094 void DOS_ShutdownFiles(); 03095 DOS_ShutdownFiles(); 03096 void DOS_ShutdownDevices(void); 03097 DOS_ShutdownDevices(); 03098 RealSetVec(0x30,int30); 03099 RealSetVec(0x31,int31); 03100 } 03101 }; 03102 03103 static DOS* test = NULL; 03104 03105 void DOS_Write_HMA_CPM_jmp(void) { 03106 assert(test != NULL); 03107 test->DOS_Write_HMA_CPM_jmp(); 03108 } 03109 03110 Bit32u DOS_Get_CPM_entry_direct(void) { 03111 assert(test != NULL); 03112 return test->DOS_Get_CPM_entry_direct(); 03113 } 03114 03115 void DOS_ShutdownFiles() { 03116 if (Files != NULL) { 03117 for (Bitu i=0;i<DOS_FILES;i++) { 03118 if (Files[i] != NULL) { 03119 delete Files[i]; 03120 Files[i] = NULL; 03121 } 03122 } 03123 delete[] Files; 03124 Files = NULL; 03125 } 03126 } 03127 03128 void DOS_ShutdownDrives() { 03129 for (Bit16u i=0;i<DOS_DRIVES;i++) { 03130 delete Drives[i]; 03131 Drives[i] = NULL; 03132 } 03133 } 03134 03135 void update_pc98_function_row(unsigned char setting,bool force_redraw=false); 03136 void DOS_Casemap_Free(); 03137 03138 extern Bit8u ZDRIVE_NUM; 03139 03140 void DOS_EnableDriveMenu(char drv) { 03141 if (drv >= 'A' && drv <= 'Z') { 03142 std::string name; 03143 #if defined (WIN32) 03144 bool empty=!dos_kernel_disabled && Drives[drv-'A'] == NULL; 03145 name = std::string("drive_") + drv + "_mountauto"; 03146 mainMenu.get_item(name).enable(empty).refresh_item(mainMenu); 03147 name = std::string("drive_") + drv + "_mounthd"; 03148 mainMenu.get_item(name).enable(empty).refresh_item(mainMenu); 03149 name = std::string("drive_") + drv + "_mountcd"; 03150 mainMenu.get_item(name).enable(empty).refresh_item(mainMenu); 03151 name = std::string("drive_") + drv + "_mountfd"; 03152 mainMenu.get_item(name).enable(empty).refresh_item(mainMenu); 03153 name = std::string("drive_") + drv + "_mountimg"; 03154 mainMenu.get_item(name).enable(empty).refresh_item(mainMenu); 03155 #endif 03156 name = std::string("drive_") + drv + "_unmount"; 03157 mainMenu.get_item(name).enable(!dos_kernel_disabled && Drives[drv-'A'] != NULL && (drv-'A') != ZDRIVE_NUM).refresh_item(mainMenu); 03158 name = std::string("drive_") + drv + "_rescan"; 03159 mainMenu.get_item(name).enable(!dos_kernel_disabled && Drives[drv-'A'] != NULL).refresh_item(mainMenu); 03160 if (drv == 'A' || drv == 'C' || drv == 'D') { 03161 name = std::string("drive_") + drv + "_boot"; 03162 mainMenu.get_item(name).enable(!dos_kernel_disabled).refresh_item(mainMenu); 03163 #if defined (WIN32) 03164 name = std::string("drive_") + drv + "_bootimg"; 03165 mainMenu.get_item(name).enable(!dos_kernel_disabled).refresh_item(mainMenu); 03166 #endif 03167 } 03168 } 03169 } 03170 03171 void DOS_DoShutDown() { 03172 if (test != NULL) { 03173 delete test; 03174 test = NULL; 03175 } 03176 03177 if (IS_PC98_ARCH) update_pc98_function_row(0); 03178 03179 DOS_Casemap_Free(); 03180 03181 mainMenu.get_item("mapper_rescanall").enable(false).refresh_item(mainMenu); 03182 for (char drv='A';drv <= 'Z';drv++) DOS_EnableDriveMenu(drv); 03183 } 03184 03185 void DOS_ShutDown(Section* /*sec*/) { 03186 DOS_DoShutDown(); 03187 } 03188 03189 void DOS_GetMemory_reinit(); 03190 03191 void DOS_OnReset(Section* /*sec*/) { 03192 DOS_DoShutDown(); 03193 DOS_GetMemory_reinit(); 03194 } 03195 03196 void DOS_Startup(Section* sec) { 03197 (void)sec;//UNUSED 03198 03199 if (test == NULL) { 03200 DOS_GetMemLog.clear(); 03201 DOS_GetMemory_reinit(); 03202 LOG(LOG_MISC,LOG_DEBUG)("Allocating DOS kernel"); 03203 test = new DOS(control->GetSection("dos")); 03204 } 03205 03206 mainMenu.get_item("mapper_rescanall").enable(true).refresh_item(mainMenu); 03207 for (char drv='A';drv <= 'Z';drv++) DOS_EnableDriveMenu(drv); 03208 } 03209 03210 void DOS_RescanAll(bool pressed) { 03211 if (!pressed) return; 03212 if (dos_kernel_disabled) return; 03213 03214 LOG(LOG_DOSMISC,LOG_DEBUG)("Triggering rescan on all drives"); 03215 for(Bitu i =0; i<DOS_DRIVES;i++) { 03216 if (Drives[i]) Drives[i]->EmptyCache(); 03217 } 03218 } 03219 03220 void DOS_Init() { 03221 LOG(LOG_MISC,LOG_DEBUG)("Initializing DOS kernel (DOS_Init)"); 03222 LOG(LOG_MISC,LOG_DEBUG)("sizeof(union bootSector) = %u",(unsigned int)sizeof(union bootSector)); 03223 LOG(LOG_MISC,LOG_DEBUG)("sizeof(struct FAT_BootSector) = %u",(unsigned int)sizeof(struct FAT_BootSector)); 03224 LOG(LOG_MISC,LOG_DEBUG)("sizeof(direntry) = %u",(unsigned int)sizeof(direntry)); 03225 03226 /* this code makes assumptions! */ 03227 assert(sizeof(direntry) == 32); 03228 assert((SECTOR_SIZE_MAX % sizeof(direntry)) == 0); 03229 assert((MAX_DIRENTS_PER_SECTOR * sizeof(direntry)) == SECTOR_SIZE_MAX); 03230 03231 AddExitFunction(AddExitFunctionFuncPair(DOS_ShutDown),false); 03232 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(DOS_OnReset)); 03233 AddVMEventFunction(VM_EVENT_DOS_EXIT_KERNEL,AddVMEventFunctionFuncPair(DOS_ShutDown)); 03234 AddVMEventFunction(VM_EVENT_DOS_EXIT_REBOOT_KERNEL,AddVMEventFunctionFuncPair(DOS_ShutDown)); 03235 AddVMEventFunction(VM_EVENT_DOS_SURPRISE_REBOOT,AddVMEventFunctionFuncPair(DOS_OnReset)); 03236 03237 DOSBoxMenu::item *item; 03238 03239 MAPPER_AddHandler(DOS_RescanAll,MK_nothing,0,"rescanall","RescanAll",&item); 03240 item->enable(false).refresh_item(mainMenu); 03241 item->set_text("Rescan all drives"); 03242 for (char drv='A';drv <= 'Z';drv++) DOS_EnableDriveMenu(drv); 03243 } 03244 03245 void DOS_Int21_7139(char *name1, const char *name2) { 03246 (void)name2; 03247 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03248 *name1='\"'; 03249 char *p=name1+strlen(name1); 03250 while (*p==' '||*p==0) p--; 03251 *(p+1)='\"'; 03252 *(p+2)=0; 03253 if (DOS_MakeDir(name1)) { 03254 reg_ax=0; 03255 CALLBACK_SCF(false); 03256 } else { 03257 reg_ax=dos.errorcode; 03258 CALLBACK_SCF(true); 03259 } 03260 } 03261 03262 void DOS_Int21_713a(char *name1, const char *name2) { 03263 (void)name2; 03264 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03265 *name1='\"'; 03266 char *p=name1+strlen(name1); 03267 while (*p==' '||*p==0) p--; 03268 *(p+1)='\"'; 03269 *(p+2)=0; 03270 if (DOS_RemoveDir(name1)) { 03271 reg_ax=0; 03272 CALLBACK_SCF(false); 03273 } else { 03274 reg_ax=dos.errorcode; 03275 CALLBACK_SCF(true); 03276 LOG(LOG_MISC,LOG_NORMAL)("Remove dir failed on %s with error %X",name1,dos.errorcode); 03277 } 03278 } 03279 03280 void DOS_Int21_713b(char *name1, const char *name2) { 03281 (void)name2; 03282 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03283 *name1='\"'; 03284 char *p=name1+strlen(name1); 03285 while (*p==' '||*p==0) p--; 03286 *(p+1)='\"'; 03287 *(p+2)=0; 03288 if (DOS_ChangeDir(name1)) { 03289 reg_ax=0; 03290 CALLBACK_SCF(false); 03291 } else { 03292 reg_ax=dos.errorcode; 03293 CALLBACK_SCF(true); 03294 } 03295 } 03296 03297 void DOS_Int21_7141(char *name1, const char *name2) { 03298 (void)name2; 03299 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03300 *name1='\"'; 03301 char *p=name1+strlen(name1); 03302 while (*p==' '||*p==0) p--; 03303 *(p+1)='\"'; 03304 *(p+2)=0; 03305 if (DOS_UnlinkFile(name1)) { 03306 reg_ax=0; 03307 CALLBACK_SCF(false); 03308 } else { 03309 reg_ax=dos.errorcode; 03310 CALLBACK_SCF(true); 03311 } 03312 } 03313 03314 void DOS_Int21_7143(char *name1, const char *name2) { 03315 (void)name2; 03316 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03317 *name1='\"'; 03318 char *p=name1+strlen(name1); 03319 while (*p==' '||*p==0) p--; 03320 *(p+1)='\"'; 03321 *(p+2)=0; 03322 switch (reg_bl) { 03323 case 0x00: /* Get */ 03324 { 03325 Bit16u attr_val=reg_cx; 03326 if (DOS_GetFileAttr(name1,&attr_val)) { 03327 reg_cx=attr_val; 03328 reg_ax=0; 03329 CALLBACK_SCF(false); 03330 } else { 03331 CALLBACK_SCF(true); 03332 reg_ax=dos.errorcode; 03333 } 03334 break; 03335 }; 03336 case 0x01: /* Set */ 03337 if (DOS_SetFileAttr(name1,reg_cx)) { 03338 reg_ax=0; 03339 CALLBACK_SCF(false); 03340 } else { 03341 CALLBACK_SCF(true); 03342 reg_ax=dos.errorcode; 03343 } 03344 break; 03345 case 0x02: /* Get compressed file size */ 03346 { 03347 reg_ax=0; 03348 reg_dx=0; 03349 unsigned long size = DOS_GetCompressedFileSize(name1); 03350 if (size != (unsigned long)(-1l)) { 03351 #if defined (WIN32) 03352 reg_ax = LOWORD(size); 03353 reg_dx = HIWORD(size); 03354 #endif 03355 CALLBACK_SCF(false); 03356 } else { 03357 CALLBACK_SCF(true); 03358 reg_ax=dos.errorcode; 03359 } 03360 break; 03361 } 03362 case 0x03: 03363 case 0x05: 03364 case 0x07: 03365 { 03366 #if defined (WIN32) && !defined(HX_DOS) 03367 HANDLE hFile = DOS_CreateOpenFile(name1); 03368 if (hFile != INVALID_HANDLE_VALUE) { 03369 time_t clock = time(NULL), ttime; 03370 struct tm *t = localtime(&clock); 03371 FILETIME time; 03372 t->tm_isdst = -1; 03373 t->tm_sec = (((int)reg_cx) << 1) & 0x3e; 03374 t->tm_min = (((int)reg_cx) >> 5) & 0x3f; 03375 t->tm_hour = (((int)reg_cx) >> 11) & 0x1f; 03376 t->tm_mday = (int)(reg_di) & 0x1f; 03377 t->tm_mon = ((int)(reg_di >> 5) & 0x0f) - 1; 03378 t->tm_year = ((int)(reg_di >> 9) & 0x7f) + 80; 03379 ttime=mktime(t); 03380 LONGLONG ll = Int32x32To64(ttime, 10000000) + 116444736000000000 + (reg_bl==0x07?reg_si*100000:0); 03381 time.dwLowDateTime = (DWORD) ll; 03382 time.dwHighDateTime = (DWORD) (ll >> 32); 03383 if (!SetFileTime(hFile, reg_bl==0x07?&time:NULL,reg_bl==0x05?&time:NULL,reg_bl==0x03?&time:NULL)) { 03384 CloseHandle(hFile); 03385 CALLBACK_SCF(true); 03386 reg_ax=dos.errorcode; 03387 break; 03388 } 03389 CloseHandle(hFile); 03390 reg_ax=0; 03391 CALLBACK_SCF(false); 03392 } else 03393 #endif 03394 { 03395 CALLBACK_SCF(true); 03396 reg_ax=dos.errorcode; 03397 } 03398 break; 03399 } 03400 case 0x04: 03401 case 0x06: 03402 case 0x08: 03403 #if !defined(HX_DOS) 03404 struct stat status; 03405 if (DOS_GetFileAttrEx(name1, &status)) { 03406 const struct tm * ltime; 03407 time_t ttime=reg_bl==0x04?status.st_mtime:reg_bl==0x06?status.st_atime:status.st_ctime; 03408 if ((ltime=localtime(&ttime))!=0) { 03409 reg_cx=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); 03410 reg_di=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); 03411 } 03412 if (reg_bl==0x08) 03413 reg_si = 0; 03414 reg_ax=0; 03415 CALLBACK_SCF(false); 03416 } else 03417 #endif 03418 { 03419 CALLBACK_SCF(true); 03420 reg_ax=dos.errorcode; 03421 } 03422 break; 03423 default: 03424 E_Exit("DOS:Illegal LFN Attr call %2X",reg_bl); 03425 } 03426 } 03427 03428 void DOS_Int21_7147(char *name1, const char *name2) { 03429 (void)name2; 03430 DOS_PSP psp(dos.psp()); 03431 psp.StoreCommandTail(); 03432 if (DOS_GetCurrentDir(reg_dl,name1,true)) { 03433 MEM_BlockWrite(SegPhys(ds)+reg_si,name1,(Bitu)(strlen(name1)+1)); 03434 psp.RestoreCommandTail(); 03435 reg_ax=0; 03436 CALLBACK_SCF(false); 03437 } else { 03438 reg_ax=dos.errorcode; 03439 CALLBACK_SCF(true); 03440 } 03441 } 03442 03443 void DOS_Int21_714e(char *name1, char *name2) { 03444 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03445 *name1='\"'; 03446 char *p=name1+strlen(name1); 03447 while (*p==' '||*p==0) p--; 03448 *(p+1)='\"'; 03449 *(p+2)=0; 03450 if (!DOS_GetSFNPath(name1,name2,false)) { 03451 reg_ax=dos.errorcode; 03452 CALLBACK_SCF(true); 03453 return; 03454 } 03455 Bit16u entry; 03456 Bit8u i,handle=(Bit8u)DOS_FILES; 03457 for (i=1;i<DOS_FILES;i++) { 03458 if (!Files[i]) { 03459 handle=i; 03460 break; 03461 } 03462 } 03463 if (handle==DOS_FILES) { 03464 reg_ax=DOSERR_TOO_MANY_OPEN_FILES; 03465 CALLBACK_SCF(true); 03466 return; 03467 } 03468 if (strlen(name2)>2&&name2[strlen(name2)-2]=='\\'&&name2[strlen(name2)-1]=='*') 03469 strcat(name2, ".*"); 03470 lfn_filefind_handle=handle; 03471 bool b=DOS_FindFirst(name2,reg_cx,false); 03472 lfn_filefind_handle=LFN_FILEFIND_NONE; 03473 int error=dos.errorcode; 03474 Bit16u attribute = 0; 03475 if (!b&&!(strlen(name2)==3&&*(name2+1)==':'&&*(name2+2)=='\\')&&DOS_GetFileAttr(name2, &attribute) && (attribute&DOS_ATTR_DIRECTORY)) { 03476 strcat(name2,"\\*.*"); 03477 lfn_filefind_handle=handle; 03478 b=DOS_FindFirst(name2,reg_cx,false); 03479 lfn_filefind_handle=LFN_FILEFIND_NONE; 03480 error=dos.errorcode; 03481 } 03482 if (b) { 03483 DOS_PSP psp(dos.psp()); 03484 entry = psp.FindFreeFileEntry(); 03485 if (entry==0xff) { 03486 reg_ax=DOSERR_TOO_MANY_OPEN_FILES; 03487 CALLBACK_SCF(true); 03488 return; 03489 } 03490 if (handle>=DOS_DEVICES||!Devices[handle]) 03491 { 03492 int m=0; 03493 for (int i=1;i<DOS_DEVICES;i++) 03494 if (Devices[i]) m=i; 03495 Files[handle]=new DOS_Device(*Devices[m]); 03496 } 03497 else 03498 Files[handle]=new DOS_Device(*Devices[handle]); 03499 Files[handle]->AddRef(); 03500 psp.SetFileHandle(entry,handle); 03501 reg_ax=handle; 03502 DOS_DTA dta(dos.dta()); 03503 char finddata[CROSS_LEN]; 03504 int c=0; 03505 MEM_BlockWrite(SegPhys(es)+reg_di,finddata,dta.GetFindData((int)reg_si,finddata,&c)); 03506 reg_cx=c; 03507 CALLBACK_SCF(false); 03508 } else { 03509 dos.errorcode=error; 03510 reg_ax=dos.errorcode; 03511 CALLBACK_SCF(true); 03512 } 03513 } 03514 03515 void DOS_Int21_714f(const char *name1, const char *name2) { 03516 (void)name1; 03517 (void)name2; 03518 Bit8u handle=(Bit8u)reg_bx; 03519 if (!handle || handle>=DOS_FILES || !Files[handle]) { 03520 reg_ax=DOSERR_INVALID_HANDLE; 03521 CALLBACK_SCF(true); 03522 return; 03523 } 03524 lfn_filefind_handle=handle; 03525 if (DOS_FindNext()) { 03526 DOS_DTA dta(dos.dta()); 03527 char finddata[CROSS_LEN]; 03528 int c=0; 03529 MEM_BlockWrite(SegPhys(es)+reg_di,finddata,dta.GetFindData((int)reg_si,finddata,&c)); 03530 reg_cx=c; 03531 CALLBACK_SCF(false); 03532 reg_ax=0x4f00+handle; 03533 } else { 03534 reg_ax=dos.errorcode; 03535 CALLBACK_SCF(true); 03536 } 03537 lfn_filefind_handle=LFN_FILEFIND_NONE; 03538 } 03539 03540 void DOS_Int21_7156(char *name1, char *name2) { 03541 MEM_StrCopy(SegPhys(ds)+reg_dx,name1+1,DOSNAMEBUF); 03542 *name1='\"'; 03543 char *p=name1+strlen(name1); 03544 while (*p==' '||*p==0) p--; 03545 *(p+1)='\"'; 03546 *(p+2)=0; 03547 MEM_StrCopy(SegPhys(es)+reg_di,name2+1,DOSNAMEBUF); 03548 *name2='\"'; 03549 p=name2+strlen(name2); 03550 while (*p==' '||*p==0) p--; 03551 *(p+1)='\"'; 03552 *(p+2)=0; 03553 if (DOS_Rename(name1,name2)) { 03554 reg_ax=0; 03555 CALLBACK_SCF(false); 03556 } else { 03557 reg_ax=dos.errorcode; 03558 CALLBACK_SCF(true); 03559 } 03560 } 03561 void DOS_Int21_7160(char *name1, char *name2) { 03562 MEM_StrCopy(SegPhys(ds)+reg_si,name1+1,DOSNAMEBUF); 03563 *name1='\"'; 03564 char *p=name1+strlen(name1); 03565 while (*p==' '||*p==0) p--; 03566 *(p+1)='\"'; 03567 *(p+2)=0; 03568 if (DOS_Canonicalize(name1,name2)) { 03569 strcpy(name1,"\""); 03570 strcat(name1,name2); 03571 strcat(name1,"\""); 03572 switch(reg_cl) { 03573 case 0: // Canonoical path name 03574 strcpy(name2,name1); 03575 MEM_BlockWrite(SegPhys(es)+reg_di,name2,(Bitu)(strlen(name2)+1)); 03576 reg_ax=0; 03577 CALLBACK_SCF(false); 03578 break; 03579 case 1: // SFN path name 03580 if (DOS_GetSFNPath(name1,name2,false)) { 03581 MEM_BlockWrite(SegPhys(es)+reg_di,name2,(Bitu)(strlen(name2)+1)); 03582 reg_ax=0; 03583 CALLBACK_SCF(false); 03584 } else { 03585 reg_ax=2; 03586 CALLBACK_SCF(true); 03587 } 03588 break; 03589 case 2: // LFN path name 03590 if (DOS_GetSFNPath(name1,name2,true)) { 03591 MEM_BlockWrite(SegPhys(es)+reg_di,name2,(Bitu)(strlen(name2)+1)); 03592 reg_ax=0; 03593 CALLBACK_SCF(false); 03594 } else { 03595 reg_ax=2; 03596 CALLBACK_SCF(true); 03597 } 03598 break; 03599 default: 03600 E_Exit("DOS:Illegal LFN GetName call %2X",reg_cl); 03601 } 03602 } else { 03603 reg_ax=dos.errorcode; 03604 CALLBACK_SCF(true); 03605 } 03606 } 03607 03608 void DOS_Int21_716c(char *name1, const char *name2) { 03609 (void)name2; 03610 MEM_StrCopy(SegPhys(ds)+reg_si,name1+1,DOSNAMEBUF); 03611 *name1='\"'; 03612 char *p=name1+strlen(name1); 03613 while (*p==' '||*p==0) p--; 03614 *(p+1)='\"'; 03615 *(p+2)=0; 03616 if (DOS_OpenFileExtended(name1,reg_bx,reg_cx,reg_dx,®_ax,®_cx)) { 03617 CALLBACK_SCF(false); 03618 } else { 03619 reg_ax=dos.errorcode; 03620 CALLBACK_SCF(true); 03621 } 03622 } 03623 03624 void DOS_Int21_71a0(char *name1, char *name2) { 03625 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 03626 if (DOS_Canonicalize(name1,name2)) { 03627 if (reg_cx > 3) 03628 MEM_BlockWrite(SegPhys(es)+reg_di,"FAT",4); 03629 reg_ax=0; 03630 reg_bx=0x4006; 03631 reg_cx=0xff; 03632 reg_dx=0x104; 03633 CALLBACK_SCF(false); 03634 } else { 03635 reg_ax=dos.errorcode; 03636 CALLBACK_SCF(true); 03637 } 03638 } 03639 03640 void DOS_Int21_71a1(const char *name1, const char *name2) { 03641 (void)name1; 03642 (void)name2; 03643 Bit8u handle=(Bit8u)reg_bx; 03644 if (!handle || handle>=DOS_FILES || !Files[handle]) { 03645 reg_ax=DOSERR_INVALID_HANDLE; 03646 CALLBACK_SCF(true); 03647 return; 03648 } 03649 DOS_PSP psp(dos.psp()); 03650 Bit16u entry=psp.FindEntryByHandle(handle); 03651 if (entry>0&&entry!=0xff) psp.SetFileHandle(entry,0xff); 03652 if (entry>0&&Files[handle]->RemoveRef()<=0) { 03653 delete Files[handle]; 03654 Files[handle]=0; 03655 } 03656 reg_ax=0; 03657 CALLBACK_SCF(false); 03658 } 03659 03660 void DOS_Int21_71a6(const char *name1, const char *name2) { 03661 (void)name1; 03662 (void)name2; 03663 char buf[64]; 03664 unsigned long serial_number=0x1234,st=0,cdate=0,ctime=0,adate=0,atime=0,mdate=0,mtime=0; 03665 Bit8u entry=(Bit8u)reg_bx, handle; 03666 if (entry>=DOS_FILES) { 03667 reg_ax=DOSERR_INVALID_HANDLE; 03668 CALLBACK_SCF(true); 03669 return; 03670 } 03671 DOS_PSP psp(dos.psp()); 03672 for (unsigned int i=0;i<=DOS_FILES;i++) 03673 if (Files[i] && psp.FindEntryByHandle(i)==entry) 03674 handle=i; 03675 if (handle < DOS_FILES && Files[handle] && Files[handle]->name!=NULL) { 03676 Bit8u drive=Files[handle]->GetDrive(); 03677 if (Drives[drive]) { 03678 if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) { 03679 fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]); 03680 if (fdp != NULL) serial_number=fdp->GetSerial(); 03681 } 03682 #if defined (WIN32) 03683 if (!strncmp(Drives[drive]->GetInfo(),"local ",6) || !strncmp(Drives[drive]->GetInfo(),"CDRom ",6)) { 03684 localDrive* ldp = !strncmp(Drives[drive]->GetInfo(),"local ",6)?dynamic_cast<localDrive*>(Drives[drive]):dynamic_cast<cdromDrive*>(Drives[drive]); 03685 if (ldp != NULL) serial_number=ldp->GetSerial(); 03686 } 03687 #endif 03688 } 03689 struct stat status; 03690 if (DOS_GetFileAttrEx(Files[handle]->name, &status, Files[handle]->GetDrive())) { 03691 #if !defined(HX_DOS) 03692 time_t ttime; 03693 const struct tm * ltime; 03694 ttime=status.st_ctime; 03695 if ((ltime=localtime(&ttime))!=0) { 03696 ctime=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); 03697 cdate=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); 03698 } 03699 ttime=status.st_atime; 03700 if ((ltime=localtime(&ttime))!=0) { 03701 atime=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); 03702 adate=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); 03703 } 03704 ttime=status.st_mtime; 03705 if ((ltime=localtime(&ttime))!=0) { 03706 mtime=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec); 03707 mdate=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday); 03708 } 03709 #endif 03710 sprintf(buf,"%-4s%-4s%-4s%-4s%-4s%-4s%-4s%-4s%-4s%-4s%-4s%-4s%-4s",(char*)&st,(char*)&ctime,(char*)&cdate,(char*)&atime,(char*)&adate,(char*)&mtime,(char*)&mdate,(char*)&serial_number,(char*)&st,(char*)&st,(char*)&st,(char*)&st,(char*)&handle); 03711 for (int i=32;i<36;i++) buf[i]=0; 03712 buf[36]=(char)((Bit32u)status.st_size%256); 03713 buf[37]=(char)(((Bit32u)status.st_size%65536)/256); 03714 buf[38]=(char)(((Bit32u)status.st_size%16777216)/65536); 03715 buf[39]=(char)((Bit32u)status.st_size/16777216); 03716 buf[40]=(char)status.st_nlink; 03717 for (int i=41;i<47;i++) buf[i]=0; 03718 buf[52]=0; 03719 MEM_BlockWrite(SegPhys(ds)+reg_dx,buf,53); 03720 reg_ax=0; 03721 CALLBACK_SCF(false); 03722 } else { 03723 reg_ax=dos.errorcode; 03724 CALLBACK_SCF(true); 03725 } 03726 } else { 03727 reg_ax=dos.errorcode; 03728 CALLBACK_SCF(true); 03729 } 03730 } 03731 03732 void DOS_Int21_71a7(const char *name1, const char *name2) { 03733 (void)name1; 03734 (void)name2; 03735 switch (reg_bl) { 03736 case 0x00: 03737 reg_cl=mem_readb(SegPhys(ds)+reg_si); //not yet a proper implementation, 03738 reg_ch=mem_readb(SegPhys(ds)+reg_si+1); //but MS-DOS 7 and 4DOS DIR should 03739 reg_dl=mem_readb(SegPhys(ds)+reg_si+4); //show date/time correctly now 03740 reg_dh=mem_readb(SegPhys(ds)+reg_si+5); 03741 reg_bh=0; 03742 reg_ax=0; 03743 CALLBACK_SCF(false); 03744 break; 03745 case 0x01: 03746 mem_writeb(SegPhys(es)+reg_di,reg_cl); 03747 mem_writeb(SegPhys(es)+reg_di+1,reg_ch); 03748 mem_writeb(SegPhys(es)+reg_di+4,reg_dl); 03749 mem_writeb(SegPhys(es)+reg_di+5,reg_dh); 03750 reg_ax=0; 03751 CALLBACK_SCF(false); 03752 break; 03753 default: 03754 E_Exit("DOS:Illegal LFN TimeConv call %2X",reg_bl); 03755 } 03756 } 03757 03758 void DOS_Int21_71a8(char* name1, const char* name2) { 03759 (void)name2; 03760 if (reg_dh == 0 || reg_dh == 1) { 03761 MEM_StrCopy(SegPhys(ds)+reg_si,name1,DOSNAMEBUF); 03762 int i,j=0,o=0; 03763 char c[13]; 03764 const char* s = strrchr(name1, '.'); 03765 for (i=0;i<8;j++) { 03766 if (name1[j] == 0 || s-name1 <= j) break; 03767 if (name1[j] == '.') continue; 03768 c[o++] = toupper(name1[j]); 03769 i++; 03770 } 03771 if (s != NULL) { 03772 s++; 03773 if (s != 0 && reg_dh == 1) c[o++] = '.'; 03774 for (i=0;i<3;i++) { 03775 if (*(s+i) == 0) break; 03776 c[o++] = toupper(*(s+i)); 03777 } 03778 } 03779 assert(o <= 12); 03780 c[o] = 0; 03781 MEM_BlockWrite(SegPhys(es)+reg_di,c,strlen(c)+1); 03782 reg_ax=0; 03783 CALLBACK_SCF(false); 03784 } else { 03785 reg_ax=1; 03786 CALLBACK_SCF(true); 03787 } 03788 } 03789 03790 void DOS_Int21_71aa(char* name1, const char* name2) { 03791 (void)name2; 03792 if (reg_bh<3 && (reg_bl<1 || reg_bl>26)) { 03793 reg_ax = DOSERR_INVALID_DRIVE; 03794 CALLBACK_SCF(true); 03795 return; 03796 } 03797 switch (reg_bh) { 03798 case 0: 03799 { 03800 Bit8u drive=reg_bl-1; 03801 if (drive==DOS_GetDefaultDrive() || Drives[drive] || drive==25) { 03802 reg_ax = DOSERR_INVALID_DRIVE; 03803 CALLBACK_SCF(true); 03804 } else { 03805 MEM_StrCopy(SegPhys(ds)+reg_dx,name1,DOSNAMEBUF); 03806 char mountstring[DOS_PATHLENGTH+CROSS_LEN+20]; 03807 strcpy(mountstring,"MOUNT "); 03808 char temp_str[3] = { 0,0,0 }; 03809 temp_str[0]=(char)('A'+reg_bl-1); 03810 temp_str[1]=' '; 03811 strcat(mountstring,temp_str); 03812 strcat(mountstring,name1); 03813 strcat(mountstring," >nul"); 03814 DOS_Shell temp; 03815 temp.ParseLine(mountstring); 03816 if (Drives[drive]) { 03817 reg_ax=0; 03818 CALLBACK_SCF(false); 03819 } else { 03820 reg_ax=DOSERR_PATH_NOT_FOUND; 03821 CALLBACK_SCF(true); 03822 } 03823 } 03824 break; 03825 } 03826 case 1: 03827 { 03828 Bit8u drive=reg_bl-1; 03829 if (drive==DOS_GetDefaultDrive() || !Drives[drive] || drive==25) { 03830 reg_ax = DOSERR_INVALID_DRIVE; 03831 CALLBACK_SCF(true); 03832 } else { 03833 char mountstring[DOS_PATHLENGTH+CROSS_LEN+20]; 03834 strcpy(mountstring,"MOUNT -u "); 03835 char temp_str[2] = { 0,0 }; 03836 temp_str[0]=(char)('A'+reg_bl-1); 03837 strcat(mountstring,temp_str); 03838 strcat(mountstring," >nul"); 03839 DOS_Shell temp; 03840 temp.ParseLine(mountstring); 03841 if (!Drives[drive]) { 03842 reg_ax =0; 03843 CALLBACK_SCF(false); 03844 } else { 03845 reg_ax=5; 03846 CALLBACK_SCF(true); 03847 } 03848 } 03849 break; 03850 } 03851 case 2: 03852 { 03853 Bit8u drive=reg_bl>0?reg_bl-1:DOS_GetDefaultDrive(); 03854 if (Drives[drive]&&!strncmp(Drives[drive]->GetInfo(),"local directory ",16)) { 03855 strcpy(name1,Drives[drive]->GetInfo()+16); 03856 MEM_BlockWrite(SegPhys(ds)+reg_dx,name1,(Bitu)(strlen(name1)+1)); 03857 reg_ax=0; 03858 CALLBACK_SCF(false); 03859 } else { 03860 reg_ax=3; 03861 CALLBACK_SCF(true); 03862 } 03863 break; 03864 } 03865 default: 03866 E_Exit("DOS:Illegal LFN Subst call %2X",reg_bh); 03867 } 03868 } 03869 03870 //save state support 03871 extern void POD_Save_DOS_Devices( std::ostream& stream ); 03872 extern void POD_Save_DOS_DriveManager( std::ostream& stream ); 03873 extern void POD_Save_DOS_Files( std::ostream& stream ); 03874 extern void POD_Save_DOS_Memory( std::ostream& stream); 03875 extern void POD_Save_DOS_Mscdex( std::ostream& stream ); 03876 extern void POD_Save_DOS_Tables( std::ostream& stream ); 03877 03878 extern void POD_Load_DOS_Devices( std::istream& stream ); 03879 extern void POD_Load_DOS_DriveManager( std::istream& stream ); 03880 extern void POD_Load_DOS_Files( std::istream& stream ); 03881 extern void POD_Load_DOS_Memory( std::istream& stream ); 03882 extern void POD_Load_DOS_Mscdex( std::istream& stream ); 03883 extern void POD_Load_DOS_Tables( std::istream& stream ); 03884 03885 namespace 03886 { 03887 class SerializeDos : public SerializeGlobalPOD 03888 { 03889 public: 03890 SerializeDos() : SerializeGlobalPOD("Dos") 03891 {} 03892 03893 private: 03894 virtual void getBytes(std::ostream& stream) 03895 { 03896 SerializeGlobalPOD::getBytes(stream); 03897 03898 //*********************************************** 03899 //*********************************************** 03900 //*********************************************** 03901 // - pure data 03902 WRITE_POD( &dos_copybuf, dos_copybuf ); 03903 03904 // - pure data 03905 WRITE_POD( &dos.firstMCB, dos.firstMCB ); 03906 WRITE_POD( &dos.errorcode, dos.errorcode ); 03907 //WRITE_POD( &dos.env, dos.env ); 03908 //WRITE_POD( &dos.cpmentry, dos.cpmentry ); 03909 WRITE_POD( &dos.return_code, dos.return_code ); 03910 WRITE_POD( &dos.return_mode, dos.return_mode ); 03911 03912 WRITE_POD( &dos.current_drive, dos.current_drive ); 03913 WRITE_POD( &dos.verify, dos.verify ); 03914 WRITE_POD( &dos.breakcheck, dos.breakcheck ); 03915 WRITE_POD( &dos.echo, dos.echo ); 03916 WRITE_POD( &dos.direct_output, dos.direct_output ); 03917 WRITE_POD( &dos.internal_output, dos.internal_output ); 03918 03919 WRITE_POD( &dos.loaded_codepage, dos.loaded_codepage ); 03920 WRITE_POD( &dos.version.major, dos.version.major ); 03921 WRITE_POD( &dos.version.minor, dos.version.minor ); 03922 WRITE_POD( &countryNo, countryNo ); 03923 WRITE_POD( &uselfn, uselfn ); 03924 WRITE_POD( &lfn_filefind_handle, lfn_filefind_handle ); 03925 03926 POD_Save_DOS_Devices(stream); 03927 POD_Save_DOS_DriveManager(stream); 03928 POD_Save_DOS_Files(stream); 03929 POD_Save_DOS_Memory(stream); 03930 POD_Save_DOS_Mscdex(stream); 03931 POD_Save_DOS_Tables(stream); 03932 } 03933 03934 virtual void setBytes(std::istream& stream) 03935 { 03936 SerializeGlobalPOD::setBytes(stream); 03937 03938 //*********************************************** 03939 //*********************************************** 03940 //*********************************************** 03941 // - pure data 03942 READ_POD( &dos_copybuf, dos_copybuf ); 03943 03944 // - pure data 03945 READ_POD( &dos.firstMCB, dos.firstMCB ); 03946 READ_POD( &dos.errorcode, dos.errorcode ); 03947 //READ_POD( &dos.env, dos.env ); 03948 //READ_POD( &dos.cpmentry, dos.cpmentry ); 03949 READ_POD( &dos.return_code, dos.return_code ); 03950 READ_POD( &dos.return_mode, dos.return_mode ); 03951 03952 READ_POD( &dos.current_drive, dos.current_drive ); 03953 READ_POD( &dos.verify, dos.verify ); 03954 READ_POD( &dos.breakcheck, dos.breakcheck ); 03955 READ_POD( &dos.echo, dos.echo ); 03956 READ_POD( &dos.direct_output, dos.direct_output ); 03957 READ_POD( &dos.internal_output, dos.internal_output ); 03958 03959 READ_POD( &dos.loaded_codepage, dos.loaded_codepage ); 03960 READ_POD( &dos.version.major, dos.version.major ); 03961 READ_POD( &dos.version.minor, dos.version.minor ); 03962 READ_POD( &countryNo, countryNo ); 03963 READ_POD( &uselfn, uselfn ); 03964 READ_POD( &lfn_filefind_handle, lfn_filefind_handle ); 03965 03966 POD_Load_DOS_Devices(stream); 03967 POD_Load_DOS_DriveManager(stream); 03968 POD_Load_DOS_Files(stream); 03969 POD_Load_DOS_Memory(stream); 03970 POD_Load_DOS_Mscdex(stream); 03971 POD_Load_DOS_Tables(stream); 03972 } 03973 } dummy; 03974 }