DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  *  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(&reg_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,&reg_cx,&reg_al,&reg_dx)) reg_al=0xff;
00865             break;
00866         case 0x1c:      /* Get allocation info for specific drive */
00867             if (!DOS_GetAllocationInfo(reg_dl,&reg_cx,&reg_al,&reg_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,&reg_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,&reg_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(),&reg_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,&sectors,&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,&reg_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,&reg_ax)) {
01339                                 force_sfn = false;
01340                 CALLBACK_SCF(false);
01341             } else {
01342                                 force_sfn = false;
01343                                 if (uselfn&&DOS_OpenFile(name1,oldal,&reg_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,&reg_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,&reg_cx,&reg_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,&reg_ax,&reg_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,&sectors_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,&sectors_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,&para)) {
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,&reg_ax,&reg_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 }