DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/bios.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include <assert.h>
00020 #include "dosbox.h"
00021 #include "mem.h"
00022 #include "cpu.h"
00023 #include "bios.h"
00024 #include "regs.h"
00025 #include "cpu.h"
00026 #include "callback.h"
00027 #include "inout.h"
00028 #include "pic.h"
00029 #include "hardware.h"
00030 #include "pci_bus.h"
00031 #include "joystick.h"
00032 #include "mouse.h"
00033 #include "callback.h"
00034 #include "setup.h"
00035 #include "bios_disk.h"
00036 #include "serialport.h"
00037 #include "mapper.h"
00038 #include "vga.h"
00039 #include "pc98_gdc.h"
00040 #include "pc98_gdc_const.h"
00041 #include "regionalloctracking.h"
00042 #include "build_timestamp.h"
00043 extern bool PS1AudioCard;
00044 #include "parport.h"
00045 #include <time.h>
00046 #include <sys/timeb.h>
00047 
00048 #if C_EMSCRIPTEN
00049 # include <emscripten.h>
00050 #endif
00051 
00052 #if defined(_MSC_VER)
00053 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00054 # pragma warning(disable:4305) /* truncation from double to float */
00055 #endif
00056 
00057 #if defined(WIN32) && !defined(S_ISREG)
00058 # define S_ISREG(x) ((x & S_IFREG) == S_IFREG)
00059 #endif
00060 
00061 /* mouse.cpp */
00062 extern bool en_bios_ps2mouse;
00063 extern bool rom_bios_8x8_cga_font;
00064 extern bool pcibus_enable;
00065 
00066 uint32_t Keyb_ig_status();
00067 bool VM_Boot_DOSBox_Kernel();
00068 Bit32u MEM_get_address_bits();
00069 Bitu bios_post_parport_count();
00070 Bitu bios_post_comport_count();
00071 bool KEYBOARD_Report_BIOS_PS2Mouse();
00072 bool MEM_map_ROM_alias_physmem(Bitu start,Bitu end);
00073 
00074 bool bochs_port_e9 = false;
00075 bool isa_memory_hole_512kb = false;
00076 bool int15_wait_force_unmask_irq = false;
00077 
00078 int unhandled_irq_method = UNHANDLED_IRQ_SIMPLE;
00079 
00080 Bit16u biosConfigSeg=0;
00081 
00082 Bitu BIOS_DEFAULT_IRQ0_LOCATION = ~0u;       // (RealMake(0xf000,0xfea5))
00083 Bitu BIOS_DEFAULT_IRQ1_LOCATION = ~0u;       // (RealMake(0xf000,0xe987))
00084 Bitu BIOS_DEFAULT_IRQ07_DEF_LOCATION = ~0u;  // (RealMake(0xf000,0xff55))
00085 Bitu BIOS_DEFAULT_IRQ815_DEF_LOCATION = ~0u; // (RealMake(0xf000,0xe880))
00086 
00087 Bitu BIOS_DEFAULT_HANDLER_LOCATION = ~0u;    // (RealMake(0xf000,0xff53))
00088 
00089 Bitu BIOS_VIDEO_TABLE_LOCATION = ~0u;        // RealMake(0xf000,0xf0a4)
00090 Bitu BIOS_VIDEO_TABLE_SIZE = 0u;
00091 
00092 Bitu BIOS_DEFAULT_RESET_LOCATION = ~0u;      // RealMake(0xf000,0xe05b)
00093 
00094 bool allow_more_than_640kb = false;
00095 
00096 unsigned int APM_BIOS_connected_minor_version = 0;// what version the OS connected to us with. default to 1.0
00097 unsigned int APM_BIOS_minor_version = 2;    // what version to emulate e.g to emulate 1.2 set this to 2
00098 
00099 /* default bios type/version/date strings */
00100 const char* const bios_type_string = "IBM COMPATIBLE 486 BIOS COPYRIGHT The DOSBox Team.";
00101 const char* const bios_version_string = "DOSBox FakeBIOS v1.0";
00102 const char* const bios_date_string = "01/01/92";
00103 
00104 bool                        APM_inactivity_timer = true;
00105 
00106 RegionAllocTracking             rombios_alloc;
00107 
00108 Bitu                        rombios_minimum_location = 0xF0000; /* minimum segment allowed */
00109 Bitu                        rombios_minimum_size = 0x10000;
00110 
00111 bool MEM_map_ROM_physmem(Bitu start,Bitu end);
00112 bool MEM_unmap_physmem(Bitu start,Bitu end);
00113 
00114 static std::string bochs_port_e9_line;
00115 
00116 static void bochs_port_e9_flush() {
00117     if (!bochs_port_e9_line.empty()) {
00118         LOG_MSG("Bochs port E9h: %s",bochs_port_e9_line.c_str());
00119         bochs_port_e9_line.clear();
00120     }
00121 }
00122 
00123 void bochs_port_e9_write(Bitu port,Bitu val,Bitu /*iolen*/) {
00124     (void)port;//UNUSED
00125     if (val == '\n' || val == '\r') {
00126         bochs_port_e9_flush();
00127     }
00128     else {
00129         bochs_port_e9_line += (char)val;
00130         if (bochs_port_e9_line.length() >= 256)
00131             bochs_port_e9_flush();
00132     }
00133 }
00134 
00135 void ROMBIOS_DumpMemory() {
00136     rombios_alloc.logDump();
00137 }
00138 
00139 void ROMBIOS_SanityCheck() {
00140     rombios_alloc.sanityCheck();
00141 }
00142 
00143 Bitu ROMBIOS_MinAllocatedLoc() {
00144     Bitu r = rombios_alloc.getMinAddress();
00145 
00146     if (r > (0x100000u - rombios_minimum_size))
00147         r = (0x100000u - rombios_minimum_size);
00148 
00149     return r & ~0xFFFu;
00150 }
00151 
00152 void ROMBIOS_FreeUnusedMinToLoc(Bitu phys) {
00153     Bitu new_phys;
00154 
00155     if (rombios_minimum_location & 0xFFF) E_Exit("ROMBIOS: FreeUnusedMinToLoc minimum location not page aligned");
00156 
00157     phys &= ~0xFFFUL;
00158     new_phys = rombios_alloc.freeUnusedMinToLoc(phys) & (~0xFFFUL);
00159     assert(new_phys >= phys);
00160     if (phys < new_phys) MEM_unmap_physmem(phys,new_phys-1);
00161     rombios_minimum_location = new_phys;
00162     ROMBIOS_SanityCheck();
00163     ROMBIOS_DumpMemory();
00164 }
00165 
00166 bool ROMBIOS_FreeMemory(Bitu phys) {
00167     return rombios_alloc.freeMemory(phys);
00168 }
00169 
00170 Bitu ROMBIOS_GetMemory(Bitu bytes,const char *who,Bitu alignment,Bitu must_be_at) {
00171     return rombios_alloc.getMemory(bytes,who,alignment,must_be_at);
00172 }
00173 
00174 void ROMBIOS_InitForCustomBIOS(void) {
00175     rombios_alloc.initSetRange(0xD8000,0xE0000);
00176 }
00177 
00178 static IO_Callout_t dosbox_int_iocallout = IO_Callout_t_none;
00179 
00180 static unsigned char dosbox_int_register_shf = 0;
00181 static uint32_t dosbox_int_register = 0;
00182 static unsigned char dosbox_int_regsel_shf = 0;
00183 static uint32_t dosbox_int_regsel = 0;
00184 static bool dosbox_int_error = false;
00185 static bool dosbox_int_busy = false;
00186 static const char *dosbox_int_version = "DOSBox-X integration device v1.0";
00187 static const char *dosbox_int_ver_read = NULL;
00188 
00189 struct dosbox_int_saved_state {
00190     unsigned char   dosbox_int_register_shf;
00191     uint32_t        dosbox_int_register;
00192     unsigned char   dosbox_int_regsel_shf;
00193     uint32_t        dosbox_int_regsel;
00194     bool            dosbox_int_error;
00195     bool            dosbox_int_busy;
00196 };
00197 
00198 #define DOSBOX_INT_SAVED_STATE_MAX      4
00199 
00200 struct dosbox_int_saved_state       dosbox_int_saved[DOSBOX_INT_SAVED_STATE_MAX];
00201 int                                 dosbox_int_saved_sp = -1;
00202 
00203 /* for use with interrupt handlers in DOS/Windows that need to save IG state
00204  * to ensure that IG state is restored on return in order to not interfere
00205  * with anything userspace is doing (as an alternative to wrapping all access
00206  * in CLI/STI or PUSHF/CLI/POPF) */
00207 bool dosbox_int_push_save_state(void) {
00208 
00209     if (dosbox_int_saved_sp >= (DOSBOX_INT_SAVED_STATE_MAX-1))
00210         return false;
00211 
00212     struct dosbox_int_saved_state *ss = &dosbox_int_saved[++dosbox_int_saved_sp];
00213 
00214     ss->dosbox_int_register_shf =       dosbox_int_register_shf;
00215     ss->dosbox_int_register =           dosbox_int_register;
00216     ss->dosbox_int_regsel_shf =         dosbox_int_regsel_shf;
00217     ss->dosbox_int_regsel =             dosbox_int_regsel;
00218     ss->dosbox_int_error =              dosbox_int_error;
00219     ss->dosbox_int_busy =               dosbox_int_busy;
00220     return true;
00221 }
00222 
00223 bool dosbox_int_pop_save_state(void) {
00224     if (dosbox_int_saved_sp < 0)
00225         return false;
00226 
00227     struct dosbox_int_saved_state *ss = &dosbox_int_saved[dosbox_int_saved_sp--];
00228 
00229     dosbox_int_register_shf =           ss->dosbox_int_register_shf;
00230     dosbox_int_register =               ss->dosbox_int_register;
00231     dosbox_int_regsel_shf =             ss->dosbox_int_regsel_shf;
00232     dosbox_int_regsel =                 ss->dosbox_int_regsel;
00233     dosbox_int_error =                  ss->dosbox_int_error;
00234     dosbox_int_busy =                   ss->dosbox_int_busy;
00235     return true;
00236 }
00237 
00238 bool dosbox_int_discard_save_state(void) {
00239     if (dosbox_int_saved_sp < 0)
00240         return false;
00241 
00242     dosbox_int_saved_sp--;
00243     return true;
00244 }
00245 
00246 extern bool user_cursor_locked;
00247 extern int user_cursor_x,user_cursor_y;
00248 extern int user_cursor_sw,user_cursor_sh;
00249 
00250 static std::string dosbox_int_debug_out;
00251 
00252 /* read triggered, update the regsel */
00253 void dosbox_integration_trigger_read() {
00254     dosbox_int_error = false;
00255 
00256     switch (dosbox_int_regsel) {
00257         case 0: /* Identification */
00258             dosbox_int_register = 0xD05B0740;
00259             break;
00260         case 1: /* test */
00261             break;
00262         case 2: /* version string */
00263             if (dosbox_int_ver_read == NULL)
00264                 dosbox_int_ver_read = dosbox_int_version;
00265 
00266             dosbox_int_register = 0;
00267             for (Bitu i=0;i < 4;i++) {
00268                 if (*dosbox_int_ver_read == 0) {
00269                     dosbox_int_ver_read = dosbox_int_version;
00270                     break;
00271                 }
00272 
00273                 dosbox_int_register += ((uint32_t)((unsigned char)(*dosbox_int_ver_read++))) << (uint32_t)(i * 8);
00274             }
00275             break;
00276         case 3: /* version number */
00277             dosbox_int_register = (0x01U/*major*/) + (0x00U/*minor*/ << 8U) + (0x00U/*subver*/ << 16U) + (0x01U/*bump*/ << 24U);
00278             break;
00279 
00280 //      case 0x804200: /* keyboard input injection -- not supposed to read */
00281 //          break;
00282 
00283         case 0x804201: /* keyboard status */
00284             dosbox_int_register = Keyb_ig_status();
00285             break;
00286 
00287         case 0x434D54: /* read user mouse status */
00288             dosbox_int_register =
00289                 (user_cursor_locked ? (1UL << 0UL) : 0UL);      /* bit 0 = mouse capture lock */
00290             break;
00291 
00292         case 0x434D55: /* read user mouse cursor position */
00293             dosbox_int_register = ((user_cursor_y & 0xFFFFUL) << 16UL) | (user_cursor_x & 0xFFFFUL);
00294             break;
00295 
00296         case 0x434D56: { /* read user mouse cursor position (normalized for Windows 3.x) */
00297             signed long long x = ((signed long long)user_cursor_x << 16LL) / (signed long long)(user_cursor_sw-1);
00298             signed long long y = ((signed long long)user_cursor_y << 16LL) / (signed long long)(user_cursor_sh-1);
00299             if (x < 0x0000LL) x = 0x0000LL;
00300             if (x > 0xFFFFLL) x = 0xFFFFLL;
00301             if (y < 0x0000LL) y = 0x0000LL;
00302             if (y > 0xFFFFLL) y = 0xFFFFLL;
00303             dosbox_int_register = ((unsigned int)y << 16UL) | (unsigned int)x;
00304             } break;
00305 
00306         case 0xC54010: /* Screenshot/capture trigger */
00307             /* TODO: This should also be hidden behind an enable switch, so that rogue DOS development
00308              *       can't retaliate if the user wants to capture video or screenshots. */
00309 #if (C_SSHOT)
00310             extern Bitu CaptureState;
00311             dosbox_int_register = 0x00000000; // available
00312             if (CaptureState & CAPTURE_IMAGE)
00313                 dosbox_int_register |= 1 << 0; // Image capture is in progress
00314             if (CaptureState & CAPTURE_VIDEO)
00315                 dosbox_int_register |= 1 << 1; // Video capture is in progress
00316             if (CaptureState & CAPTURE_WAVE)
00317                 dosbox_int_register |= 1 << 2; // WAVE capture is in progress
00318 #else
00319             dosbox_int_register = 0xC0000000; // not available (bit 31 set), not enabled (bit 30 set)
00320 #endif
00321             break;
00322 
00323         case 0xAA55BB66UL: /* interface reset result */
00324             break;
00325 
00326         default:
00327             dosbox_int_register = 0xAA55AA55;
00328             dosbox_int_error = true;
00329             break;
00330     };
00331 
00332     LOG(LOG_MISC,LOG_DEBUG)("DOSBox integration read 0x%08lx got 0x%08lx (err=%u)\n",
00333         (unsigned long)dosbox_int_regsel,
00334         (unsigned long)dosbox_int_register,
00335         dosbox_int_error?1:0);
00336 }
00337 
00338 unsigned int mouse_notify_mode = 0;
00339 // 0 = off
00340 // 1 = trigger as PS/2 mouse interrupt
00341 
00342 /* write triggered */
00343 void dosbox_integration_trigger_write() {
00344     dosbox_int_error = false;
00345 
00346     LOG(LOG_MISC,LOG_DEBUG)("DOSBox integration write 0x%08lx val 0x%08lx\n",
00347         (unsigned long)dosbox_int_regsel,
00348         (unsigned long)dosbox_int_register);
00349 
00350     switch (dosbox_int_regsel) {
00351         case 1: /* test */
00352             break;
00353 
00354         case 2: /* version string */
00355             dosbox_int_ver_read = NULL;
00356             break;
00357 
00358         case 0xDEB0: /* debug output (to log) */
00359             for (unsigned int b=0;b < 4;b++) {
00360                 unsigned char c = (unsigned char)(dosbox_int_register >> (b * 8U));
00361                 if (c == '\n' || dosbox_int_debug_out.length() >= 200) {
00362                     LOG_MSG("Client debug message: %s\n",dosbox_int_debug_out.c_str());
00363                     dosbox_int_debug_out.clear();
00364                 }
00365                 else if (c != 0) {
00366                     dosbox_int_debug_out += ((char)c);
00367                 }
00368                 else {
00369                     break;
00370                 }
00371             }
00372             dosbox_int_register = 0;
00373             break;
00374 
00375         case 0xDEB1: /* debug output clear */
00376             dosbox_int_debug_out.clear();
00377             break;
00378 
00379         case 0x52434D: /* release mouse capture 'MCR' */
00380             void GFX_ReleaseMouse(void);
00381             GFX_ReleaseMouse();
00382             break;
00383 
00384         case 0x804200: /* keyboard input injection */
00385             void Mouse_ButtonPressed(Bit8u button);
00386             void Mouse_ButtonReleased(Bit8u button);
00387             void pc98_keyboard_send(const unsigned char b);
00388             void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate);
00389             void KEYBOARD_AUX_Event(float x,float y,Bitu buttons,int scrollwheel);
00390             void KEYBOARD_AddBuffer(Bit16u data);
00391 
00392             switch ((dosbox_int_register>>8)&0xFF) {
00393                 case 0x00: // keyboard
00394                     if (IS_PC98_ARCH)
00395                         pc98_keyboard_send(dosbox_int_register&0xFF);
00396                     else
00397                         KEYBOARD_AddBuffer(dosbox_int_register&0xFF);
00398                     break;
00399                 case 0x01: // AUX
00400                     if (!IS_PC98_ARCH)
00401                         KEYBOARD_AddBuffer((dosbox_int_register&0xFF)|0x100/*AUX*/);
00402                     else   // no such interface in PC-98 mode
00403                         dosbox_int_error = true;
00404                     break;
00405                 case 0x08: // mouse button injection
00406                     if (dosbox_int_register&0x80) Mouse_ButtonPressed(dosbox_int_register&0x7F);
00407                     else Mouse_ButtonReleased(dosbox_int_register&0x7F);
00408                     break;
00409                 case 0x09: // mouse movement injection (X)
00410                     Mouse_CursorMoved(((dosbox_int_register>>16UL) / 256.0f) - 1.0f,0,0,0,true);
00411                     break;
00412                 case 0x0A: // mouse movement injection (Y)
00413                     Mouse_CursorMoved(0,((dosbox_int_register>>16UL) / 256.0f) - 1.0f,0,0,true);
00414                     break;
00415                 case 0x0B: // mouse scrollwheel injection
00416                     // TODO
00417                     break;
00418                 default:
00419                     dosbox_int_error = true;
00420                     break;
00421             }
00422             break;
00423 
00424 //      case 0x804201: /* keyboard status do not write */
00425 //          break;
00426 
00427         /* this command is used to enable notification of mouse movement over the windows even if the mouse isn't captured */
00428         case 0x434D55: /* read user mouse cursor position */
00429         case 0x434D56: /* read user mouse cursor position (normalized for Windows 3.x) */
00430             mouse_notify_mode = dosbox_int_register & 0xFF;
00431             LOG(LOG_MISC,LOG_DEBUG)("Mouse notify mode=%u",mouse_notify_mode);
00432             break;
00433  
00434         case 0xC54010: /* Screenshot/capture trigger */
00435 #if (C_SSHOT)
00436             void CAPTURE_ScreenShotEvent(bool pressed);
00437             void CAPTURE_VideoEvent(bool pressed);
00438 #endif
00439             void CAPTURE_WaveEvent(bool pressed);
00440 
00441             /* TODO: It would be wise to grant/deny access to this register through another dosbox.conf option
00442              *       so that rogue DOS development cannot shit-spam the capture folder */
00443 #if (C_SSHOT)
00444             if (dosbox_int_register & 1)
00445                 CAPTURE_ScreenShotEvent(true);
00446             if (dosbox_int_register & 2)
00447                 CAPTURE_VideoEvent(true);
00448 #endif
00449             if (dosbox_int_register & 4)
00450                 CAPTURE_WaveEvent(true);
00451             break;
00452 
00453         default:
00454             dosbox_int_register = 0x55AA55AA;
00455             dosbox_int_error = true;
00456             break;
00457     };
00458 }
00459 
00460 /* PORT 0x28: Index
00461  *      0x29: Data
00462  *      0x2A: Status(R) or Command(W)
00463  *      0x2B: Not yet assigned
00464  *
00465  *      Registers are 32-bit wide. I/O to index and data rotate through the
00466  *      bytes of the register depending on I/O length, meaning that one 32-bit
00467  *      I/O read will read the entire register, while four 8-bit reads will
00468  *      read one byte out of 4. */
00469 
00470 static Bitu dosbox_integration_port00_index_r(Bitu port,Bitu iolen) {
00471     (void)port;//UNUSED
00472     Bitu retb = 0;
00473     Bitu ret = 0;
00474 
00475     while (iolen > 0) {
00476         ret += ((dosbox_int_regsel >> (dosbox_int_regsel_shf * 8)) & 0xFFU) << (retb * 8);
00477         if ((++dosbox_int_regsel_shf) >= 4) dosbox_int_regsel_shf = 0;
00478         iolen--;
00479         retb++;
00480     }
00481 
00482     return ret;
00483 }
00484 
00485 static void dosbox_integration_port00_index_w(Bitu port,Bitu val,Bitu iolen) {
00486     (void)port;//UNUSED
00487     uint32_t msk;
00488 
00489     while (iolen > 0) {
00490         msk = 0xFFU << (dosbox_int_regsel_shf * 8);
00491         dosbox_int_regsel = (dosbox_int_regsel & ~msk) + ((val & 0xFF) << (dosbox_int_regsel_shf * 8));
00492         if ((++dosbox_int_regsel_shf) >= 4) dosbox_int_regsel_shf = 0;
00493         val >>= 8U;
00494         iolen--;
00495     }
00496 }
00497 
00498 static Bitu dosbox_integration_port01_data_r(Bitu port,Bitu iolen) {
00499     (void)port;//UNUSED
00500     Bitu retb = 0;
00501     Bitu ret = 0;
00502 
00503     while (iolen > 0) {
00504         if (dosbox_int_register_shf == 0) dosbox_integration_trigger_read();
00505         ret += ((dosbox_int_register >> (dosbox_int_register_shf * 8)) & 0xFFU) << (retb * 8);
00506         if ((++dosbox_int_register_shf) >= 4) dosbox_int_register_shf = 0;
00507         iolen--;
00508         retb++;
00509     }
00510 
00511     return ret;
00512 }
00513 
00514 static void dosbox_integration_port01_data_w(Bitu port,Bitu val,Bitu iolen) {
00515     (void)port;//UNUSED
00516     uint32_t msk;
00517 
00518     while (iolen > 0) {
00519         msk = 0xFFU << (dosbox_int_register_shf * 8);
00520         dosbox_int_register = (dosbox_int_register & ~msk) + ((val & 0xFF) << (dosbox_int_register_shf * 8));
00521         if ((++dosbox_int_register_shf) >= 4) dosbox_int_register_shf = 0;
00522         if (dosbox_int_register_shf == 0) dosbox_integration_trigger_write();
00523         val >>= 8U;
00524         iolen--;
00525     }
00526 }
00527 
00528 static Bitu dosbox_integration_port02_status_r(Bitu port,Bitu iolen) {
00529     (void)iolen;//UNUSED
00530     (void)port;//UNUSED
00531     /* status */
00532     /* 7:6 = regsel byte index
00533      * 5:4 = register byte index
00534      * 3:2 = reserved
00535      *   1 = error
00536      *   0 = busy */
00537     return
00538         ((unsigned int)dosbox_int_regsel_shf << 6u) + ((unsigned int)dosbox_int_register_shf << 4u) +
00539         (dosbox_int_error ? 2u : 0u) + (dosbox_int_busy ? 1u : 0u);
00540 }
00541 
00542 static void dosbox_integration_port02_command_w(Bitu port,Bitu val,Bitu iolen) {
00543     (void)port;
00544     (void)iolen;
00545     switch (val) {
00546         case 0x00: /* reset latch */
00547             dosbox_int_register_shf = 0;
00548             dosbox_int_regsel_shf = 0;
00549             break;
00550         case 0x01: /* flush write */
00551             if (dosbox_int_register_shf != 0) {
00552                 dosbox_integration_trigger_write();
00553                 dosbox_int_register_shf = 0;
00554             }
00555             break;
00556         case 0x20: /* push state */
00557             if (dosbox_int_push_save_state()) {
00558                 dosbox_int_register_shf = 0;
00559                 dosbox_int_regsel_shf = 0;
00560                 dosbox_int_error = false;
00561                 dosbox_int_busy = false;
00562                 dosbox_int_regsel = 0xAA55BB66;
00563                 dosbox_int_register = 0xD05B0C5;
00564                 LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state saved");
00565             }
00566             else {
00567                 LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to push state, stack overflow");
00568                 dosbox_int_error = true;
00569             }
00570             break;
00571         case 0x21: /* pop state */
00572             if (dosbox_int_pop_save_state()) {
00573                 LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state restored");
00574             }
00575             else {
00576                 LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to pop state, stack underflow");
00577                 dosbox_int_error = true;
00578             }
00579             break;
00580         case 0x22: /* discard state */
00581             if (dosbox_int_discard_save_state()) {
00582                 LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state discarded");
00583             }
00584             else {
00585                 LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to discard state, stack underflow");
00586                 dosbox_int_error = true;
00587             }
00588             break;
00589         case 0x23: /* discard all state */
00590             while (dosbox_int_discard_save_state());
00591             break;
00592         case 0xFE: /* clear error */
00593             dosbox_int_error = false;
00594             break;
00595         case 0xFF: /* reset interface */
00596             dosbox_int_busy = false;
00597             dosbox_int_error = false;
00598             dosbox_int_regsel = 0xAA55BB66;
00599             dosbox_int_register = 0xD05B0C5;
00600             break;
00601         default:
00602             dosbox_int_error = true;
00603             break;
00604     }
00605 }
00606 
00607 static IO_ReadHandler* const dosbox_integration_cb_ports_r[4] = {
00608     dosbox_integration_port00_index_r,
00609     dosbox_integration_port01_data_r,
00610     dosbox_integration_port02_status_r,
00611     NULL
00612 };
00613 
00614 static IO_ReadHandler* dosbox_integration_cb_port_r(IO_CalloutObject &co,Bitu port,Bitu iolen) {
00615     (void)co;
00616     (void)iolen;
00617     return dosbox_integration_cb_ports_r[port&3];
00618 }
00619 
00620 static IO_WriteHandler* const dosbox_integration_cb_ports_w[4] = {
00621     dosbox_integration_port00_index_w,
00622     dosbox_integration_port01_data_w,
00623     dosbox_integration_port02_command_w,
00624     NULL
00625 };
00626 
00627 static IO_WriteHandler* dosbox_integration_cb_port_w(IO_CalloutObject &co,Bitu port,Bitu iolen) {
00628     (void)co;
00629     (void)iolen;
00630     return dosbox_integration_cb_ports_w[port&3];
00631 }
00632 
00633 /* if mem_systems 0 then size_extended is reported as the real size else 
00634  * zero is reported. ems and xms can increase or decrease the other_memsystems
00635  * counter using the BIOS_ZeroExtendedSize call */
00636 static Bit16u size_extended;
00637 static unsigned int ISA_PNP_WPORT = 0x20B;
00638 static unsigned int ISA_PNP_WPORT_BIOS = 0;
00639 static IO_ReadHandleObject *ISAPNP_PNP_READ_PORT = NULL;        /* 0x200-0x3FF range */
00640 static IO_WriteHandleObject *ISAPNP_PNP_ADDRESS_PORT = NULL;        /* 0x279 */
00641 static IO_WriteHandleObject *ISAPNP_PNP_DATA_PORT = NULL;       /* 0xA79 */
00642 static IO_WriteHandleObject *BOCHS_PORT_E9 = NULL;
00643 //static unsigned char ISA_PNP_CUR_CSN = 0;
00644 static unsigned char ISA_PNP_CUR_ADDR = 0;
00645 static unsigned char ISA_PNP_CUR_STATE = 0;
00646 enum {
00647     ISA_PNP_WAIT_FOR_KEY=0,
00648     ISA_PNP_SLEEP,
00649     ISA_PNP_ISOLATE,
00650     ISA_PNP_CONFIG
00651 };
00652 
00653 const unsigned char isa_pnp_init_keystring[32] = {
00654     0x6A,0xB5,0xDA,0xED,0xF6,0xFB,0x7D,0xBE,
00655     0xDF,0x6F,0x37,0x1B,0x0D,0x86,0xC3,0x61,
00656     0xB0,0x58,0x2C,0x16,0x8B,0x45,0xA2,0xD1,
00657     0xE8,0x74,0x3A,0x9D,0xCE,0xE7,0x73,0x39
00658 };
00659 
00660 static RealPt INT15_apm_pmentry=0;
00661 static unsigned char ISA_PNP_KEYMATCH=0;
00662 static Bits other_memsystems=0;
00663 static bool apm_realmode_connected = false;
00664 void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word
00665 bool enable_integration_device_pnp=false;
00666 bool enable_integration_device=false;
00667 bool ISAPNPBIOS=false;
00668 bool APMBIOS=false;
00669 bool APMBIOS_pnp=false;
00670 bool APMBIOS_allow_realmode=false;
00671 bool APMBIOS_allow_prot16=false;
00672 bool APMBIOS_allow_prot32=false;
00673 int APMBIOS_connect_mode=0;
00674 
00675 enum {
00676     APMBIOS_CONNECT_REAL=0,
00677     APMBIOS_CONNECT_PROT16,
00678     APMBIOS_CONNECT_PROT32
00679 };
00680 
00681 unsigned int APMBIOS_connected_already_err() {
00682     switch (APMBIOS_connect_mode) {
00683         case APMBIOS_CONNECT_REAL:  return 0x02;
00684         case APMBIOS_CONNECT_PROT16:    return 0x05;
00685         case APMBIOS_CONNECT_PROT32:    return 0x07;
00686     }
00687 
00688     return 0x00;
00689 }
00690 
00691 ISAPnPDevice::ISAPnPDevice() {
00692     CSN = 0;
00693     logical_device = 0;
00694     memset(ident,0,sizeof(ident));
00695     ident_bp = 0;
00696     ident_2nd = 0;
00697     resource_data_len = 0;
00698     resource_data_pos = 0;
00699     resource_data = NULL;
00700     resource_ident = 0;
00701     alloc_res = NULL;
00702     alloc_write = 0;
00703     alloc_sz = 0;
00704 }
00705 
00706 bool ISAPnPDevice::alloc(size_t sz) {
00707     if (sz == alloc_sz)
00708         return true;
00709 
00710     if (alloc_res == resource_data) {
00711         resource_data_len = 0;
00712         resource_data_pos = 0;
00713         resource_data = NULL;
00714     }
00715     if (alloc_res != NULL)
00716         delete[] alloc_res;
00717 
00718     alloc_res = NULL;
00719     alloc_write = 0;
00720     alloc_sz = 0;
00721 
00722     if (sz == 0)
00723         return true;
00724     if (sz > 65536)
00725         return false;
00726 
00727     alloc_res = new unsigned char[sz];
00728     if (alloc_res == NULL) return false;
00729     memset(alloc_res,0xFF,sz);
00730     alloc_sz = sz;
00731     return true;
00732 }
00733 
00734 ISAPnPDevice::~ISAPnPDevice() {
00735     alloc(0);
00736 }
00737 
00738 void ISAPnPDevice::begin_write_res() {
00739     if (alloc_res == NULL) return;
00740 
00741     resource_data_pos = 0;
00742     resource_data_len = 0;
00743     resource_data = NULL;
00744     alloc_write = 0;
00745 }
00746 
00747 void ISAPnPDevice::write_byte(const unsigned char c) {
00748     if (alloc_res == NULL || alloc_write >= alloc_sz) return;
00749     alloc_res[alloc_write++] = c;
00750 }
00751 
00752 void ISAPnPDevice::write_begin_SMALLTAG(const ISAPnPDevice::SmallTags stag,unsigned char len) {
00753     if (len >= 8 || (unsigned int)stag >= 0x10) return;
00754     write_byte(((unsigned char)stag << 3) + len);
00755 }
00756 
00757 void ISAPnPDevice::write_begin_LARGETAG(const ISAPnPDevice::LargeTags stag,unsigned int len) {
00758     if (len >= 4096) return;
00759     write_byte(0x80 + ((unsigned char)stag));
00760     write_byte(len & 0xFF);
00761     write_byte(len >> 8);
00762 }
00763 
00764 void ISAPnPDevice::write_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) {
00765     write_byte((((unsigned char)c1 & 0x1FU) << 2) + (((unsigned char)c2 & 0x18U) >> 3));
00766     write_byte((((unsigned char)c2 & 0x07U) << 5) + (((unsigned char)c3 & 0x1FU)     ));
00767     write_byte((((unsigned char)c4 & 0x0FU) << 4) + (((unsigned char)c5 & 0x0FU)     ));
00768     write_byte((((unsigned char)c6 & 0x0FU) << 4) + (((unsigned char)c7 & 0x0FU)     ));
00769 }
00770 
00771 void ISAPnPDevice::write_Logical_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) {
00772     write_begin_SMALLTAG(SmallTags::LogicalDeviceID,5);
00773     write_Device_ID(c1,c2,c3,c4,c5,c6,c7);
00774     write_byte(0x00);
00775 }
00776 
00777 void ISAPnPDevice::write_Compatible_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) {
00778     write_begin_SMALLTAG(SmallTags::CompatibleDeviceID,4);
00779     write_Device_ID(c1,c2,c3,c4,c5,c6,c7);
00780 }
00781 
00782 void ISAPnPDevice::write_IRQ_Format(const uint16_t IRQ_mask,const unsigned char IRQ_signal_type) {
00783     bool write_irq_info = (IRQ_signal_type != 0);
00784 
00785     write_begin_SMALLTAG(SmallTags::IRQFormat,write_irq_info?3:2);
00786     write_byte(IRQ_mask & 0xFF);
00787     write_byte(IRQ_mask >> 8);
00788     if (write_irq_info) write_byte(((unsigned char)IRQ_signal_type & 0x0F));
00789 }
00790 
00791 void ISAPnPDevice::write_DMA_Format(const uint8_t DMA_mask,const unsigned char transfer_type_preference,const bool is_bus_master,const bool byte_mode,const bool word_mode,const unsigned char speed_supported) {
00792     write_begin_SMALLTAG(SmallTags::DMAFormat,2);
00793     write_byte(DMA_mask);
00794     write_byte(
00795         (transfer_type_preference & 0x03) |
00796         (is_bus_master ? 0x04 : 0x00) |
00797         (byte_mode ? 0x08 : 0x00) |
00798         (word_mode ? 0x10 : 0x00) |
00799         ((speed_supported & 3) << 5));
00800 }
00801 
00802 void ISAPnPDevice::write_IO_Port(const uint16_t min_port,const uint16_t max_port,const uint8_t count,const uint8_t alignment,const bool full16bitdecode) {
00803     write_begin_SMALLTAG(SmallTags::IOPortDescriptor,7);
00804     write_byte((full16bitdecode ? 0x01 : 0x00));
00805     write_byte(min_port & 0xFF);
00806     write_byte(min_port >> 8);
00807     write_byte(max_port & 0xFF);
00808     write_byte(max_port >> 8);
00809     write_byte(alignment);
00810     write_byte(count);
00811 }
00812 
00813 void ISAPnPDevice::write_Dependent_Function_Start(const ISAPnPDevice::DependentFunctionConfig cfg,const bool force) {
00814     bool write_cfg_byte = force || (cfg != ISAPnPDevice::DependentFunctionConfig::AcceptableDependentConfiguration);
00815 
00816     write_begin_SMALLTAG(SmallTags::StartDependentFunctions,write_cfg_byte ? 1 : 0);
00817     if (write_cfg_byte) write_byte((unsigned char)cfg);
00818 }
00819 
00820 void ISAPnPDevice::write_End_Dependent_Functions() {
00821     write_begin_SMALLTAG(SmallTags::EndDependentFunctions,0);
00822 }
00823 
00824 void ISAPnPDevice::write_nstring(const char *str,const size_t l) {
00825     (void)l;
00826 
00827     if (alloc_res == NULL || alloc_write >= alloc_sz) return;
00828 
00829     while (*str != 0 && alloc_write < alloc_sz)
00830         alloc_res[alloc_write++] = (unsigned char)(*str++);
00831 }
00832 
00833 void ISAPnPDevice::write_Identifier_String(const char *str) {
00834     const size_t l = strlen(str);
00835     if (l > 4096) return;
00836 
00837     write_begin_LARGETAG(LargeTags::IdentifierStringANSI,(unsigned int)l);
00838     if (l != 0) write_nstring(str,l);
00839 }
00840 
00841 void ISAPnPDevice::write_ISAPnP_version(unsigned char major,unsigned char minor,unsigned char vendor) {
00842     write_begin_SMALLTAG(SmallTags::PlugAndPlayVersionNumber,2);
00843     write_byte((major << 4) + minor);
00844     write_byte(vendor);
00845 }
00846 
00847 void ISAPnPDevice::write_END() {
00848     unsigned char sum = 0;
00849     size_t i;
00850 
00851     write_begin_SMALLTAG(SmallTags::EndTag,/*length*/1);
00852 
00853     for (i=0;i < alloc_write;i++) sum += alloc_res[i];
00854     write_byte((0x100 - sum) & 0xFF);
00855 }
00856 
00857 void ISAPnPDevice::end_write_res() {
00858     if (alloc_res == NULL) return;
00859 
00860     write_END();
00861 
00862     if (alloc_write >= alloc_sz) LOG(LOG_MISC,LOG_WARN)("ISA PNP generation overflow");
00863 
00864     resource_data_pos = 0;
00865     resource_data_len = alloc_sz; // the device usually has a reason for allocating the fixed size it does
00866     resource_data = alloc_res;
00867     alloc_write = 0;
00868 }
00869 
00870 void ISAPnPDevice::config(Bitu val) {
00871     (void)val;
00872 }
00873 
00874 void ISAPnPDevice::wakecsn(Bitu val) {
00875     (void)val;
00876     ident_bp = 0;
00877     ident_2nd = 0;
00878     resource_data_pos = 0;
00879     resource_ident = 0;
00880 }
00881 
00882 void ISAPnPDevice::select_logical_device(Bitu val) {
00883     (void)val;
00884 }
00885     
00886 void ISAPnPDevice::checksum_ident() {
00887     unsigned char checksum = 0x6a,bit;
00888     int i,j;
00889 
00890     for (i=0;i < 8;i++) {
00891         for (j=0;j < 8;j++) {
00892             bit = (ident[i] >> j) & 1;
00893             checksum = ((((checksum ^ (checksum >> 1)) & 1) ^ bit) << 7) | (checksum >> 1);
00894         }
00895     }
00896 
00897     ident[8] = checksum;
00898 }
00899 
00900 void ISAPnPDevice::on_pnp_key() {
00901     resource_ident = 0;
00902 }
00903 
00904 uint8_t ISAPnPDevice::read(Bitu addr) {
00905     (void)addr;
00906     return 0x00;
00907 }
00908 
00909 void ISAPnPDevice::write(Bitu addr,Bitu val) {
00910     (void)addr;
00911     (void)val;
00912 }
00913 
00914 #define MAX_ISA_PNP_DEVICES     64
00915 #define MAX_ISA_PNP_SYSDEVNODES     256
00916 
00917 static ISAPnPDevice *ISA_PNP_selected = NULL;
00918 static ISAPnPDevice *ISA_PNP_devs[MAX_ISA_PNP_DEVICES] = {NULL}; /* FIXME: free objects on shutdown */
00919 static Bitu ISA_PNP_devnext = 0;
00920 
00921 static const unsigned char ISAPnPIntegrationDevice_sysdev[] = {
00922     ISAPNP_IO_RANGE(
00923             0x01,                   /* decodes 16-bit ISA addr */
00924             0x28,0x28,              /* min-max range I/O port */
00925             0x04,0x04),             /* align=4 length=4 */
00926     ISAPNP_END
00927 };
00928 
00929 class ISAPnPIntegrationDevice : public ISAPnPDevice {
00930     public:
00931         ISAPnPIntegrationDevice() : ISAPnPDevice() {
00932             resource_ident = 0;
00933             resource_data = (unsigned char*)ISAPnPIntegrationDevice_sysdev;
00934             resource_data_len = sizeof(ISAPnPIntegrationDevice_sysdev);
00935             host_writed(ident+0,ISAPNP_ID('D','O','S',0x1,0x2,0x3,0x4)); /* DOS1234 test device */
00936             host_writed(ident+4,0xFFFFFFFFUL);
00937             checksum_ident();
00938         }
00939 };
00940 
00941 ISAPnPIntegrationDevice *isapnpigdevice = NULL;
00942 
00943 class ISAPNP_SysDevNode {
00944 public:
00945     ISAPNP_SysDevNode(const unsigned char *ir,size_t len,bool already_alloc=false) {
00946         if (already_alloc) {
00947             raw = (unsigned char*)ir;
00948             raw_len = len;
00949             own = false;
00950         }
00951         else {
00952             if (len > 65535) E_Exit("ISAPNP_SysDevNode data too long");
00953             raw = new unsigned char[(size_t)len+1u];
00954             if (ir == NULL) E_Exit("ISAPNP_SysDevNode cannot allocate buffer");
00955             memcpy(raw,ir,(size_t)len);
00956             raw_len = len;
00957             raw[len] = 0;
00958             own = true;
00959         }
00960     }
00961     virtual ~ISAPNP_SysDevNode() {
00962         if (own) delete[] raw;
00963     }
00964 public:
00965     unsigned char*      raw;
00966     size_t              raw_len;
00967     bool                own;
00968 };
00969 
00970 static ISAPNP_SysDevNode*   ISAPNP_SysDevNodes[MAX_ISA_PNP_SYSDEVNODES] = {NULL};
00971 static Bitu         ISAPNP_SysDevNodeLargest=0;
00972 static Bitu         ISAPNP_SysDevNodeCount=0;
00973 
00974 void ISA_PNP_FreeAllSysNodeDevs() {
00975     Bitu i;
00976 
00977     for (i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) {
00978         if (ISAPNP_SysDevNodes[i] != NULL) delete ISAPNP_SysDevNodes[i];
00979         ISAPNP_SysDevNodes[i] = NULL;
00980     }
00981 
00982     ISAPNP_SysDevNodeLargest=0;
00983     ISAPNP_SysDevNodeCount=0;
00984 }
00985 
00986 void ISA_PNP_FreeAllDevs() {
00987     Bitu i;
00988 
00989     for (i=0;i < MAX_ISA_PNP_DEVICES;i++) {
00990         if (ISA_PNP_devs[i] != NULL) {
00991             delete ISA_PNP_devs[i];
00992             ISA_PNP_devs[i] = NULL;
00993         }
00994     }
00995     for (i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) {
00996         if (ISAPNP_SysDevNodes[i] != NULL) delete ISAPNP_SysDevNodes[i];
00997         ISAPNP_SysDevNodes[i] = NULL;
00998     }
00999 
01000     ISAPNP_SysDevNodeLargest=0;
01001     ISAPNP_SysDevNodeCount=0;
01002 }
01003 
01004 void ISA_PNP_devreg(ISAPnPDevice *x) {
01005     if (ISA_PNP_devnext < MAX_ISA_PNP_DEVICES) {
01006         if (ISA_PNP_WPORT_BIOS == 0) ISA_PNP_WPORT_BIOS = ISA_PNP_WPORT;
01007         ISA_PNP_devs[ISA_PNP_devnext++] = x;
01008         x->CSN = ISA_PNP_devnext;
01009     }
01010 }
01011 
01012 static Bitu isapnp_read_port(Bitu port,Bitu /*iolen*/) {
01013     (void)port;//UNUSED
01014     Bitu ret=0xff;
01015 
01016     switch (ISA_PNP_CUR_ADDR) {
01017         case 0x01:  /* serial isolation */
01018                if (ISA_PNP_selected && ISA_PNP_selected->CSN == 0) {
01019                    if (ISA_PNP_selected->ident_bp < 72) {
01020                        if (ISA_PNP_selected->ident[ISA_PNP_selected->ident_bp>>3] & (1 << (ISA_PNP_selected->ident_bp&7)))
01021                            ret = ISA_PNP_selected->ident_2nd ? 0xAA : 0x55;
01022                        else
01023                            ret = 0xFF;
01024 
01025                        if (++ISA_PNP_selected->ident_2nd >= 2) {
01026                            ISA_PNP_selected->ident_2nd = 0;
01027                            ISA_PNP_selected->ident_bp++;
01028                        }
01029                    }
01030                }
01031                else {
01032                    ret = 0xFF;
01033                }
01034                break;
01035         case 0x04:  /* read resource data */
01036                if (ISA_PNP_selected) {
01037                    if (ISA_PNP_selected->resource_ident < 9)
01038                        ret = ISA_PNP_selected->ident[ISA_PNP_selected->resource_ident++];              
01039                    else {
01040                        /* real-world hardware testing shows that devices act as if there was some fixed block of ROM,
01041                         * that repeats every 128, 256, 512, or 1024 bytes if you just blindly read from this port. */
01042                        if (ISA_PNP_selected->resource_data_pos < ISA_PNP_selected->resource_data_len)
01043                            ret = ISA_PNP_selected->resource_data[ISA_PNP_selected->resource_data_pos++];
01044 
01045                        /* that means that if you read enough bytes the ROM loops back to returning the ident */
01046                        if (ISA_PNP_selected->resource_data_pos >= ISA_PNP_selected->resource_data_len) {
01047                            ISA_PNP_selected->resource_data_pos = 0;
01048                            ISA_PNP_selected->resource_ident = 0;
01049                        }
01050                    }
01051                }
01052                break;
01053         case 0x05:  /* read resource status */
01054                if (ISA_PNP_selected) {
01055                    /* real-world hardware testing shows that devices act as if there was some fixed block of ROM,
01056                     * that repeats every 128, 256, 512, or 1024 bytes if you just blindly read from this port.
01057                     * therefore, there's always a byte to return. */
01058                    ret = 0x01;  /* TODO: simulate hardware slowness */
01059                }
01060                break;
01061         case 0x06:  /* card select number */
01062                if (ISA_PNP_selected) ret = ISA_PNP_selected->CSN;
01063                break;
01064         case 0x07:  /* logical device number */
01065                if (ISA_PNP_selected) ret = ISA_PNP_selected->logical_device;
01066                break;
01067         default:    /* pass the rest down to the class */
01068                if (ISA_PNP_selected) ret = ISA_PNP_selected->read(ISA_PNP_CUR_ADDR);
01069                break;
01070     }
01071 
01072 //  if (1) LOG_MSG("PnP read(%02X) = %02X\n",ISA_PNP_CUR_ADDR,ret);
01073     return ret;
01074 }
01075 
01076 void isapnp_write_port(Bitu port,Bitu val,Bitu /*iolen*/) {
01077     Bitu i;
01078 
01079     if (port == 0x279) {
01080 //      if (1) LOG_MSG("PnP addr(%02X)\n",val);
01081         if (val == isa_pnp_init_keystring[ISA_PNP_KEYMATCH]) {
01082             if (++ISA_PNP_KEYMATCH == 32) {
01083 //              LOG_MSG("ISA PnP key -> going to sleep\n");
01084                 ISA_PNP_CUR_STATE = ISA_PNP_SLEEP;
01085                 ISA_PNP_KEYMATCH = 0;
01086                 for (i=0;i < MAX_ISA_PNP_DEVICES;i++) {
01087                     if (ISA_PNP_devs[i] != NULL) {
01088                         ISA_PNP_devs[i]->on_pnp_key();
01089                     }
01090                 }
01091             }
01092         }
01093         else {
01094             ISA_PNP_KEYMATCH = 0;
01095         }
01096 
01097         ISA_PNP_CUR_ADDR = val;
01098     }
01099     else if (port == 0xA79) {
01100 //      if (1) LOG_MSG("PnP write(%02X) = %02X\n",ISA_PNP_CUR_ADDR,val);
01101         switch (ISA_PNP_CUR_ADDR) {
01102             case 0x00: {    /* RD_DATA */
01103                 unsigned int np = ((val & 0xFF) << 2) | 3;
01104                 if (np != ISA_PNP_WPORT) {
01105                     if (ISAPNP_PNP_READ_PORT != NULL) {
01106                         ISAPNP_PNP_READ_PORT = NULL;
01107                         delete ISAPNP_PNP_READ_PORT;
01108                     }
01109 
01110                     if (np >= 0x200 && np <= 0x3FF) { /* allowable port I/O range according to spec */
01111                         LOG_MSG("PNP OS changed I/O read port to 0x%03X (from 0x%03X)\n",np,ISA_PNP_WPORT);
01112 
01113                         ISA_PNP_WPORT = np;
01114                         ISAPNP_PNP_READ_PORT = new IO_ReadHandleObject;
01115                         ISAPNP_PNP_READ_PORT->Install(ISA_PNP_WPORT,isapnp_read_port,IO_MB);
01116                     }
01117                     else {
01118                         LOG_MSG("PNP OS I/O read port disabled\n");
01119 
01120                         ISA_PNP_WPORT = 0;
01121                     }
01122 
01123                     if (ISA_PNP_selected != NULL) {
01124                         ISA_PNP_selected->ident_bp = 0;
01125                         ISA_PNP_selected->ident_2nd = 0;
01126                         ISA_PNP_selected->resource_data_pos = 0;
01127                     }
01128                 }
01129             } break;
01130             case 0x02:  /* config control */
01131                    if (val & 4) {
01132                        /* ALL CARDS RESET CSN to 0 */
01133                        for (i=0;i < MAX_ISA_PNP_DEVICES;i++) {
01134                            if (ISA_PNP_devs[i] != NULL) {
01135                                ISA_PNP_devs[i]->CSN = 0;
01136                            }
01137                        }
01138                    }
01139                    if (val & 2) ISA_PNP_CUR_STATE = ISA_PNP_WAIT_FOR_KEY;
01140                    if ((val & 1) && ISA_PNP_selected) ISA_PNP_selected->config(val);
01141                    for (i=0;i < MAX_ISA_PNP_DEVICES;i++) {
01142                        if (ISA_PNP_devs[i] != NULL) {
01143                            ISA_PNP_devs[i]->ident_bp = 0;
01144                            ISA_PNP_devs[i]->ident_2nd = 0;
01145                            ISA_PNP_devs[i]->resource_data_pos = 0;
01146                        }
01147                    }
01148                    break;
01149             case 0x03: {    /* wake[CSN] */
01150                 ISA_PNP_selected = NULL;
01151                 for (i=0;ISA_PNP_selected == NULL && i < MAX_ISA_PNP_DEVICES;i++) {
01152                     if (ISA_PNP_devs[i] == NULL)
01153                         continue;
01154                     if (ISA_PNP_devs[i]->CSN == val) {
01155                         ISA_PNP_selected = ISA_PNP_devs[i];
01156                         ISA_PNP_selected->wakecsn(val);
01157                     }
01158                 }
01159                 if (val == 0)
01160                     ISA_PNP_CUR_STATE = ISA_PNP_ISOLATE;
01161                 else
01162                     ISA_PNP_CUR_STATE = ISA_PNP_CONFIG;
01163                 } break;
01164             case 0x06:  /* card select number */
01165                 if (ISA_PNP_selected) ISA_PNP_selected->CSN = val;
01166                 break;
01167             case 0x07:  /* logical device number */
01168                 if (ISA_PNP_selected) ISA_PNP_selected->select_logical_device(val);
01169                 break;
01170             default:    /* pass the rest down to the class */
01171                 if (ISA_PNP_selected) ISA_PNP_selected->write(ISA_PNP_CUR_ADDR,val);
01172                 break;
01173         }
01174     }
01175 }
01176 
01177 static Bitu INT15_Handler(void);
01178 
01179 // FIXME: This initializes both APM BIOS and ISA PNP emulation!
01180 //        Need to separate APM BIOS init from ISA PNP init from ISA PNP BIOS init!
01181 //        It might also be appropriate to move this into the BIOS init sequence.
01182 void ISAPNP_Cfg_Reset(Section *sec) {
01183     (void)sec;//UNUSED
01184     Section_prop * section=static_cast<Section_prop *>(control->GetSection("cpu"));
01185 
01186     LOG(LOG_MISC,LOG_DEBUG)("Initializing ISA PnP emulation");
01187 
01188     enable_integration_device = section->Get_bool("integration device");
01189     enable_integration_device_pnp = section->Get_bool("integration device pnp");
01190     ISAPNPBIOS = section->Get_bool("isapnpbios");
01191     APMBIOS = section->Get_bool("apmbios");
01192     APMBIOS_pnp = section->Get_bool("apmbios pnp");
01193     APMBIOS_allow_realmode = section->Get_bool("apmbios allow realmode");
01194     APMBIOS_allow_prot16 = section->Get_bool("apmbios allow 16-bit protected mode");
01195     APMBIOS_allow_prot32 = section->Get_bool("apmbios allow 32-bit protected mode");
01196 
01197     std::string apmbiosver = section->Get_string("apmbios version");
01198 
01199     /* PC-98 does not have the IBM PC/AT APM BIOS interface */
01200     if (IS_PC98_ARCH) {
01201         APMBIOS = false;
01202         APMBIOS_pnp = false;
01203     }
01204 
01205     if (apmbiosver == "1.0")
01206         APM_BIOS_minor_version = 0;
01207     else if (apmbiosver == "1.1")
01208         APM_BIOS_minor_version = 1;
01209     else if (apmbiosver == "1.2")
01210         APM_BIOS_minor_version = 2;
01211     else//auto
01212         APM_BIOS_minor_version = 2;
01213 
01214     /* PC-98 does not have APM.
01215      * I *think* it has Plug & Play, but probably different from ISA PnP and specific to the C-Bus interface,
01216      * which I have no information on at this time --J.C. */
01217     if (IS_PC98_ARCH)
01218         return;
01219 
01220     LOG(LOG_MISC,LOG_DEBUG)("APM BIOS allow: real=%u pm16=%u pm32=%u version=1.%u",
01221         APMBIOS_allow_realmode,
01222         APMBIOS_allow_prot16,
01223         APMBIOS_allow_prot32,
01224         APM_BIOS_minor_version);
01225 
01226     if (APMBIOS && (APMBIOS_allow_prot16 || APMBIOS_allow_prot32) && INT15_apm_pmentry == 0) {
01227         Bitu cb,base;
01228 
01229         /* NTS: This is... kind of a terrible hack. It basically tricks Windows into executing our
01230          *      INT 15h handler as if the APM entry point. Except that instead of an actual INT 15h
01231          *      triggering the callback, a FAR CALL triggers the callback instead (CB_RETF not CB_IRET). */
01232         /* TODO: We should really consider moving the APM BIOS code in INT15_Handler() out into it's
01233          *       own function, then having the INT15_Handler() call it as well as directing this callback
01234          *       directly to it. If you think about it, this hack also lets the "APM entry point" invoke
01235          *       other arbitrary INT 15h calls which is not valid. */
01236 
01237         cb = CALLBACK_Allocate();
01238         INT15_apm_pmentry = CALLBACK_RealPointer(cb);
01239         LOG_MSG("Allocated APM BIOS pm entry point at %04x:%04x\n",INT15_apm_pmentry>>16,INT15_apm_pmentry&0xFFFF);
01240         CALLBACK_Setup(cb,INT15_Handler,CB_RETF,"APM BIOS protected mode entry point");
01241 
01242         /* NTS: Actually INT15_Handler is written to act like an interrupt (IRETF) type callback.
01243          *      Prior versions hacked this into something that responds by CB_RETF, however some
01244          *      poking around reveals that CALLBACK_SCF and friends still assume an interrupt
01245          *      stack, thus, the cause of random crashes in Windows was simply that we were
01246          *      flipping flag bits in the middle of the return address on the stack. The other
01247          *      source of random crashes is that the CF/ZF manipulation in INT 15h wasn't making
01248          *      it's way back to Windows, meaning that when APM BIOS emulation intended to return
01249          *      an error (by setting CF), Windows didn't get the memo (CF wasn't set on return)
01250          *      and acted as if the call succeeded, or worse, CF happened to be set on entry and
01251          *      was never cleared by APM BIOS emulation.
01252          *
01253          *      So what we need is:
01254          *
01255          *      PUSHF           ; put flags in right place
01256          *      PUSH    BP      ; dummy FAR pointer
01257          *      PUSH    BP      ; again
01258          *      <callback>
01259          *      POP     BP      ; drop it
01260          *      POP     BP      ; drop it
01261          *      POPF
01262          *      RETF
01263          *
01264          *      Then CALLBACK_SCF can work normally this way.
01265          *
01266          * NTS: We *still* need to separate APM BIOS calls from the general INT 15H emulation though... */
01267         base = Real2Phys(INT15_apm_pmentry);
01268         LOG_MSG("Writing code to %05x\n",(unsigned int)base);
01269 
01270         phys_writeb(base+0x00,0x9C);                             /* pushf */
01271         phys_writeb(base+0x01,0x55);                             /* push (e)bp */
01272         phys_writeb(base+0x02,0x55);                             /* push (e)bp */
01273 
01274         phys_writeb(base+0x03,(Bit8u)0xFE);                     //GRP 4
01275         phys_writeb(base+0x04,(Bit8u)0x38);                     //Extra Callback instruction
01276         phys_writew(base+0x05,(Bit16u)cb);                      //The immediate word
01277 
01278         phys_writeb(base+0x07,0x5D);                             /* pop (e)bp */
01279         phys_writeb(base+0x08,0x5D);                             /* pop (e)bp */
01280         phys_writeb(base+0x09,0x9D);                             /* popf */
01281         phys_writeb(base+0x0A,0xCB);                             /* retf */
01282     }
01283 }
01284 
01285 void ISAPNP_Cfg_Init() {
01286     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(ISAPNP_Cfg_Reset));
01287 }
01288 
01289 /* the PnP callback registered two entry points. One for real, one for protected mode. */
01290 static Bitu PNPentry_real,PNPentry_prot;
01291 
01292 static bool ISAPNP_Verify_BiosSelector(Bitu seg) {
01293     if (!cpu.pmode || (reg_flags & FLAG_VM)) {
01294         return (seg == 0xF000);
01295     } else if (seg == 0)
01296         return 0;
01297     else {
01298 #if 1
01299         /* FIXME: Always return true. But figure out how to ask DOSBox the linear->phys
01300               mapping to determine whether the segment's base address maps to 0xF0000.
01301               In the meantime disabling this check makes PnP BIOS emulation work with
01302               Windows 95 OSR2 which appears to give us a segment mapped to a virtual
01303               address rather than linearly mapped to 0xF0000 as Windows 95 original
01304               did. */
01305         return true;
01306 #else
01307         Descriptor desc;
01308         cpu.gdt.GetDescriptor(seg,desc);
01309 
01310         /* TODO: Check desc.Type() to make sure it's a writeable data segment */
01311         return (desc.GetBase() == 0xF0000);
01312 #endif
01313     }
01314 }
01315 
01316 static bool ISAPNP_CPU_ProtMode() {
01317     if (!cpu.pmode || (reg_flags & FLAG_VM))
01318         return 0;
01319 
01320     return 1;
01321 }
01322 
01323 static Bitu ISAPNP_xlate_address(Bitu far_ptr) {
01324     if (!cpu.pmode || (reg_flags & FLAG_VM))
01325         return Real2Phys(far_ptr);
01326     else {
01327         Descriptor desc;
01328         cpu.gdt.GetDescriptor(far_ptr >> 16,desc);
01329 
01330         /* TODO: Check desc.Type() to make sure it's a writeable data segment */
01331         return (desc.GetBase() + (far_ptr & 0xFFFF));
01332     }
01333 }
01334 
01335 static const unsigned char ISAPNP_sysdev_Keyboard[] = {
01336     ISAPNP_SYSDEV_HEADER(
01337             ISAPNP_ID('P','N','P',0x0,0x3,0x0,0x3), /* PNP0303 IBM Enhanced 101/102 key with PS/2 */
01338             ISAPNP_TYPE(0x09,0x00,0x00),        /* type: input, keyboard */
01339             0x0001 | 0x0002),           /* can't disable, can't configure */
01340     /*----------allocated--------*/
01341     ISAPNP_IO_RANGE(
01342             0x01,                   /* decodes 16-bit ISA addr */
01343             0x60,0x60,              /* min-max range I/O port */
01344             0x01,0x01),             /* align=1 length=1 */
01345     ISAPNP_IO_RANGE(
01346             0x01,                   /* decodes 16-bit ISA addr */
01347             0x64,0x64,              /* min-max range I/O port */
01348             0x01,0x01),             /* align=1 length=1 */
01349     ISAPNP_IRQ_SINGLE(
01350             1,                  /* IRQ 1 */
01351             0x09),                  /* HTE=1 LTL=1 */
01352     ISAPNP_END,
01353     /*----------possible--------*/
01354     ISAPNP_END,
01355     /*----------compatible--------*/
01356     ISAPNP_END
01357 };
01358 
01359 static const unsigned char ISAPNP_sysdev_Mouse[] = {
01360     ISAPNP_SYSDEV_HEADER(
01361             ISAPNP_ID('P','N','P',0x0,0xF,0x0,0xE), /* PNP0F0E Microsoft compatible PS/2 */
01362             ISAPNP_TYPE(0x09,0x02,0x00),        /* type: input, keyboard */
01363             0x0001 | 0x0002),           /* can't disable, can't configure */
01364     /*----------allocated--------*/
01365     ISAPNP_IRQ_SINGLE(
01366             12,                 /* IRQ 12 */
01367             0x09),                  /* HTE=1 LTL=1 */
01368     ISAPNP_END,
01369     /*----------possible--------*/
01370     ISAPNP_END,
01371     /*----------compatible--------*/
01372     ISAPNP_END
01373 };
01374 
01375 static const unsigned char ISAPNP_sysdev_DMA_Controller[] = {
01376     ISAPNP_SYSDEV_HEADER(
01377             ISAPNP_ID('P','N','P',0x0,0x2,0x0,0x0), /* PNP0200 AT DMA controller */
01378             ISAPNP_TYPE(0x08,0x01,0x00),        /* type: input, keyboard */
01379             0x0001 | 0x0002),           /* can't disable, can't configure */
01380     /*----------allocated--------*/
01381     ISAPNP_IO_RANGE(
01382             0x01,                   /* decodes 16-bit ISA addr */
01383             0x00,0x00,              /* min-max range I/O port (DMA channels 0-3) */
01384             0x10,0x10),             /* align=16 length=16 */
01385     ISAPNP_IO_RANGE(
01386             0x01,                   /* decodes 16-bit ISA addr */
01387             0x81,0x81,              /* min-max range I/O port (DMA page registers) */
01388             0x01,0x0F),             /* align=1 length=15 */
01389     ISAPNP_IO_RANGE(
01390             0x01,                   /* decodes 16-bit ISA addr */
01391             0xC0,0xC0,              /* min-max range I/O port (AT DMA channels 4-7) */
01392             0x20,0x20),             /* align=32 length=32 */
01393     ISAPNP_DMA_SINGLE(
01394             4,                  /* DMA 4 */
01395             0x01),                  /* 8/16-bit transfers, compatible speed */
01396     ISAPNP_END,
01397     /*----------possible--------*/
01398     ISAPNP_END,
01399     /*----------compatible--------*/
01400     ISAPNP_END
01401 };
01402 
01403 static const unsigned char ISAPNP_sysdev_PIC[] = {
01404     ISAPNP_SYSDEV_HEADER(
01405             ISAPNP_ID('P','N','P',0x0,0x0,0x0,0x0), /* PNP0000 Interrupt controller */
01406             ISAPNP_TYPE(0x08,0x00,0x01),        /* type: ISA interrupt controller */
01407             0x0001 | 0x0002),           /* can't disable, can't configure */
01408     /*----------allocated--------*/
01409     ISAPNP_IO_RANGE(
01410             0x01,                   /* decodes 16-bit ISA addr */
01411             0x20,0x20,              /* min-max range I/O port */
01412             0x01,0x02),             /* align=1 length=2 */
01413     ISAPNP_IO_RANGE(
01414             0x01,                   /* decodes 16-bit ISA addr */
01415             0xA0,0xA0,              /* min-max range I/O port */
01416             0x01,0x02),             /* align=1 length=2 */
01417     ISAPNP_IRQ_SINGLE(
01418             2,                  /* IRQ 2 */
01419             0x09),                  /* HTE=1 LTL=1 */
01420     ISAPNP_END,
01421     /*----------possible--------*/
01422     ISAPNP_END,
01423     /*----------compatible--------*/
01424     ISAPNP_END
01425 };
01426 
01427 static const unsigned char ISAPNP_sysdev_Timer[] = {
01428     ISAPNP_SYSDEV_HEADER(
01429             ISAPNP_ID('P','N','P',0x0,0x1,0x0,0x0), /* PNP0100 Timer */
01430             ISAPNP_TYPE(0x08,0x02,0x01),        /* type: ISA timer */
01431             0x0001 | 0x0002),           /* can't disable, can't configure */
01432     /*----------allocated--------*/
01433     ISAPNP_IO_RANGE(
01434             0x01,                   /* decodes 16-bit ISA addr */
01435             0x40,0x40,              /* min-max range I/O port */
01436             0x04,0x04),             /* align=4 length=4 */
01437     ISAPNP_IRQ_SINGLE(
01438             0,                  /* IRQ 0 */
01439             0x09),                  /* HTE=1 LTL=1 */
01440     ISAPNP_END,
01441     /*----------possible--------*/
01442     ISAPNP_END,
01443     /*----------compatible--------*/
01444     ISAPNP_END
01445 };
01446 
01447 static const unsigned char ISAPNP_sysdev_RTC[] = {
01448     ISAPNP_SYSDEV_HEADER(
01449             ISAPNP_ID('P','N','P',0x0,0xB,0x0,0x0), /* PNP0B00 Real-time clock */
01450             ISAPNP_TYPE(0x08,0x03,0x01),        /* type: ISA real-time clock */
01451             0x0001 | 0x0002),           /* can't disable, can't configure */
01452     /*----------allocated--------*/
01453     ISAPNP_IO_RANGE(
01454             0x01,                   /* decodes 16-bit ISA addr */
01455             0x70,0x70,              /* min-max range I/O port */
01456             0x01,0x02),             /* align=1 length=2 */
01457     ISAPNP_IRQ_SINGLE(
01458             8,                  /* IRQ 8 */
01459             0x09),                  /* HTE=1 LTL=1 */
01460     ISAPNP_END,
01461     /*----------possible--------*/
01462     ISAPNP_END,
01463     /*----------compatible--------*/
01464     ISAPNP_END
01465 };
01466 
01467 static const unsigned char ISAPNP_sysdev_PC_Speaker[] = {
01468     ISAPNP_SYSDEV_HEADER(
01469             ISAPNP_ID('P','N','P',0x0,0x8,0x0,0x0), /* PNP0800 PC speaker */
01470             ISAPNP_TYPE(0x04,0x01,0x00),        /* type: PC speaker */
01471             0x0001 | 0x0002),           /* can't disable, can't configure */
01472     /*----------allocated--------*/
01473     ISAPNP_IO_RANGE(
01474             0x01,                   /* decodes 16-bit ISA addr */
01475             0x61,0x61,              /* min-max range I/O port */
01476             0x01,0x01),             /* align=1 length=1 */
01477     ISAPNP_END,
01478     /*----------possible--------*/
01479     ISAPNP_END,
01480     /*----------compatible--------*/
01481     ISAPNP_END
01482 };
01483 
01484 static const unsigned char ISAPNP_sysdev_Numeric_Coprocessor[] = {
01485     ISAPNP_SYSDEV_HEADER(
01486             ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x4), /* PNP0C04 Numeric Coprocessor */
01487             ISAPNP_TYPE(0x0B,0x80,0x00),        /* type: FPU */
01488             0x0001 | 0x0002),           /* can't disable, can't configure */
01489     /*----------allocated--------*/
01490     ISAPNP_IO_RANGE(
01491             0x01,                   /* decodes 16-bit ISA addr */
01492             0xF0,0xF0,              /* min-max range I/O port */
01493             0x10,0x10),             /* align=16 length=16 */
01494     ISAPNP_IRQ_SINGLE(
01495             13,                 /* IRQ 13 */
01496             0x09),                  /* HTE=1 LTL=1 */
01497     ISAPNP_END,
01498     /*----------possible--------*/
01499     ISAPNP_END,
01500     /*----------compatible--------*/
01501     ISAPNP_END
01502 };
01503 
01504 static const unsigned char ISAPNP_sysdev_System_Board[] = {
01505     ISAPNP_SYSDEV_HEADER(
01506             ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x1), /* PNP0C01 System board */
01507             ISAPNP_TYPE(0x08,0x80,0x00),        /* type: System peripheral, Other */
01508             0x0001 | 0x0002),           /* can't disable, can't configure */
01509     /*----------allocated--------*/
01510     ISAPNP_IO_RANGE(
01511             0x01,                   /* decodes 16-bit ISA addr */
01512             0x24,0x24,              /* min-max range I/O port */
01513             0x04,0x04),             /* align=4 length=4 */
01514     ISAPNP_END,
01515     /*----------possible--------*/
01516     ISAPNP_END,
01517     /*----------compatible--------*/
01518     ISAPNP_END
01519 };
01520 
01521 /* NTS: If some of my late 1990's laptops are any indication, this resource list can be used
01522  *      as a hint that the motherboard supports Intel EISA/PCI controller DMA registers that
01523  *      allow ISA DMA to extend to 32-bit addresses instead of being limited to 24-bit */
01524 static const unsigned char ISAPNP_sysdev_General_ISAPNP[] = {
01525     ISAPNP_SYSDEV_HEADER(
01526             ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x2), /* PNP0C02 General ID for reserving resources */
01527             ISAPNP_TYPE(0x08,0x80,0x00),        /* type: System peripheral, Other */
01528             0x0001 | 0x0002),           /* can't disable, can't configure */
01529     /*----------allocated--------*/
01530     ISAPNP_IO_RANGE(
01531             0x01,                   /* decodes 16-bit ISA addr */
01532             0x208,0x208,                /* min-max range I/O port */
01533             0x04,0x04),             /* align=4 length=4 */
01534     ISAPNP_END,
01535     /*----------possible--------*/
01536     ISAPNP_END,
01537     /*----------compatible--------*/
01538     ISAPNP_END
01539 };
01540 
01541 /* PnP system entry to tell Windows 95 the obvious: That there's an ISA bus present */
01542 /* NTS: Examination of some old laptops of mine shows that these devices do not list any resources,
01543  *      or at least, an old Toshiba of mine lists the PCI registers 0xCF8-0xCFF as motherboard resources
01544  *      and defines no resources for the PCI Bus PnP device. */
01545 static const unsigned char ISAPNP_sysdev_ISA_BUS[] = {
01546     ISAPNP_SYSDEV_HEADER(
01547             ISAPNP_ID('P','N','P',0x0,0xA,0x0,0x0), /* PNP0A00 ISA Bus */
01548             ISAPNP_TYPE(0x06,0x04,0x00),        /* type: System device, peripheral bus */
01549             0x0001 | 0x0002),           /* can't disable, can't configure */
01550     /*----------allocated--------*/
01551     ISAPNP_END,
01552     /*----------possible--------*/
01553     ISAPNP_END,
01554     /*----------compatible--------*/
01555     ISAPNP_END
01556 };
01557 
01558 /* PnP system entry to tell Windows 95 the obvious: That there's a PCI bus present */
01559 static const unsigned char ISAPNP_sysdev_PCI_BUS[] = {
01560     ISAPNP_SYSDEV_HEADER(
01561             ISAPNP_ID('P','N','P',0x0,0xA,0x0,0x3), /* PNP0A03 PCI Bus */
01562             ISAPNP_TYPE(0x06,0x04,0x00),        /* type: System device, peripheral bus */
01563             0x0001 | 0x0002),           /* can't disable, can't configure */
01564     /*----------allocated--------*/
01565     ISAPNP_END,
01566     /*----------possible--------*/
01567     ISAPNP_END,
01568     /*----------compatible--------*/
01569     ISAPNP_END
01570 };
01571 
01572 /* to help convince Windows 95 that the APM BIOS is present */
01573 static const unsigned char ISAPNP_sysdev_APM_BIOS[] = {
01574     ISAPNP_SYSDEV_HEADER(
01575             ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x5), /* PNP0C05 APM BIOS */
01576             ISAPNP_TYPE(0x08,0x80,0x00),        /* type: FIXME is this right?? I can't find any examples or documentation */
01577             0x0001 | 0x0002),           /* can't disable, can't configure */
01578     /*----------allocated--------*/
01579     ISAPNP_END,
01580     /*----------possible--------*/
01581     ISAPNP_END,
01582     /*----------compatible--------*/
01583     ISAPNP_END
01584 };
01585 
01586 bool ISAPNP_RegisterSysDev(const unsigned char *raw,Bitu len,bool already) {
01587     if (ISAPNP_SysDevNodeCount >= MAX_ISA_PNP_SYSDEVNODES)
01588         return false;
01589 
01590     ISAPNP_SysDevNodes[ISAPNP_SysDevNodeCount] = new ISAPNP_SysDevNode(raw,len,already);
01591     if (ISAPNP_SysDevNodes[ISAPNP_SysDevNodeCount] == NULL)
01592         return false;
01593     
01594     ISAPNP_SysDevNodeCount++;
01595     if (ISAPNP_SysDevNodeLargest < (len+3))
01596         ISAPNP_SysDevNodeLargest = len+3;
01597 
01598     return true;
01599 }
01600 
01601 /* ISA PnP function calls have their parameters stored on the stack "C" __cdecl style. Parameters
01602  * are either int, long, or FAR pointers. Like __cdecl an assembly language implementation pushes
01603  * the function arguments on the stack BACKWARDS */
01604 static Bitu ISAPNP_Handler(bool protmode /* called from protected mode interface == true */) {
01605     Bitu arg;
01606     Bitu func,BiosSelector;
01607 
01608     /* I like how the ISA PnP spec says that the 16-bit entry points (real and protected) are given 16-bit data segments
01609      * which implies that all segments involved might as well be 16-bit.
01610      *
01611      * Right?
01612      *
01613      * Well, guess what Windows 95 gives us when calling this entry point:
01614      *
01615      *     Segment SS = DS = 0x30  base=0 limit=0xFFFFFFFF
01616      *       SS:SP = 0x30:0xC138BADF or something like that from within BIOS.VXD
01617      *
01618      * Yeah... for a 16-bit code segment call. Right. Typical Microsoft. >:(
01619      *
01620      * This might also explain why my early experiments with Bochs always had the perpetual
01621      * APM BIOS that never worked but was always detected.
01622      *
01623      * ------------------------------------------------------------------------
01624      * Windows 95 OSR2:
01625      *
01626      * Windows 95 OSR2 however uses a 16-bit stack (where the stack segment is based somewhere
01627      * around 0xC1xxxxxx), all we have to do to correctly access it is work through the page tables.
01628      * This is within spec, but now Microsoft sends us a data segment that is based at virtual address
01629      * 0xC2xxxxxx, which is why I had to disable the "verify selector" routine */
01630     arg = SegPhys(ss) + (reg_esp&cpu.stack.mask) + (2*2); /* entry point (real and protected) is 16-bit, expected to RETF (skip CS:IP) */
01631 
01632     if (protmode != ISAPNP_CPU_ProtMode()) {
01633         //LOG_MSG("ISA PnP %s entry point called from %s. On real BIOSes this would CRASH\n",protmode ? "Protected mode" : "Real mode",
01634         //  ISAPNP_CPU_ProtMode() ? "Protected mode" : "Real mode");
01635         reg_ax = 0x84;/* BAD_PARAMETER */
01636         return 0;
01637     }
01638 
01639     func = mem_readw(arg);
01640 //  LOG_MSG("PnP prot=%u DS=%04x (base=0x%08lx) SS:ESP=%04x:%04x (base=0x%08lx phys=0x%08lx) function=0x%04x\n",
01641 //      (unsigned int)protmode,(unsigned int)SegValue(ds),(unsigned long)SegPhys(ds),
01642 //      (unsigned int)SegValue(ss),(unsigned int)reg_esp,(unsigned long)SegPhys(ss),
01643 //      (unsigned long)arg,(unsigned int)func);
01644 
01645     /* every function takes the form
01646      *
01647      * int __cdecl FAR (*entrypoint)(int Function...);
01648      *
01649      * so the first argument on the stack is an int that we read to determine what the caller is asking
01650      *
01651      * Dont forget in the real-mode world:
01652      *    sizeof(int) == 16 bits
01653      *    sizeof(long) == 32 bits
01654      */    
01655     switch (func) {
01656         case 0: {       /* Get Number of System Nodes */
01657             /* int __cdecl FAR (*entrypoint)(int Function,unsigned char FAR *NumNodes,unsigned int FAR *NodeSize,unsigned int BiosSelector);
01658              *                               ^ +0         ^ +2                        ^ +6                       ^ +10                       = 12 */
01659             Bitu NumNodes_ptr = mem_readd(arg+2);
01660             Bitu NodeSize_ptr = mem_readd(arg+6);
01661             BiosSelector = mem_readw(arg+10);
01662 
01663             if (!ISAPNP_Verify_BiosSelector(BiosSelector))
01664                 goto badBiosSelector;
01665 
01666             if (NumNodes_ptr != 0) mem_writeb(ISAPNP_xlate_address(NumNodes_ptr),ISAPNP_SysDevNodeCount);
01667             if (NodeSize_ptr != 0) mem_writew(ISAPNP_xlate_address(NodeSize_ptr),ISAPNP_SysDevNodeLargest);
01668 
01669             reg_ax = 0x00;/* SUCCESS */
01670         } break;
01671         case 1: {       /* Get System Device Node */
01672             /* int __cdecl FAR (*entrypoint)(int Function,unsigned char FAR *Node,struct DEV_NODE FAR *devNodeBuffer,unsigned int Control,unsigned int BiosSelector);
01673              *                               ^ +0         ^ +2                    ^ +6                               ^ +10                ^ +12                       = 14 */
01674             Bitu Node_ptr = mem_readd(arg+2);
01675             Bitu devNodeBuffer_ptr = mem_readd(arg+6);
01676             Bitu Control = mem_readw(arg+10);
01677             BiosSelector = mem_readw(arg+12);
01678             unsigned char Node;
01679             Bitu i=0;
01680 
01681             if (!ISAPNP_Verify_BiosSelector(BiosSelector))
01682                 goto badBiosSelector;
01683 
01684             /* control bits 0-1 must be '01' or '10' but not '00' or '11' */
01685             if (Control == 0 || (Control&3) == 3) {
01686                 LOG_MSG("ISAPNP Get System Device Node: Invalid Control value 0x%04x\n",(int)Control);
01687                 reg_ax = 0x84;/* BAD_PARAMETER */
01688                 break;
01689             }
01690 
01691             devNodeBuffer_ptr = ISAPNP_xlate_address(devNodeBuffer_ptr);
01692             Node_ptr = ISAPNP_xlate_address(Node_ptr);
01693             Node = mem_readb(Node_ptr);
01694             if (Node >= ISAPNP_SysDevNodeCount) {
01695                 LOG_MSG("ISAPNP Get System Device Node: Invalid Node 0x%02x (max 0x%04x)\n",(int)Node,(int)ISAPNP_SysDevNodeCount);
01696                 reg_ax = 0x84;/* BAD_PARAMETER */
01697                 break;
01698             }
01699 
01700             ISAPNP_SysDevNode *nd = ISAPNP_SysDevNodes[Node];
01701 
01702             mem_writew(devNodeBuffer_ptr+0,(Bit16u)(nd->raw_len+3)); /* Length */
01703             mem_writeb(devNodeBuffer_ptr+2,Node); /* on most PnP BIOS implementations I've seen "handle" is set to the same value as Node */
01704             for (i=0;i < (Bitu)nd->raw_len;i++)
01705                 mem_writeb(devNodeBuffer_ptr+i+3,nd->raw[i]);
01706 
01707 //          LOG_MSG("ISAPNP OS asked for Node 0x%02x\n",Node);
01708 
01709             if (++Node >= ISAPNP_SysDevNodeCount) Node = 0xFF; /* no more nodes */
01710             mem_writeb(Node_ptr,Node);
01711 
01712             reg_ax = 0x00;/* SUCCESS */
01713         } break;
01714         case 4: {       /* Send Message */
01715             /* int __cdecl FAR (*entrypoint)(int Function,unsigned int Message,unsigned int BiosSelector);
01716              *                               ^ +0         ^ +2                 ^ +4                        = 6 */
01717             Bitu Message = mem_readw(arg+2);
01718             BiosSelector = mem_readw(arg+4);
01719 
01720             if (!ISAPNP_Verify_BiosSelector(BiosSelector))
01721                 goto badBiosSelector;
01722 
01723             switch (Message) {
01724                 case 0x41:  /* POWER_OFF */
01725                     LOG_MSG("Plug & Play OS requested power off.\n");
01726                     throw 1;    /* NTS: Based on the Reboot handler code, causes DOSBox to cleanly shutdown and exit */
01727                     reg_ax = 0;
01728                     break;
01729                 case 0x42:  /* PNP_OS_ACTIVE */
01730                     LOG_MSG("Plug & Play OS reports itself active\n");
01731                     reg_ax = 0;
01732                     break;
01733                 case 0x43:  /* PNP_OS_INACTIVE */
01734                     LOG_MSG("Plug & Play OS reports itself inactive\n");
01735                     reg_ax = 0;
01736                     break;
01737                 default:
01738                     LOG_MSG("Unknown ISA PnP message 0x%04x\n",(int)Message);
01739                     reg_ax = 0x82;/* FUNCTION_NOT_SUPPORTED */
01740                     break;
01741             }
01742         } break;
01743         case 0x40: {        /* Get PnP ISA configuration */
01744             /* int __cdecl FAR (*entrypoint)(int Function,unsigned char far *struct,unsigned int BiosSelector);
01745              *                               ^ +0         ^ +2                      ^ +6                        = 8 */
01746             Bitu struct_ptr = mem_readd(arg+2);
01747             BiosSelector = mem_readw(arg+6);
01748 
01749             if (!ISAPNP_Verify_BiosSelector(BiosSelector))
01750                 goto badBiosSelector;
01751 
01752             /* struct isapnp_pnp_isa_cfg {
01753                  uint8_t    revision;
01754                  uint8_t    total_csn;
01755                  uint16_t   isa_pnp_port;
01756                  uint16_t   reserved;
01757              }; */
01758 
01759             if (struct_ptr != 0) {
01760                 Bitu ph = ISAPNP_xlate_address(struct_ptr);
01761                 mem_writeb(ph+0,0x01);      /* ->revision = 0x01 */
01762                 mem_writeb(ph+1,ISA_PNP_devnext); /* ->total_csn */
01763                 mem_writew(ph+2,ISA_PNP_WPORT_BIOS);    /* ->isa_pnp_port */
01764                 mem_writew(ph+4,0);     /* ->reserved */
01765             }
01766 
01767             reg_ax = 0x00;/* SUCCESS */
01768         } break;
01769         default:
01770             //LOG_MSG("Unsupported ISA PnP function 0x%04x\n",func);
01771             reg_ax = 0x82;/* FUNCTION_NOT_SUPPORTED */
01772             break;
01773     };
01774 
01775     return 0;
01776 badBiosSelector:
01777     /* return an error. remind the user (possible developer) how lucky he is, a real
01778      * BIOS implementation would CRASH when misused like this */
01779     LOG_MSG("ISA PnP function 0x%04x called with incorrect BiosSelector parameter 0x%04x\n",(int)func,(int)BiosSelector);
01780     LOG_MSG(" > STACK %04X %04X %04X %04X %04X %04X %04X %04X\n",
01781         mem_readw(arg),     mem_readw(arg+2),   mem_readw(arg+4),   mem_readw(arg+6),
01782         mem_readw(arg+8),   mem_readw(arg+10),  mem_readw(arg+12),  mem_readw(arg+14));
01783 
01784     reg_ax = 0x84;/* BAD_PARAMETER */
01785     return 0;
01786 }
01787 
01788 static Bitu ISAPNP_Handler_PM(void) {
01789     return ISAPNP_Handler(true);
01790 }
01791 
01792 static Bitu ISAPNP_Handler_RM(void) {
01793     return ISAPNP_Handler(false);
01794 }
01795 
01796 static Bitu INT70_Handler(void) {
01797     /* Acknowledge irq with cmos */
01798     IO_Write(0x70,0xc);
01799     IO_Read(0x71);
01800     if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) {
01801         Bit32u count=mem_readd(BIOS_WAIT_FLAG_COUNT);
01802         if (count>997) {
01803             mem_writed(BIOS_WAIT_FLAG_COUNT,count-997);
01804         } else {
01805             mem_writed(BIOS_WAIT_FLAG_COUNT,0);
01806             PhysPt where=Real2Phys(mem_readd(BIOS_WAIT_FLAG_POINTER));
01807             mem_writeb(where,mem_readb(where)|0x80);
01808             mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0);
01809             mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP));
01810             IO_Write(0x70,0xb);
01811             IO_Write(0x71,IO_Read(0x71)&~0x40);
01812         }
01813     } 
01814     /* Signal EOI to both pics */
01815     IO_Write(0xa0,0x20);
01816     IO_Write(0x20,0x20);
01817     return 0;
01818 }
01819 
01820 CALLBACK_HandlerObject* tandy_DAC_callback[2];
01821 static struct {
01822     Bit16u port;
01823     Bit8u irq;
01824     Bit8u dma;
01825 } tandy_sb;
01826 static struct {
01827     Bit16u port;
01828     Bit8u irq;
01829     Bit8u dma;
01830 } tandy_dac;
01831 
01832 static bool Tandy_InitializeSB() {
01833     /* see if soundblaster module available and at what port/IRQ/DMA */
01834     Bitu sbport, sbirq, sbdma;
01835     if (SB_Get_Address(sbport, sbirq, sbdma)) {
01836         tandy_sb.port=(Bit16u)(sbport&0xffff);
01837         tandy_sb.irq =(Bit8u)(sbirq&0xff);
01838         tandy_sb.dma =(Bit8u)(sbdma&0xff);
01839         return true;
01840     } else {
01841         /* no soundblaster accessible, disable Tandy DAC */
01842         tandy_sb.port=0;
01843         return false;
01844     }
01845 }
01846 
01847 static bool Tandy_InitializeTS() {
01848     /* see if Tandy DAC module available and at what port/IRQ/DMA */
01849     Bitu tsport, tsirq, tsdma;
01850     if (TS_Get_Address(tsport, tsirq, tsdma)) {
01851         tandy_dac.port=(Bit16u)(tsport&0xffff);
01852         tandy_dac.irq =(Bit8u)(tsirq&0xff);
01853         tandy_dac.dma =(Bit8u)(tsdma&0xff);
01854         return true;
01855     } else {
01856         /* no Tandy DAC accessible */
01857         tandy_dac.port=0;
01858         return false;
01859     }
01860 }
01861 
01862 /* check if Tandy DAC is still playing */
01863 static bool Tandy_TransferInProgress(void) {
01864     if (real_readw(0x40,0xd0)) return true;         /* not yet done */
01865     if (real_readb(0x40,0xd4)==0xff) return false;  /* still in init-state */
01866 
01867     Bit8u tandy_dma = 1;
01868     if (tandy_sb.port) tandy_dma = tandy_sb.dma;
01869     else if (tandy_dac.port) tandy_dma = tandy_dac.dma;
01870 
01871     IO_Write(0x0c,0x00);
01872     Bit16u datalen=(Bit8u)(IO_ReadB(tandy_dma*2u+1u)&0xffu);
01873     datalen|=(IO_ReadB(tandy_dma*2u+1u)<<8u);
01874     if (datalen==0xffff) return false;  /* no DMA transfer */
01875     else if ((datalen<0x10) && (real_readb(0x40,0xd4)==0x0f) && (real_readw(0x40,0xd2)==0x1c)) {
01876         /* stop already requested */
01877         return false;
01878     }
01879     return true;
01880 }
01881 
01882 static void Tandy_SetupTransfer(PhysPt bufpt,bool isplayback) {
01883     Bitu length=real_readw(0x40,0xd0);
01884     if (length==0) return;  /* nothing to do... */
01885 
01886     if ((tandy_sb.port==0) && (tandy_dac.port==0)) return;
01887 
01888     Bit8u tandy_irq = 7;
01889     if (tandy_sb.port) tandy_irq = tandy_sb.irq;
01890     else if (tandy_dac.port) tandy_irq = tandy_dac.irq;
01891     Bit8u tandy_irq_vector = tandy_irq;
01892     if (tandy_irq_vector<8) tandy_irq_vector += 8;
01893     else tandy_irq_vector += (0x70-8);
01894 
01895     /* revector IRQ-handler if necessary */
01896     RealPt current_irq=RealGetVec(tandy_irq_vector);
01897     if (current_irq!=tandy_DAC_callback[0]->Get_RealPointer()) {
01898         real_writed(0x40,0xd6,current_irq);
01899         RealSetVec(tandy_irq_vector,tandy_DAC_callback[0]->Get_RealPointer());
01900     }
01901 
01902     Bit8u tandy_dma = 1;
01903     if (tandy_sb.port) tandy_dma = tandy_sb.dma;
01904     else if (tandy_dac.port) tandy_dma = tandy_dac.dma;
01905 
01906     if (tandy_sb.port) {
01907         IO_Write(tandy_sb.port+0xcu,0xd0);               /* stop DMA transfer */
01908         IO_Write(0x21,IO_Read(0x21)&(~(1u<<tandy_irq))); /* unmask IRQ */
01909         IO_Write(tandy_sb.port+0xcu,0xd1);               /* turn speaker on */
01910     } else {
01911         IO_Write(tandy_dac.port,IO_Read(tandy_dac.port)&0x60);  /* disable DAC */
01912         IO_Write(0x21,IO_Read(0x21)&(~(1u<<tandy_irq)));         /* unmask IRQ */
01913     }
01914 
01915     IO_Write(0x0a,0x04|tandy_dma);  /* mask DMA channel */
01916     IO_Write(0x0c,0x00);            /* clear DMA flipflop */
01917     if (isplayback) IO_Write(0x0b,0x48|tandy_dma);
01918     else IO_Write(0x0b,0x44|tandy_dma);
01919     /* set physical address of buffer */
01920     Bit8u bufpage=(Bit8u)((bufpt>>16u)&0xff);
01921     IO_Write(tandy_dma*2u,(Bit8u)(bufpt&0xff));
01922     IO_Write(tandy_dma*2u,(Bit8u)((bufpt>>8u)&0xff));
01923     switch (tandy_dma) {
01924         case 0: IO_Write(0x87,bufpage); break;
01925         case 1: IO_Write(0x83,bufpage); break;
01926         case 2: IO_Write(0x81,bufpage); break;
01927         case 3: IO_Write(0x82,bufpage); break;
01928     }
01929     real_writeb(0x40,0xd4,bufpage);
01930 
01931     /* calculate transfer size (respects segment boundaries) */
01932     Bit32u tlength=length;
01933     if (tlength+(bufpt&0xffff)>0x10000) tlength=0x10000-(bufpt&0xffff);
01934     real_writew(0x40,0xd0,(Bit16u)(length-tlength));    /* remaining buffer length */
01935     tlength--;
01936 
01937     /* set transfer size */
01938     IO_Write(tandy_dma*2u+1u,(Bit8u)(tlength&0xffu));
01939     IO_Write(tandy_dma*2u+1u,(Bit8u)((tlength>>8u)&0xffu));
01940 
01941     Bit16u delay=(Bit16u)(real_readw(0x40,0xd2)&0xfff);
01942     Bit8u amplitude=(Bit8u)(((unsigned int)real_readw(0x40,0xd2)>>13u)&0x7u);
01943     if (tandy_sb.port) {
01944         IO_Write(0x0a,tandy_dma);   /* enable DMA channel */
01945         /* set frequency */
01946         IO_Write(tandy_sb.port+0xcu,0x40);
01947         IO_Write(tandy_sb.port+0xcu,256u - delay*100u/358u);
01948         /* set playback type to 8bit */
01949         if (isplayback) IO_Write(tandy_sb.port+0xcu,0x14u);
01950         else IO_Write(tandy_sb.port+0xcu,0x24u);
01951         /* set transfer size */
01952         IO_Write(tandy_sb.port+0xcu,(Bit8u)(tlength&0xffu));
01953         IO_Write(tandy_sb.port+0xcu,(Bit8u)((tlength>>8)&0xffu));
01954     } else {
01955         if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x03u);
01956         else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x02u);
01957         IO_Write(tandy_dac.port+2u,(Bit8u)(delay&0xffu));
01958         IO_Write(tandy_dac.port+3u,(Bit8u)((((unsigned int)delay>>8u)&0xfu) | ((unsigned int)amplitude<<5u)));
01959         if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x1fu);
01960         else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x1e);
01961         IO_Write(0x0a,tandy_dma);   /* enable DMA channel */
01962     }
01963 
01964     if (!isplayback) {
01965         /* mark transfer as recording operation */
01966         real_writew(0x40,0xd2,(Bit16u)(delay|0x1000));
01967     }
01968 }
01969 
01970 static Bitu IRQ_TandyDAC(void) {
01971     if (tandy_dac.port) {
01972         IO_Read(tandy_dac.port);
01973     }
01974     if (real_readw(0x40,0xd0)) {    /* play/record next buffer */
01975         /* acknowledge IRQ */
01976         IO_Write(0x20,0x20);
01977         if (tandy_sb.port) {
01978             IO_Read(tandy_sb.port+0xeu);
01979         }
01980 
01981         /* buffer starts at the next page */
01982         Bit8u npage=real_readb(0x40,0xd4)+1u;
01983         real_writeb(0x40,0xd4,npage);
01984 
01985         Bitu rb=real_readb(0x40,0xd3);
01986         if (rb&0x10) {
01987             /* start recording */
01988             real_writeb(0x40,0xd3,rb&0xefu);
01989             Tandy_SetupTransfer((unsigned int)npage<<16u,false);
01990         } else {
01991             /* start playback */
01992             Tandy_SetupTransfer((unsigned int)npage<<16u,true);
01993         }
01994     } else {    /* playing/recording is finished */
01995         Bit8u tandy_irq = 7u;
01996         if (tandy_sb.port) tandy_irq = tandy_sb.irq;
01997         else if (tandy_dac.port) tandy_irq = tandy_dac.irq;
01998         Bit8u tandy_irq_vector = tandy_irq;
01999         if (tandy_irq_vector<8u) tandy_irq_vector += 8u;
02000         else tandy_irq_vector += (0x70u-8u);
02001 
02002         RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6));
02003 
02004         /* turn off speaker and acknowledge soundblaster IRQ */
02005         if (tandy_sb.port) {
02006             IO_Write(tandy_sb.port+0xcu,0xd3u);
02007             IO_Read(tandy_sb.port+0xeu);
02008         }
02009 
02010         /* issue BIOS tandy sound device busy callout */
02011         SegSet16(cs, RealSeg(tandy_DAC_callback[1]->Get_RealPointer()));
02012         reg_ip = RealOff(tandy_DAC_callback[1]->Get_RealPointer());
02013     }
02014     return CBRET_NONE;
02015 }
02016 
02017 static void TandyDAC_Handler(Bit8u tfunction) {
02018     if ((!tandy_sb.port) && (!tandy_dac.port)) return;
02019     switch (tfunction) {
02020     case 0x81:  /* Tandy sound system check */
02021         if (tandy_dac.port) {
02022             reg_ax=tandy_dac.port;
02023         } else {
02024             reg_ax=0xc4;
02025         }
02026         CALLBACK_SCF(Tandy_TransferInProgress());
02027         break;
02028     case 0x82:  /* Tandy sound system start recording */
02029     case 0x83:  /* Tandy sound system start playback */
02030         if (Tandy_TransferInProgress()) {
02031             /* cannot play yet as the last transfer isn't finished yet */
02032             reg_ah=0x00;
02033             CALLBACK_SCF(true);
02034             break;
02035         }
02036         /* store buffer length */
02037         real_writew(0x40,0xd0,reg_cx);
02038         /* store delay and volume */
02039         real_writew(0x40,0xd2,(reg_dx&0xfff)|((reg_al&7)<<13));
02040         Tandy_SetupTransfer(PhysMake(SegValue(es),reg_bx),reg_ah==0x83);
02041         reg_ah=0x00;
02042         CALLBACK_SCF(false);
02043         break;
02044     case 0x84:  /* Tandy sound system stop playing */
02045         reg_ah=0x00;
02046 
02047         /* setup for a small buffer with silence */
02048         real_writew(0x40,0xd0,0x0a);
02049         real_writew(0x40,0xd2,0x1c);
02050         Tandy_SetupTransfer(PhysMake(0xf000,0xa084),true);
02051         CALLBACK_SCF(false);
02052         break;
02053     case 0x85:  /* Tandy sound system reset */
02054         if (tandy_dac.port) {
02055             IO_Write(tandy_dac.port,(Bit8u)(IO_Read(tandy_dac.port)&0xe0));
02056         }
02057         reg_ah=0x00;
02058         CALLBACK_SCF(false);
02059         break;
02060     }
02061 }
02062 
02063 extern bool date_host_forced;
02064 static Bit8u ReadCmosByte (Bitu index) {
02065     IO_Write(0x70, index);
02066     return IO_Read(0x71);
02067 }
02068 
02069 static void WriteCmosByte (Bitu index, Bitu val) {
02070     IO_Write(0x70, index);
02071     IO_Write(0x71, val);
02072 }
02073 
02074 static bool RtcUpdateDone () {
02075     while ((ReadCmosByte(0x0a) & 0x80) != 0) CALLBACK_Idle();
02076     return true;            // cannot fail in DOSbox
02077 }
02078 
02079 static void InitRtc () {
02080     WriteCmosByte(0x0a, 0x26);      // default value (32768Hz, 1024Hz)
02081 
02082     // leave bits 6 (pirq), 5 (airq), 0 (dst) untouched
02083     // reset bits 7 (freeze), 4 (uirq), 3 (sqw), 2 (bcd)
02084     // set bit 1 (24h)
02085     WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x61u) | 0x02u);
02086 
02087     ReadCmosByte(0x0c);             // clear any bits set
02088 }
02089 
02090 static Bitu INT1A_Handler(void) {
02091     CALLBACK_SIF(true);
02092     switch (reg_ah) {
02093     case 0x00:  /* Get System time */
02094         {
02095             Bit32u ticks=mem_readd(BIOS_TIMER);
02096             reg_al=mem_readb(BIOS_24_HOURS_FLAG);
02097             mem_writeb(BIOS_24_HOURS_FLAG,0); // reset the "flag"
02098             reg_cx=(Bit16u)(ticks >> 16u);
02099             reg_dx=(Bit16u)(ticks & 0xffff);
02100             break;
02101         }
02102     case 0x01:  /* Set System time */
02103         mem_writed(BIOS_TIMER,((unsigned int)reg_cx<<16u)|reg_dx);
02104         break;
02105     case 0x02:  /* GET REAL-TIME CLOCK TIME (AT,XT286,PS) */
02106         if(date_host_forced) {
02107             InitRtc();                          // make sure BCD and no am/pm
02108             if (RtcUpdateDone()) {              // make sure it's safe to read
02109                 reg_ch = ReadCmosByte(0x04);    // hours
02110                 reg_cl = ReadCmosByte(0x02);    // minutes
02111                 reg_dh = ReadCmosByte(0x00);    // seconds
02112                 reg_dl = ReadCmosByte(0x0b) & 0x01; // daylight saving time
02113             }
02114             CALLBACK_SCF(false);
02115             break;
02116         }
02117         IO_Write(0x70,0x04);        //Hours
02118         reg_ch=IO_Read(0x71);
02119         IO_Write(0x70,0x02);        //Minutes
02120         reg_cl=IO_Read(0x71);
02121         IO_Write(0x70,0x00);        //Seconds
02122         reg_dh=IO_Read(0x71);
02123         reg_dl=0;           //Daylight saving disabled
02124         CALLBACK_SCF(false);
02125         break;
02126     case 0x03:  // set RTC time
02127         if(date_host_forced) {
02128             InitRtc();                          // make sure BCD and no am/pm
02129             WriteCmosByte(0x0b, ReadCmosByte(0x0b) | 0x80u);     // prohibit updates
02130             WriteCmosByte(0x04, reg_ch);        // hours
02131             WriteCmosByte(0x02, reg_cl);        // minutes
02132             WriteCmosByte(0x00, reg_dh);        // seconds
02133             WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x7eu) | (reg_dh & 0x01u)); // dst + implicitly allow updates
02134         }
02135         break;
02136     case 0x04:  /* GET REAL-TIME ClOCK DATE  (AT,XT286,PS) */
02137         if(date_host_forced) {
02138             InitRtc();                          // make sure BCD and no am/pm
02139             if (RtcUpdateDone()) {              // make sure it's safe to read
02140                 reg_ch = ReadCmosByte(0x32);    // century
02141                 reg_cl = ReadCmosByte(0x09);    // year
02142                 reg_dh = ReadCmosByte(0x08);    // month
02143                 reg_dl = ReadCmosByte(0x07);    // day
02144             }
02145             CALLBACK_SCF(false);
02146             break;
02147         }
02148         IO_Write(0x70,0x32);        //Centuries
02149         reg_ch=IO_Read(0x71);
02150         IO_Write(0x70,0x09);        //Years
02151         reg_cl=IO_Read(0x71);
02152         IO_Write(0x70,0x08);        //Months
02153         reg_dh=IO_Read(0x71);
02154         IO_Write(0x70,0x07);        //Days
02155         reg_dl=IO_Read(0x71);
02156         CALLBACK_SCF(false);
02157         break;
02158     case 0x05:  // set RTC date
02159         if(date_host_forced) {
02160             InitRtc();                          // make sure BCD and no am/pm
02161             WriteCmosByte(0x0b, ReadCmosByte(0x0b) | 0x80);     // prohibit updates
02162             WriteCmosByte(0x32, reg_ch);    // century
02163             WriteCmosByte(0x09, reg_cl);    // year
02164             WriteCmosByte(0x08, reg_dh);    // month
02165             WriteCmosByte(0x07, reg_dl);    // day
02166             WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x7f));   // allow updates
02167         }
02168         break;
02169     case 0x80:  /* Pcjr Setup Sound Multiplexer */
02170         LOG(LOG_BIOS,LOG_ERROR)("INT1A:80:Setup tandy sound multiplexer to %d",reg_al);
02171         break;
02172     case 0x81:  /* Tandy sound system check */
02173     case 0x82:  /* Tandy sound system start recording */
02174     case 0x83:  /* Tandy sound system start playback */
02175     case 0x84:  /* Tandy sound system stop playing */
02176     case 0x85:  /* Tandy sound system reset */
02177         TandyDAC_Handler(reg_ah);
02178         break;
02179     case 0xb1:      /* PCI Bios Calls */
02180         if (pcibus_enable) {
02181             LOG(LOG_BIOS,LOG_WARN)("INT1A:PCI bios call %2X",reg_al);
02182             switch (reg_al) {
02183                 case 0x01:  // installation check
02184                     if (PCI_IsInitialized()) {
02185                         reg_ah=0x00;
02186                         reg_al=0x01;    // cfg space mechanism 1 supported
02187                         reg_bx=0x0210;  // ver 2.10
02188                         reg_cx=0x0000;  // only one PCI bus
02189                         reg_edx=0x20494350;
02190                         reg_edi=PCI_GetPModeInterface();
02191                         CALLBACK_SCF(false);
02192                     } else {
02193                         CALLBACK_SCF(true);
02194                     }
02195                     break;
02196                 case 0x02: {    // find device
02197                     Bitu devnr=0u;
02198                     Bitu count=0x100u;
02199                     Bit32u devicetag=((unsigned int)reg_cx<<16u)|reg_dx;
02200                     Bits found=-1;
02201                     for (Bitu i=0; i<=count; i++) {
02202                         IO_WriteD(0xcf8,0x80000000u|(i<<8u)); // query unique device/subdevice entries
02203                         if (IO_ReadD(0xcfc)==devicetag) {
02204                             if (devnr==reg_si) {
02205                                 found=(Bits)i;
02206                                 break;
02207                             } else {
02208                                 // device found, but not the SIth device
02209                                 devnr++;
02210                             }
02211                         }
02212                     }
02213                     if (found>=0) {
02214                         reg_ah=0x00;
02215                         reg_bh=0x00;    // bus 0
02216                         reg_bl=(Bit8u)(found&0xff);
02217                         CALLBACK_SCF(false);
02218                     } else {
02219                         reg_ah=0x86;    // device not found
02220                         CALLBACK_SCF(true);
02221                     }
02222                     }
02223                     break;
02224                 case 0x03: {    // find device by class code
02225                     Bitu devnr=0;
02226                     Bitu count=0x100u;
02227                     Bit32u classtag=reg_ecx&0xffffffu;
02228                     Bits found=-1;
02229                     for (Bitu i=0; i<=count; i++) {
02230                         IO_WriteD(0xcf8,0x80000000u|(i<<8u)); // query unique device/subdevice entries
02231                         if (IO_ReadD(0xcfc)!=0xffffffffu) {
02232                             IO_WriteD(0xcf8,0x80000000u|(i<<8u)|0x08u);
02233                             if ((IO_ReadD(0xcfc)>>8u)==classtag) {
02234                                 if (devnr==reg_si) {
02235                                     found=(Bits)i;
02236                                     break;
02237                                 } else {
02238                                     // device found, but not the SIth device
02239                                     devnr++;
02240                                 }
02241                             }
02242                         }
02243                     }
02244                     if (found>=0) {
02245                         reg_ah=0x00;
02246                         reg_bh=0x00;    // bus 0
02247                         reg_bl=(Bit8u)(found&0xffu);
02248                         CALLBACK_SCF(false);
02249                     } else {
02250                         reg_ah=0x86;    // device not found
02251                         CALLBACK_SCF(true);
02252                     }
02253                     }
02254                     break;
02255                 case 0x08:  // read configuration byte
02256                     IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu));
02257                     reg_cl=IO_ReadB(0xcfc+(reg_di&3u));
02258                     CALLBACK_SCF(false);
02259                     reg_ah=0x00;
02260                     break;
02261                 case 0x09:  // read configuration word
02262                     IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu));
02263                     reg_cx=IO_ReadW(0xcfc+(reg_di&2u));
02264                     CALLBACK_SCF(false);
02265                     reg_ah=0x00;
02266                     break;
02267                 case 0x0a:  // read configuration dword
02268                     IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu));
02269                     reg_ecx=IO_ReadD(0xcfc+(reg_di&3u));
02270                     CALLBACK_SCF(false);
02271                     reg_ah=0x00;
02272                     break;
02273                 case 0x0b:  // write configuration byte
02274                     IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu));
02275                     IO_WriteB(0xcfc+(reg_di&3u),reg_cl);
02276                     CALLBACK_SCF(false);
02277                     reg_ah=0x00;
02278                     break;
02279                 case 0x0c:  // write configuration word
02280                     IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu));
02281                     IO_WriteW(0xcfc+(reg_di&2u),reg_cx);
02282                     CALLBACK_SCF(false);
02283                     reg_ah=0x00;
02284                     break;
02285                 case 0x0d:  // write configuration dword
02286                     IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu));
02287                     IO_WriteD(0xcfc+((unsigned int)reg_di&3u),reg_ecx);
02288                     CALLBACK_SCF(false);
02289                     reg_ah=0x00;
02290                     break;
02291                 default:
02292                     LOG(LOG_BIOS,LOG_ERROR)("INT1A:PCI BIOS: unknown function %x (%x %x %x)",
02293                         reg_ax,reg_bx,reg_cx,reg_dx);
02294                     CALLBACK_SCF(true);
02295                     break;
02296             }
02297         }
02298         else {
02299             CALLBACK_SCF(true);
02300         }
02301         break;
02302     default:
02303         LOG(LOG_BIOS,LOG_ERROR)("INT1A:Undefined call %2X",reg_ah);
02304     }
02305     return CBRET_NONE;
02306 }   
02307 
02308 bool INT16_get_key(Bit16u &code);
02309 bool INT16_peek_key(Bit16u &code);
02310 
02311 extern uint8_t                     GDC_display_plane;
02312 extern uint8_t                     GDC_display_plane_pending;
02313 
02314 unsigned char prev_pc98_mode42 = 0;
02315 
02316 bool pc98_function_row = false;
02317 
02318 const char *pc98_func_key[10] = {
02319     "  C1  ",
02320     "  CU  ",
02321     "  CA  ",
02322     "  S1  ",
02323     "  SU  ",
02324 
02325     " VOID ",
02326     " NWL  ",
02327     " INS  ",
02328     " REP  ",
02329     "  ^Z  "
02330 };
02331 
02332 // shortcuts offered by SHIFT F1-F10. You can bring this onscreen using CTRL+F7. This row shows '*' in col 2.
02333 // [0] is onscreen display, [1] is what is entered to STDIN.
02334 const char *pc98_shcut_key[10][2] = {
02335     {"dir a:",   "dir a:\x0D"},
02336     {"dir b:",   "dir b:\x0D"},
02337     {"copy  ",   "copy "},
02338     {"del   ",   "del "},
02339     {"ren   ",   "ren "},
02340 
02341     {"chkdsk",   "chkdsk a:\x0D"},
02342     {"chkdsk",   "chkdsk b:\x0D"},
02343     {"type  ",   "type "},
02344     {"date\x0D ","date\x0D"},           // display includes CR
02345     {"time\x0D ","time\x0D"}
02346 };
02347 
02348 #include "int10.h"
02349 
02350 void update_pc98_function_row(bool enable) {
02351     pc98_function_row = enable;
02352 
02353     real_writeb(0x60,0x112,25 - 1 - (pc98_function_row ? 1 : 0));
02354 
02355     unsigned char c = real_readb(0x60,0x11C);
02356     unsigned char r = real_readb(0x60,0x110);
02357     unsigned int o = 80 * 24;
02358 
02359     if (pc98_function_row) {
02360         if (r > 23) r = 23;
02361 
02362         /* draw the function row.
02363          * based on on real hardware:
02364          *
02365          * The function key is 72 chars wide. 4 blank chars on each side of the screen.
02366          * It is divided into two halves, 36 chars each.
02367          * Within each half, aligned to it's side, is 5 x 7 regions.
02368          * 6 of the 7 are inverted. centered in the white block is the function key. */
02369         for (unsigned int i=0;i < 40;) {
02370             mem_writew(0xA0000+((o+i)*2),0x0000);
02371             mem_writeb(0xA2000+((o+i)*2),0xE1);
02372 
02373             mem_writew(0xA0000+((o+(79-i))*2),0x0000);
02374             mem_writeb(0xA2000+((o+(79-i))*2),0xE1);
02375 
02376             if (i >= 3 && i < 38)
02377                 i += 7;
02378             else
02379                 i++;
02380         }
02381 
02382         for (unsigned int i=0;i < 5u;i++) {
02383             unsigned int co = 4u + (i * 7u);
02384             const char *str = pc98_func_key[i];
02385 
02386             for (unsigned int j=0;j < 6u;j++) {
02387                 mem_writew(0xA0000+((o+co+j)*2u),(unsigned char)str[j]);
02388                 mem_writeb(0xA2000+((o+co+j)*2u),0xE5); // white  reverse  visible
02389            }
02390         }
02391 
02392         for (unsigned int i=5;i < 10;i++) {
02393             unsigned int co = 42u + ((i - 5u) * 7u);
02394             const char *str = pc98_func_key[i];
02395 
02396             for (unsigned int j=0;j < 6u;j++) {
02397                 mem_writew(0xA0000+((o+co+j)*2u),(unsigned char)str[j]);
02398                 mem_writeb(0xA2000+((o+co+j)*2u),0xE5); // white  reverse  visible
02399            }
02400         }
02401     }
02402     else {
02403         /* erase the function row */
02404         for (unsigned int i=0;i < 80;i++) {
02405             mem_writew(0xA0000+((o+i)*2),0x0000);
02406             mem_writeb(0xA2000+((o+i)*2),0xE1);
02407         }
02408     }
02409 
02410     real_writeb(0x60,0x11C,c);
02411     real_writeb(0x60,0x110,r);
02412 
02413     real_writeb(0x60,0x111,pc98_function_row ? 0x01 : 0x00);/* function key row display status */
02414 
02415     void vga_pc98_direct_cursor_pos(Bit16u address);
02416     vga_pc98_direct_cursor_pos((r*80)+c);
02417 }
02418 
02419 void pc98_set_digpal_entry(unsigned char ent,unsigned char grb);
02420 void PC98_show_cursor(bool show);
02421 
02422 extern bool                         gdc_5mhz_mode;
02423 extern bool                         enable_pc98_egc;
02424 extern bool                         enable_pc98_grcg;
02425 extern bool                         enable_pc98_16color;
02426 extern bool                         enable_pc98_188usermod;
02427 extern bool                         pc98_31khz_mode;
02428 extern bool                         pc98_attr4_graphic;
02429 
02430 extern unsigned char                pc98_text_first_row_scanline_start;  /* port 70h */
02431 extern unsigned char                pc98_text_first_row_scanline_end;    /* port 72h */
02432 extern unsigned char                pc98_text_row_scanline_blank_at;     /* port 74h */
02433 extern unsigned char                pc98_text_row_scroll_lines;          /* port 76h */
02434 extern unsigned char                pc98_text_row_scroll_count_start;    /* port 78h */
02435 extern unsigned char                pc98_text_row_scroll_num_lines;      /* port 7Ah */
02436 
02437 void pc98_update_text_layer_lineheight_from_bda(void) {
02438 //    unsigned char c = mem_readb(0x53C);
02439     unsigned char lineheight = mem_readb(0x53B) + 1;
02440 
02441     pc98_gdc[GDC_MASTER].force_fifo_complete();
02442     pc98_gdc[GDC_MASTER].row_height = lineheight;
02443 
02444     if (lineheight > 16) { // usually 20
02445         pc98_text_first_row_scanline_start = 0x1E;
02446         pc98_text_first_row_scanline_end = lineheight - 3;
02447         pc98_text_row_scanline_blank_at = 16;
02448     }
02449     else {
02450         pc98_text_first_row_scanline_start = 0;
02451         pc98_text_first_row_scanline_end = lineheight - 1;
02452         pc98_text_row_scanline_blank_at = lineheight;
02453     }
02454 
02455     pc98_text_row_scroll_lines = 0;
02456     pc98_text_row_scroll_count_start = 0;
02457     pc98_text_row_scroll_num_lines = 0;
02458 
02459     vga.crtc.cursor_start = 0;
02460     vga.draw.cursor.sline = 0;
02461 
02462     vga.crtc.cursor_end   = lineheight - 1;
02463     vga.draw.cursor.eline = lineheight - 1;
02464 }
02465 
02466 void pc98_update_text_lineheight_from_bda(void) {
02467     unsigned char c = mem_readb(0x53C);
02468     unsigned char lineheight;
02469 
02470     if (c & 0x01)/*20-line mode*/
02471         lineheight = 20;
02472     else         /*25-line mode*/
02473         lineheight = 16;
02474 
02475     mem_writeb(0x53B,lineheight - 1);
02476 }
02477 
02478 static Bitu INT18_PC98_Handler(void) {
02479     Bit16u temp16;
02480 
02481 #if 0
02482     if (reg_ah >= 0x0A) {
02483             LOG_MSG("PC-98 INT 18h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
02484                 reg_ax,
02485                 reg_bx,
02486                 reg_cx,
02487                 reg_dx,
02488                 reg_si,
02489                 reg_di,
02490                 SegValue(ds),
02491                 SegValue(es));
02492     }
02493 #endif
02494  
02495     /* NTS: Based on information gleaned from Neko Project II source code including comments which
02496      *      I've run through GNU iconv to convert from SHIFT-JIS to UTF-8 here in case Google Translate
02497      *      got anything wrong. */
02498     switch (reg_ah) {
02499         case 0x00: /* Reading of key data (キー・データの読みだし) */
02500             /* FIXME: We use the IBM PC/AT keyboard buffer to fake this call.
02501              *        This will be replaced with PROPER emulation once the PC-98 keyboard handler has been
02502              *        updated to write the buffer the way PC-98 BIOSes do it.
02503              *
02504              *        IBM PC/AT keyboard buffer at 40:1E-40:3D
02505              *
02506              *        PC-98 keyboard buffer at 50:02-50:21 */
02507             /* This call blocks until keyboard input */
02508             if (INT16_get_key(temp16)) {
02509                 reg_ax = temp16;
02510             }
02511             else {
02512                 /* Keyboard checks.
02513                  * If the interrupt got masked, unmask it.
02514                  * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler
02515                  * handled the keyboard interrupt and never read the keyboard (Quarth).
02516                  *
02517                  * TODO: Is this what real PC-98 BIOSes do? */
02518                 void check_keyboard_fire_IRQ1(void);
02519                 check_keyboard_fire_IRQ1();
02520                 IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1
02521 
02522                 reg_ip += 1; /* step over IRET, to NOPs which then JMP back to callback */
02523             }
02524             break;
02525         case 0x01: /* Sense of key buffer state (キー・バッファ状態のセンス) */
02526             /* This call returns whether or not there is input waiting.
02527              * The waiting data is read, but NOT discarded from the buffer. */
02528             if (INT16_peek_key(temp16)) {
02529                 reg_ax = temp16;
02530                 reg_bh = 1;
02531             }
02532             else {
02533                 /* Keyboard checks.
02534                  * If the interrupt got masked, unmask it.
02535                  * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler
02536                  * handled the keyboard interrupt and never read the keyboard (Quarth).
02537                  *
02538                  * TODO: Is this what real PC-98 BIOSes do? */
02539                 void check_keyboard_fire_IRQ1(void);
02540                 check_keyboard_fire_IRQ1();
02541                 IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1
02542 
02543                 reg_bh = 0;
02544             }
02545             break;
02546         case 0x02: /* Sense of shift key state (シフト・キー状態のセンス) */
02547             reg_al = mem_readb(0x52A + 0x0E); /* FIXME: Seems to match 14th bitmap byte. Does real hardware do this?? */
02548             break;
02549         case 0x03: /* Initialization of keyboard interface (キーボード・インタフェイスの初期化) */
02550             /* TODO */
02551             break;
02552         case 0x04: /* Sense of key input state (キー入力状態のセンス) */
02553             reg_ah = mem_readb(0x52A + (unsigned int)(reg_al & 0x0Fu));
02554             break;
02555         case 0x05: /* Key input sense (キー入力センス) */
02556             /* This appears to return a key from the buffer (and remove from
02557              * buffer) or return BH == 0 to signal no key was pending. */
02558             if (INT16_get_key(temp16)) {
02559                 reg_ax = temp16;
02560                 reg_bh = 1;
02561             }
02562             else {
02563                 /* Keyboard checks.
02564                  * If the interrupt got masked, unmask it.
02565                  * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler
02566                  * handled the keyboard interrupt and never read the keyboard (Quarth).
02567                  *
02568                  * TODO: Is this what real PC-98 BIOSes do? */
02569                 void check_keyboard_fire_IRQ1(void);
02570                 check_keyboard_fire_IRQ1();
02571                 IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1
02572 
02573                 reg_bh = 0;
02574             }
02575             break;
02576         case 0x0A: /* set CRT mode */
02577             /* bit      off         on
02578                 0       25lines     20lines
02579                 1       80cols      40cols
02580                 2       v.lines     simp.graphics
02581                 3       K-CG access mode(not used in PC-98) */
02582             
02583             //TODO: set 25/20 lines mode and 80/40 columns mode.
02584             //Attribute bit (bit 2)
02585             pc98_attr4_graphic = !!(reg_al & 0x04);
02586 
02587             mem_writeb(0x53C,reg_al);
02588 
02589             if (reg_al & 2)
02590                 LOG_MSG("INT 18H AH=0Ah warning: 40-column PC-98 text mode not supported");
02591             if (reg_al & 8)
02592                 LOG_MSG("INT 18H AH=0Ah warning: K-CG dot access mode not supported");
02593 
02594             pc98_update_text_lineheight_from_bda();
02595             pc98_update_text_layer_lineheight_from_bda();
02596 
02597             /* Apparently, this BIOS call also hides the cursor */
02598             PC98_show_cursor(0);
02599             break;
02600         case 0x0B: /* get CRT mode */
02601             /* bit      off         on
02602                 0       25lines     20lines
02603                 1       80cols      40cols
02604                 2       v.lines     simp.graphics
02605                 3       K-CG access mode(not used in PC-98) 
02606                 7       std CRT     hi-res CRT */
02607             /* NTS: I assume that real hardware doesn't offer a way to read back the state of these bits,
02608              *      so the BIOS's only option is to read the mode byte back from the data area.
02609              *      Neko Project II agrees. */
02610             reg_al = mem_readb(0x53C);
02611             break;
02612         // TODO: "Edge" is using INT 18h AH=06h, what is that?
02613         //       Neko Project is also unaware of such a call.
02614         case 0x0C: /* text layer enable */
02615             pc98_gdc[GDC_MASTER].force_fifo_complete();
02616             pc98_gdc[GDC_MASTER].display_enable = true;
02617             break;
02618         case 0x0D: /* text layer disable */
02619             pc98_gdc[GDC_MASTER].force_fifo_complete();
02620             pc98_gdc[GDC_MASTER].display_enable = false;
02621             break;
02622         case 0x0E: /* set text display area (DX=byte offset) */
02623             pc98_gdc[GDC_MASTER].force_fifo_complete();
02624             pc98_gdc[GDC_MASTER].param_ram[0] = (reg_dx >> 1) & 0xFF;
02625             pc98_gdc[GDC_MASTER].param_ram[1] = (reg_dx >> 9) & 0xFF;
02626             pc98_gdc[GDC_MASTER].param_ram[2] = (400 << 4) & 0xFF;
02627             pc98_gdc[GDC_MASTER].param_ram[3] = (400 << 4) >> 8;
02628             break;
02629         case 0x11: /* show cursor */
02630             PC98_show_cursor(true);
02631             break;
02632         case 0x12: /* hide cursor */
02633             PC98_show_cursor(false);
02634             break;
02635         case 0x13: /* set cursor position (DX=byte position) */
02636             void vga_pc98_direct_cursor_pos(Bit16u address);
02637 
02638             pc98_gdc[GDC_MASTER].force_fifo_complete();
02639             vga_pc98_direct_cursor_pos(reg_dx >> 1);
02640             pc98_gdc[GDC_MASTER].cursor_enable = true; // FIXME: Right?
02641             break;
02642         case 0x14: /* read FONT RAM */
02643             {
02644                 unsigned int i,o,r;
02645 
02646                 /* DX = code (must be 0x76xx or 0x7700)
02647                  * BX:CX = 34-byte region to write to */
02648                 if (reg_dh == 0x80u) { /* 8x16 ascii */
02649                     i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u;
02650                     mem_writew(i-2u,0x0102u);
02651                     for (r=0;r < 16u;r++) {
02652                         o = (reg_dl*16u)+r;
02653 
02654                         assert((o+2u) <= sizeof(vga.draw.font));
02655 
02656                         mem_writeb(i+r,vga.draw.font[o]);
02657                     }
02658                 }
02659                 else if ((reg_dh & 0xFC) == 0x28) { /* 8x16 kanji */
02660                     i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u;
02661                     mem_writew(i-2u,0x0102u);
02662                     for (r=0;r < 16u;r++) {
02663                         o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u;
02664 
02665                         assert((o+2u) <= sizeof(vga.draw.font));
02666 
02667                         mem_writeb(i+r+0u,vga.draw.font[o+0u]);
02668                     }
02669                 }
02670                 else if (reg_dh != 0) { /* 16x16 kanji */
02671                     i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u;
02672                     mem_writew(i-2u,0x0202u);
02673                     for (r=0;r < 16u;r++) {
02674                         o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u;
02675 
02676                         assert((o+2u) <= sizeof(vga.draw.font));
02677 
02678                         mem_writeb(i+(r*2u)+0u,vga.draw.font[o+0u]);
02679                         mem_writeb(i+(r*2u)+1u,vga.draw.font[o+1u]);
02680                     }
02681                 }
02682                 else {
02683                     LOG_MSG("PC-98 INT 18h AH=14h font RAM read ignored, code 0x%04x not supported",reg_dx);
02684                 }
02685             }
02686             break;
02687         case 0x16: /* fill screen with chr + attr */
02688             {
02689                 /* DL = character
02690                  * DH = attribute */
02691                 unsigned int i;
02692 
02693                 for (i=0;i < 0x2000;i += 2) {
02694                     vga.mem.linear[i+0] = reg_dl;
02695                     vga.mem.linear[i+1] = 0x00;
02696                 }
02697                 for (   ;i < 0x3FE0;i += 2) {
02698                     vga.mem.linear[i+0] = reg_dh;
02699                     vga.mem.linear[i+1] = 0x00;
02700                 }
02701             }
02702             break;
02703         case 0x1A: /* load FONT RAM */
02704             {
02705                 unsigned int i,o,r;
02706 
02707                 /* DX = code (must be 0x76xx or 0x7700)
02708                  * BX:CX = 34-byte region to read from */
02709                 if ((reg_dh & 0x7Eu) == 0x76u) {
02710                     i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u;
02711                     for (r=0;r < 16u;r++) {
02712                         o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u;
02713 
02714                         assert((o+2u) <= sizeof(vga.draw.font));
02715 
02716                         vga.draw.font[o+0u] = mem_readb(i+(r*2u)+0u);
02717                         vga.draw.font[o+1u] = mem_readb(i+(r*2u)+1u);
02718                     }
02719                 }
02720                 else {
02721                     LOG_MSG("PC-98 INT 18h AH=1Ah font RAM load ignored, code 0x%04x out of range",reg_dx);
02722                 }
02723             }
02724             break;
02725         case 0x31: /* Return display mode and status */
02726             if (enable_pc98_egc) { /* FIXME: INT 18h AH=31/30h availability is tied to EGC enable */
02727                 unsigned char b597 = mem_readb(0x597);
02728                 unsigned char tstat = mem_readb(0x53C);
02729                 /* Return values:
02730                  *
02731                  * AL =
02732                  *      bit [7:7] = ?
02733                  *      bit [6:6] = ?
02734                  *      bit [5:5] = ?
02735                  *      bit [4:4] = ?
02736                  *      bit [3:2] = horizontal sync
02737                  *                   00 = 15.98KHz
02738                  *                   01 = ?
02739                  *                   10 = 24.83KHz
02740                  *                   11 = 31.47KHz
02741                  *      bit [1:1] = ?
02742                  *      bit [0:0] = interlaced (1=yes 0=no)
02743                  * BH =
02744                  *      bit [7:7] = ?
02745                  *      bit [6:6] = ?
02746                  *      bit [5:4] = graphics video mode
02747                  *                   00 = 640x200 (upper half)
02748                  *                   01 = 640x200 (lower half)
02749                  *                   10 = 640x400
02750                  *                   11 = 640x480
02751                  *      bit [3:3] = ?
02752                  *      bit [2:2] = ?
02753                  *      bit [1:0] = number of text rows
02754                  *                   00 = 20 rows
02755                  *                   01 = 25 rows
02756                  *                   10 = 30 rows
02757                  *                   11 = ?
02758                  */
02759                 reg_al =
02760                     ((pc98_31khz_mode ? 3 : 2) << 2)/*hsync*/;
02761                 reg_bh =
02762                     ((b597 & 3) << 4)/*graphics video mode*/;
02763                 if (tstat & 0x10)
02764                     reg_bh |= 2;/*30 rows*/
02765                 else if ((tstat & 0x01) == 0)
02766                     reg_bh |= 1;/*25 rows*/
02767             }
02768             break;
02769         /* From this point on the INT 18h call list appears to wander off from the keyboard into CRT/GDC/display management. */
02770         case 0x40: /* Start displaying the graphics screen (グラフィック画面の表示開始) */
02771             pc98_gdc[GDC_SLAVE].force_fifo_complete();
02772             pc98_gdc[GDC_SLAVE].display_enable = true;
02773  
02774             {
02775                 unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/);
02776                 mem_writeb(0x54C/*MEMB_PRXCRT*/,b | 0x80);
02777             }
02778             break;
02779         case 0x41: /* Stop displaying the graphics screen (グラフィック画面の表示終了) */
02780             pc98_gdc[GDC_SLAVE].force_fifo_complete();
02781             pc98_gdc[GDC_SLAVE].display_enable = false;
02782 
02783             {
02784                 unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/);
02785                 mem_writeb(0x54C/*MEMB_PRXCRT*/,b & (~0x80));
02786             }
02787             break;
02788         case 0x42: /* Display area setup (表示領域の設定) */
02789             pc98_gdc[GDC_MASTER].force_fifo_complete();
02790             pc98_gdc[GDC_SLAVE].force_fifo_complete();
02791             /* reset scroll area of graphics */
02792             if ((reg_ch & 0xC0) == 0x40) { /* 640x200 G-RAM upper half */
02793                 pc98_gdc[GDC_SLAVE].param_ram[0] = (200*40) & 0xFF;
02794                 pc98_gdc[GDC_SLAVE].param_ram[1] = (200*40) >> 8;
02795             }
02796             else {
02797                 pc98_gdc[GDC_SLAVE].param_ram[0] = 0;
02798                 pc98_gdc[GDC_SLAVE].param_ram[1] = 0;
02799             }
02800             pc98_gdc[GDC_SLAVE].param_ram[2] = 0xF0;
02801             pc98_gdc[GDC_SLAVE].param_ram[3] = 0x3F;
02802             pc98_gdc[GDC_SLAVE].active_display_words_per_line = 40; /* 40 x 16 = 640 pixel wide graphics */
02803 
02804             // CH
02805             //   [7:6] = G-RAM setup
02806             //           00 = no graphics (?)
02807             //           01 = 640x200 upper half
02808             //           10 = 640x200 lower half
02809             //           11 = 640x400
02810             //   [5:5] = CRT
02811             //           0 = color
02812             //           1 = monochrome
02813             //   [4:4] = Display bank
02814 
02815             // FIXME: This is a guess. I have no idea as to actual behavior, yet.
02816             //        This seems to help with clearing the text layer when games start the graphics.
02817             //        This is ALSO how we will detect games that switch on the 200-line double-scan mode vs 400-line mode.
02818             if ((reg_ch & 0xC0) != 0) {
02819                 pc98_gdc[GDC_SLAVE].doublescan = ((reg_ch & 0xC0) == 0x40) || ((reg_ch & 0xC0) == 0x80);
02820                 pc98_gdc[GDC_SLAVE].row_height = pc98_gdc[GDC_SLAVE].doublescan ? 2 : 1;
02821 
02822                 /* update graphics mode bits */
02823                 {
02824                     unsigned char b = mem_readb(0x597);
02825 
02826                     b &= ~3;
02827                     b |= ((reg_ch >> 6) - 1) & 3;
02828 
02829                     mem_writeb(0x597,b);
02830                 }
02831             }
02832             else {
02833                 pc98_gdc[GDC_SLAVE].doublescan = false;
02834                 pc98_gdc[GDC_SLAVE].row_height = 1;
02835             }
02836 
02837             {
02838                 unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/);
02839 
02840                 // Real hardware behavior: graphics selection updated by BIOS to reflect MEMB_PRXCRT state
02841                 pc98_gdc[GDC_SLAVE].display_enable = !!(b & 0x80);
02842             }
02843 
02844             pc98_gdc_vramop &= ~(1 << VOPBIT_ACCESS);
02845             GDC_display_plane = GDC_display_plane_pending = (reg_ch & 0x10) ? 1 : 0;
02846 
02847             prev_pc98_mode42 = reg_ch;
02848 
02849             LOG_MSG("PC-98 INT 18 AH=42h CH=0x%02X",reg_ch);
02850             break;
02851         case 0x43:  // Palette register settings? Only works in digital mode? --leonier
02852                     //
02853                     // This is said to fix Thexder's GAME ARTS logo. --Jonathan C.
02854                     //
02855                     // TODO: Validate this against real PC-98 hardware and BIOS
02856             {
02857                 unsigned int gbcpc = SegValue(ds)*0x10u + reg_bx;
02858                 for(unsigned int i=0;i<4;i++)
02859                 {
02860                     unsigned char p=mem_readb(gbcpc+4u+i);
02861                     pc98_set_digpal_entry(7u-2u*i, p&0xFu);
02862                     pc98_set_digpal_entry(6u-2u*i, p>>4u);
02863                 }
02864                 LOG_MSG("PC-98 INT 18 AH=43h CX=0x%04X DS=0x%04X", reg_cx, SegValue(ds));
02865                 break;
02866             }
02867         default:
02868             LOG_MSG("PC-98 INT 18h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
02869                 reg_ax,
02870                 reg_bx,
02871                 reg_cx,
02872                 reg_dx,
02873                 reg_si,
02874                 reg_di,
02875                 SegValue(ds),
02876                 SegValue(es));
02877             break;
02878     };
02879 
02880     /* FIXME: What do actual BIOSes do when faced with an unknown INT 18h call? */
02881     return CBRET_NONE;
02882 }
02883 
02884 #define PC98_FLOPPY_HIGHDENSITY     0x01
02885 #define PC98_FLOPPY_2HEAD           0x02
02886 #define PC98_FLOPPY_RPM_3MODE       0x04
02887 #define PC98_FLOPPY_RPM_IBMPC       0x08
02888 
02889 unsigned char PC98_BIOS_FLOPPY_BUFFER[32768]; /* 128 << 8 */
02890 
02891 static unsigned int PC98_FDC_SZ_TO_BYTES(unsigned int sz) {
02892     return 128U << sz;
02893 }
02894 
02895 int PC98_BIOS_SCSI_POS(imageDisk *floppy,Bit32u &sector) {
02896     if (reg_al & 0x80) {
02897         Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0;
02898 
02899         floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
02900 
02901         /* DL = sector
02902          * DH = head
02903          * CX = track */
02904         if (reg_dl >= img_sect ||
02905             reg_dh >= img_heads ||
02906             reg_cx >= img_cyl) {
02907             return (reg_ah=0x60);
02908         }
02909 
02910         sector  = reg_cx;
02911         sector *= img_heads;
02912         sector += reg_dh;
02913         sector *= img_sect;
02914         sector += reg_dl;
02915 
02916 //        LOG_MSG("Sector CHS %u/%u/%u -> %u (geo %u/%u/%u)",reg_cx,reg_dh,reg_dl,sector,img_cyl,img_heads,img_sect);
02917     }
02918     else {
02919         /* Linear LBA addressing */
02920         sector = ((unsigned int)reg_dl << 16UL) + reg_cx;
02921         /* TODO: SASI caps at 0x1FFFFF according to NP2 */
02922     }
02923 
02924     return 0;
02925 }
02926 
02927 void PC98_BIOS_SCSI_CALL(void) {
02928     Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0;
02929     Bit32u memaddr,size,ssize;
02930     imageDisk *floppy;
02931     unsigned int i;
02932     Bit32u sector;
02933     int idx;
02934 
02935 #if 0
02936             LOG_MSG("PC-98 INT 1Bh SCSI BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
02937                     reg_ax,
02938                     reg_bx,
02939                     reg_cx,
02940                     reg_dx,
02941                     reg_si,
02942                     reg_di,
02943                     SegValue(ds),
02944                     SegValue(es));
02945 #endif
02946 
02947     idx = (reg_al & 0xF) + 2;
02948     if (idx < 0 || idx >= MAX_DISK_IMAGES) {
02949         CALLBACK_SCF(true);
02950         reg_ah = 0x00;
02951         /* TODO? Error code? */
02952         return;
02953     }
02954 
02955     floppy = imageDiskList[idx];
02956     if (floppy == NULL) {
02957         CALLBACK_SCF(true);
02958         reg_ah = 0x60;
02959         return;
02960     }
02961 
02962     /* what to do is in the lower 4 bits of AH */
02963     switch (reg_ah & 0x0F) {
02964         case 0x05: /* write */
02965             if (PC98_BIOS_SCSI_POS(floppy,/*&*/sector) == 0) {
02966                 floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
02967                 assert(img_ssz != 0);
02968 
02969                 size = reg_bx;
02970                 if (size == 0) size = 0x10000U;
02971                 memaddr = ((unsigned int)SegValue(es) << 4u) + reg_bp;
02972 
02973                 reg_ah = 0;
02974                 CALLBACK_SCF(false);
02975 
02976 //                LOG_MSG("WRITE memaddr=0x%lx size=0x%x sector=0x%lx ES:BP=%04x:%04X",
02977 //                    (unsigned long)memaddr,(unsigned int)size,(unsigned long)sector,SegValue(es),reg_bp);
02978 
02979                 while (size != 0) {
02980                     ssize = size;
02981                     if (ssize > img_ssz) ssize = img_ssz;
02982 
02983 //                    LOG_MSG(" ... memaddr=0x%lx ssize=0x%x sector=0x%lx",
02984 //                        (unsigned long)memaddr,(unsigned int)ssize,(unsigned long)sector);
02985 
02986                     for (i=0;i < ssize;i++) PC98_BIOS_FLOPPY_BUFFER[i] = mem_readb(memaddr+i);
02987 
02988                     if (floppy->Write_AbsoluteSector(sector,PC98_BIOS_FLOPPY_BUFFER) == 0) {
02989                     }
02990                     else {
02991                         reg_ah = 0xD0;
02992                         CALLBACK_SCF(true);
02993                         break;
02994                     }
02995 
02996                     sector++;
02997                     size -= ssize;
02998                     memaddr += ssize;
02999                 }
03000             }
03001             else {
03002                 CALLBACK_SCF(true);
03003             }
03004             break;
03005         case 0x06: /* read */
03006             if (PC98_BIOS_SCSI_POS(floppy,/*&*/sector) == 0) {
03007                 floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
03008                 assert(img_ssz != 0);
03009 
03010                 size = reg_bx;
03011                 if (size == 0) size = 0x10000U;
03012                 memaddr = ((unsigned int)SegValue(es) << 4u) + reg_bp;
03013 
03014                 reg_ah = 0;
03015                 CALLBACK_SCF(false);
03016 
03017 //                LOG_MSG("READ memaddr=0x%lx size=0x%x sector=0x%lx ES:BP=%04x:%04X",
03018 //                    (unsigned long)memaddr,(unsigned int)size,(unsigned long)sector,SegValue(es),reg_bp);
03019 
03020                 while (size != 0) {
03021                     ssize = size;
03022                     if (ssize > img_ssz) ssize = img_ssz;
03023 
03024 //                    LOG_MSG(" ... memaddr=0x%lx ssize=0x%x sector=0x%lx",
03025 //                        (unsigned long)memaddr,(unsigned int)ssize,(unsigned long)sector);
03026 
03027                     if (floppy->Read_AbsoluteSector(sector,PC98_BIOS_FLOPPY_BUFFER) == 0) {
03028                         for (i=0;i < ssize;i++) mem_writeb(memaddr+i,PC98_BIOS_FLOPPY_BUFFER[i]);
03029                     }
03030                     else {
03031                         reg_ah = 0xD0;
03032                         CALLBACK_SCF(true);
03033                         break;
03034                     }
03035 
03036                     sector++;
03037                     size -= ssize;
03038                     memaddr += ssize;
03039                 }
03040             }
03041             else {
03042                 CALLBACK_SCF(true);
03043             }
03044             break;
03045         case 0x07: /* unknown, always succeeds */
03046             reg_ah = 0x00;
03047             CALLBACK_SCF(false);
03048             break;
03049         case 0x0E: /* unknown, always fails */
03050             reg_ah = 0x40;
03051             CALLBACK_SCF(true);
03052             break;
03053         case 0x04: /* drive status */
03054             if (reg_ah == 0x84) {
03055                 floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
03056 
03057                 reg_dl = img_sect;
03058                 reg_dh = img_heads; /* Max 16 */
03059                 reg_cx = img_cyl;   /* Max 4096 */
03060                 reg_bx = img_ssz;
03061 
03062                 reg_ah = 0x00;
03063                 CALLBACK_SCF(false);
03064                 break;
03065             }
03066             else if (reg_ah == 0x04 || reg_ah == 0x14) {
03067                 reg_ah = 0x00;
03068                 CALLBACK_SCF(false);
03069             }
03070             else {
03071                 goto default_goto;
03072             }
03073         default:
03074         default_goto:
03075             LOG_MSG("PC-98 INT 1Bh unknown SCSI BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03076                     reg_ax,
03077                     reg_bx,
03078                     reg_cx,
03079                     reg_dx,
03080                     reg_si,
03081                     reg_di,
03082                     SegValue(ds),
03083                     SegValue(es));
03084             CALLBACK_SCF(true);
03085             break;
03086     };
03087 }
03088 
03089 void PC98_BIOS_FDC_CALL_GEO_UNPACK(unsigned int &fdc_cyl,unsigned int &fdc_head,unsigned int &fdc_sect,unsigned int &fdc_sz) {
03090     fdc_cyl = reg_cl;
03091     fdc_head = reg_dh;
03092     fdc_sect = reg_dl;
03093     fdc_sz = reg_ch;
03094     if (fdc_sz > 8) fdc_sz = 8;
03095 }
03096 
03097 /* NTS: FDC calls reset IRQ 0 timer to a specific fixed interval,
03098  *      because the real BIOS likely does the same in the act of
03099  *      controlling the floppy drive.
03100  *
03101  *      Resetting the interval is required to prevent Ys II from
03102  *      crashing after disk swap (divide by zero/overflow) because
03103  *      Ys II reads the timer after INT 1Bh for whatever reason
03104  *      and the upper half of the timer byte later affects a divide
03105  *      by 3 in the code. */
03106 
03107 void PC98_Interval_Timer_Continue(void);
03108 
03109 bool enable_fdc_timer_hack = false;
03110 
03111 void FDC_WAIT_TIMER_HACK(void) {
03112     unsigned int v,pv;
03113     unsigned int c=0;
03114 
03115     // Explanation:
03116     //
03117     // Originally the FDC code here changed the timer interval back to the stock 100hz
03118     // normally used in PC-98, to fix Ys II. However that seems to break other booter
03119     // games that hook IRQ 0 directly and set the timer ONCE, then access the disk.
03120     //
03121     // For example, "Angelus" ran WAY too slow with the timer hack because it programs
03122     // the timer to a 600hz interval and expects it to stay that way.
03123     //
03124     // So the new method to satisfy both games is to loop here until the timer
03125     // count is below the maximum that would occur if the 100hz tick count were
03126     // still in effect, even if the timer interval was reprogrammed.
03127     //
03128     // NTS: Writing port 0x77 to relatch the timer also seems to break games
03129     //
03130     // TODO: As a safety against getting stuck, perhaps PIC_FullIndex() should be used
03131     //       to break out of the loop if this runs for more than 1 second, since that
03132     //       is a sign the timer is in an odd state that will never terminate this loop.
03133 
03134     v = ~0U;
03135     c = 10;
03136     do {
03137         void CALLBACK_Idle(void);
03138         CALLBACK_Idle();
03139 
03140         pv = v;
03141 
03142         v  = IO_ReadB(0x71);
03143         v |= IO_ReadB(0x71) << 8;
03144 
03145         if (v > pv) {
03146             /* if the timer rolled around, we might have missed the narrow window we're watching for */
03147             if (--c == 0) break;
03148         }
03149     } while (v >= 0x60);
03150 }
03151 
03152 void PC98_BIOS_FDC_CALL(unsigned int flags) {
03153     static unsigned int fdc_cyl[2]={0,0},fdc_head[2]={0,0},fdc_sect[2]={0,0},fdc_sz[2]={0,0}; // FIXME: Rename and move out. Making "static" is a hack here.
03154     Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0;
03155     unsigned int drive;
03156     unsigned int status;
03157     unsigned int size,accsize,unitsize;
03158     unsigned long memaddr;
03159     imageDisk *floppy;
03160 
03161     /* AL bits[1:0] = which floppy drive */
03162     if ((reg_al & 3) >= 2) {
03163         /* This emulation only supports up to 2 floppy drives */
03164         CALLBACK_SCF(true);
03165         reg_ah = 0x00;
03166         /* TODO? Error code? */
03167         return;
03168     }
03169 
03170     floppy = GetINT13FloppyDrive(drive=(reg_al & 3));
03171 
03172     /* what to do is in the lower 4 bits of AH */
03173     switch (reg_ah & 0x0F) {
03174         /* TODO: 0x00 = seek to track (in CL) */
03175         /* TODO: 0x01 = test read? */
03176         /* TODO: 0x03 = equipment flags? */
03177         /* TODO: 0x04 = format detect? */
03178         /* TODO: 0x05 = write disk */
03179         /* TODO: 0x07 = recalibrate (seek to track 0) */
03180         /* TODO: 0x0A = Read ID */
03181         /* TODO: 0x0D = Format track */
03182         /* TODO: 0x0E = ?? */
03183         case 0x03: /* equipment flags update (?) */
03184             // TODO: Update the disk equipment flags in BDA.
03185             //       For now, make Alantia happy by returning success.
03186             reg_ah = 0x00;
03187             CALLBACK_SCF(false);
03188             break;
03189         case 0x00: /* seek */
03190             /* CL = track */
03191             if (floppy == NULL) {
03192                 CALLBACK_SCF(true);
03193                 reg_ah = 0x00;
03194                 /* TODO? Error code? */
03195                 return;
03196             }
03197 
03198             if (enable_fdc_timer_hack) {
03199                 // Hack for Ys II
03200                 FDC_WAIT_TIMER_HACK();
03201             }
03202 
03203             fdc_cyl[drive] = reg_cl;
03204 
03205             reg_ah = 0x00;
03206             CALLBACK_SCF(false);
03207             break;
03208         case 0x01: /* test read */
03209             /* AH bits[4:4] = If set, seek to track specified */
03210             /* CL           = cylinder (track) */
03211             /* CH           = sector size (0=128 1=256 2=512 3=1024 etc) */
03212             /* DL           = sector number (1-based) */
03213             /* DH           = head */
03214             /* BX           = size (in bytes) of data to read */
03215             /* ES:BP        = buffer to read data into */
03216             if (floppy == NULL) {
03217                 CALLBACK_SCF(true);
03218                 reg_ah = 0x00;
03219                 /* TODO? Error code? */
03220                 return;
03221             }
03222             floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
03223 
03224             if (enable_fdc_timer_hack) {
03225                 // Hack for Ys II
03226                 FDC_WAIT_TIMER_HACK();
03227             }
03228 
03229             /* Prevent reading 1.44MB floppyies using 1.2MB read commands and vice versa.
03230              * FIXME: It seems MS-DOS 5.0 booted from a HDI image has trouble understanding
03231              *        when Drive A: (the first floppy) is a 1.44MB drive or not and fails
03232              *        because it only attempts it using 1.2MB format read commands. */
03233             if (flags & PC98_FLOPPY_RPM_IBMPC) {
03234                 if (img_ssz == 1024) { /* reject 1.2MB 3-mode format */
03235                     CALLBACK_SCF(true);
03236                     reg_ah = 0x00;
03237                     /* TODO? Error code? */
03238                     return;
03239                 }
03240             }
03241             else {
03242                 if (img_ssz == 512) { /* reject IBM PC 1.44MB format */
03243                     CALLBACK_SCF(true);
03244                     reg_ah = 0x00;
03245                     /* TODO? Error code? */
03246                     return;
03247                 }
03248             }
03249 
03250             PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]);
03251             unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]);
03252             if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) {
03253                 CALLBACK_SCF(true);
03254                 reg_ah = 0x00;
03255                 /* TODO? Error code? */
03256                 return;
03257             }
03258 
03259             size = reg_bx;
03260             while (size > 0) {
03261                 accsize = size > unitsize ? unitsize : size;
03262 
03263                 if (floppy->Read_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) {
03264                     CALLBACK_SCF(true);
03265                     reg_ah = 0x00;
03266                     /* TODO? Error code? */
03267                     return;
03268                 }
03269 
03270                 size -= accsize;
03271 
03272                 if (size == 0) break;
03273 
03274                 if ((++fdc_sect[drive]) > img_sect && img_sect != 0) {
03275                     fdc_sect[drive] = 1;
03276                     if ((++fdc_head[drive]) >= img_heads && img_heads != 0) {
03277                         fdc_head[drive] = 0;
03278                         fdc_cyl[drive]++;
03279                     }
03280                 }
03281             }
03282 
03283             reg_ah = 0x00;
03284             CALLBACK_SCF(false);
03285             break;
03286         case 0x02: /* read sectors */
03287         case 0x06: /* read sectors (what's the difference from 0x02?) */
03288             /* AH bits[4:4] = If set, seek to track specified */
03289             /* CL           = cylinder (track) */
03290             /* CH           = sector size (0=128 1=256 2=512 3=1024 etc) */
03291             /* DL           = sector number (1-based) */
03292             /* DH           = head */
03293             /* BX           = size (in bytes) of data to read */
03294             /* ES:BP        = buffer to read data into */
03295             if (floppy == NULL) {
03296                 CALLBACK_SCF(true);
03297                 reg_ah = 0x00;
03298                 /* TODO? Error code? */
03299                 return;
03300             }
03301             floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
03302 
03303             if (enable_fdc_timer_hack) {
03304                 // Hack for Ys II
03305                 FDC_WAIT_TIMER_HACK();
03306             }
03307 
03308             /* Prevent reading 1.44MB floppyies using 1.2MB read commands and vice versa.
03309              * FIXME: It seems MS-DOS 5.0 booted from a HDI image has trouble understanding
03310              *        when Drive A: (the first floppy) is a 1.44MB drive or not and fails
03311              *        because it only attempts it using 1.2MB format read commands. */
03312             if (flags & PC98_FLOPPY_RPM_IBMPC) {
03313                 if (img_ssz == 1024) { /* reject 1.2MB 3-mode format */
03314                     CALLBACK_SCF(true);
03315                     reg_ah = 0x00;
03316                     /* TODO? Error code? */
03317                     return;
03318                 }
03319             }
03320             else {
03321                 if (img_ssz == 512) { /* reject IBM PC 1.44MB format */
03322                     CALLBACK_SCF(true);
03323                     reg_ah = 0x00;
03324                     /* TODO? Error code? */
03325                     return;
03326                 }
03327             }
03328 
03329             PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]);
03330             unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]);
03331             if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) {
03332                 CALLBACK_SCF(true);
03333                 reg_ah = 0x00;
03334                 /* TODO? Error code? */
03335                 return;
03336             }
03337 
03338             size = reg_bx;
03339             memaddr = ((unsigned int)SegValue(es) << 4U) + reg_bp;
03340             while (size > 0) {
03341                 accsize = size > unitsize ? unitsize : size;
03342 
03343                 if (floppy->Read_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) {
03344                     CALLBACK_SCF(true);
03345                     reg_ah = 0x00;
03346                     /* TODO? Error code? */
03347                     return;
03348                 }
03349 
03350                 for (unsigned int i=0;i < accsize;i++)
03351                     mem_writeb(memaddr+i,PC98_BIOS_FLOPPY_BUFFER[i]);
03352 
03353                 memaddr += accsize;
03354                 size -= accsize;
03355 
03356                 if (size == 0) break;
03357 
03358                 if ((++fdc_sect[drive]) > img_sect && img_sect != 0) {
03359                     fdc_sect[drive] = 1;
03360                     if ((++fdc_head[drive]) >= img_heads && img_heads != 0) {
03361                         fdc_head[drive] = 0;
03362                         fdc_cyl[drive]++;
03363                     }
03364                 }
03365             }
03366 
03367             reg_ah = 0x00;
03368             CALLBACK_SCF(false);
03369             break;
03370         case 0x04: /* drive status */
03371             status = 0;
03372 
03373             /* TODO: bit 4 is set if write protected */
03374 
03375             if (reg_al & 0x80) { /* high density */
03376                 status |= 0x01;
03377             }
03378             else { /* double density */
03379                 /* TODO: */
03380                 status |= 0x01;
03381             }
03382 
03383             if ((reg_ax & 0x8F40) == 0x8400) {
03384                 status |= 8;        /* 1MB/640KB format, spindle speed for 3-mode */
03385                 if (reg_ah & 0x40) /* DOSBox-X always supports 1.44MB */
03386                     status |= 4;    /* 1.44MB format, spindle speed for IBM PC format */
03387             }
03388 
03389             if (floppy == NULL)
03390                 status |= 0xC0;
03391 
03392             reg_ah = status;
03393             CALLBACK_SCF(false);
03394             break;
03395         /* TODO: 0x00 = seek to track (in CL) */
03396         /* TODO: 0x01 = test read? */
03397         /* TODO: 0x03 = equipment flags? */
03398         /* TODO: 0x04 = format detect? */
03399         /* TODO: 0x05 = write disk */
03400         /* TODO: 0x07 = recalibrate (seek to track 0) */
03401         /* TODO: 0x0A = Read ID */
03402         /* TODO: 0x0D = Format track */
03403         /* TODO: 0x0E = ?? */
03404         case 0x05: /* write sectors */
03405             /* AH bits[4:4] = If set, seek to track specified */
03406             /* CL           = cylinder (track) */
03407             /* CH           = sector size (0=128 1=256 2=512 3=1024 etc) */
03408             /* DL           = sector number (1-based) */
03409             /* DH           = head */
03410             /* BX           = size (in bytes) of data to read */
03411             /* ES:BP        = buffer to write data from */
03412             if (floppy == NULL) {
03413                 CALLBACK_SCF(true);
03414                 reg_ah = 0x00;
03415                 /* TODO? Error code? */
03416                 return;
03417             }
03418             floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
03419 
03420             if (enable_fdc_timer_hack) {
03421                 // Hack for Ys II
03422                 FDC_WAIT_TIMER_HACK();
03423             }
03424 
03425             /* TODO: Error if write protected */
03426 
03427             PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]);
03428             unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]);
03429             if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) {
03430                 CALLBACK_SCF(true);
03431                 reg_ah = 0x00;
03432                 /* TODO? Error code? */
03433                 return;
03434             }
03435 
03436             size = reg_bx;
03437             memaddr = ((unsigned int)SegValue(es) << 4U) + reg_bp;
03438             while (size > 0) {
03439                 accsize = size > unitsize ? unitsize : size;
03440 
03441                 for (unsigned int i=0;i < accsize;i++)
03442                     PC98_BIOS_FLOPPY_BUFFER[i] = mem_readb(memaddr+i);
03443 
03444                 if (floppy->Write_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) {
03445                     CALLBACK_SCF(true);
03446                     reg_ah = 0x00;
03447                     /* TODO? Error code? */
03448                     return;
03449                 }
03450 
03451                 memaddr += accsize;
03452                 size -= accsize;
03453 
03454                 if (size == 0) break;
03455 
03456                 if ((++fdc_sect[drive]) > img_sect && img_sect != 0) {
03457                     fdc_sect[drive] = 1;
03458                     if ((++fdc_head[drive]) >= img_heads && img_heads != 0) {
03459                         fdc_head[drive] = 0;
03460                         fdc_cyl[drive]++;
03461                     }
03462                 }
03463             }
03464 
03465             reg_ah = 0x00;
03466             CALLBACK_SCF(false);
03467             break;
03468         case 0x07: /* recalibrate (seek to track 0) */
03469             if (floppy == NULL) {
03470                 CALLBACK_SCF(true);
03471                 reg_ah = 0x00;
03472                 /* TODO? Error code? */
03473                 return;
03474             }
03475 
03476             if (enable_fdc_timer_hack) {
03477                 // Hack for Ys II
03478                 FDC_WAIT_TIMER_HACK();
03479             }
03480 
03481             reg_ah = 0x00;
03482             CALLBACK_SCF(false);
03483             break;
03484         case 0x0D: /* format track */
03485             if (floppy == NULL) {
03486                 CALLBACK_SCF(true);
03487                 reg_ah = 0x00;
03488                 /* TODO? Error code? */
03489                 return;
03490             }
03491 
03492             PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]);
03493             unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]);
03494 
03495             if (enable_fdc_timer_hack) {
03496                 // Hack for Ys II
03497                 FDC_WAIT_TIMER_HACK();
03498             }
03499 
03500             LOG_MSG("WARNING: INT 1Bh FDC format track command not implemented. Formatting is faked, for now on C/H/S/sz %u/%u/%u/%u drive %c.",
03501                 (unsigned int)fdc_cyl[drive],
03502                 (unsigned int)fdc_head[drive],
03503                 (unsigned int)fdc_sect[drive],
03504                 (unsigned int)unitsize,
03505                 drive + 'A');
03506 
03507             reg_ah = 0x00;
03508             CALLBACK_SCF(false);
03509             break;
03510         case 0x0A: /* read ID */
03511             /* NTS: PC-98 "MEGDOS" used by some games seems to rely heavily on this call to
03512              *      verify the floppy head is where it thinks it should be! */
03513             if (floppy == NULL) {
03514                 CALLBACK_SCF(true);
03515                 reg_ah = 0x00;
03516                 /* TODO? Error code? */
03517                 return;
03518             }
03519 
03520             floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz);
03521 
03522             if (enable_fdc_timer_hack) {
03523                 // Hack for Ys II
03524                 FDC_WAIT_TIMER_HACK();
03525             }
03526 
03527             if (reg_ah & 0x10) { // seek to track number in CL
03528                 if (img_cyl != 0 && reg_cl >= img_cyl) {
03529                     CALLBACK_SCF(true);
03530                     reg_ah = 0x00;
03531                     /* TODO? Error code? */
03532                     return;
03533                 }
03534 
03535                 fdc_cyl[drive] = reg_cl;
03536             }
03537 
03538             if (fdc_sect[drive] == 0)
03539                 fdc_sect[drive] = 1;
03540 
03541             if (img_ssz >= 1024)
03542                 fdc_sz[drive] = 3;
03543             else if (img_ssz >= 512)
03544                 fdc_sz[drive] = 2;
03545             else if (img_ssz >= 256)
03546                 fdc_sz[drive] = 1;
03547             else
03548                 fdc_sz[drive] = 0;
03549 
03550             reg_cl = fdc_cyl[drive];
03551             reg_dh = fdc_head[drive];
03552             reg_dl = fdc_sect[drive];
03553             /* ^ FIXME: A more realistic emulation would return a random number from 1 to N
03554              *          where N=sectors/track because the floppy motor is running and tracks
03555              *          are moving past the head. */
03556             reg_ch = fdc_sz[drive];
03557 
03558             /* per read ID call, increment the sector through the range on disk.
03559              * This is REQUIRED or else MEGDOS will not attempt to read this disk. */
03560             if (img_sect != 0) {
03561                 if ((++fdc_sect[drive]) > img_sect)
03562                     fdc_sect[drive] = 1;
03563             }
03564 
03565             reg_ah = 0x00;
03566             CALLBACK_SCF(false);
03567             break;
03568         default:
03569             LOG_MSG("PC-98 INT 1Bh unknown FDC BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03570                     reg_ax,
03571                     reg_bx,
03572                     reg_cx,
03573                     reg_dx,
03574                     reg_si,
03575                     reg_di,
03576                     SegValue(ds),
03577                     SegValue(es));
03578             CALLBACK_SCF(true);
03579             break;
03580     };
03581 }
03582 
03583 static Bitu INT19_PC98_Handler(void) {
03584     LOG_MSG("PC-98 INT 19h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03585         reg_ax,
03586         reg_bx,
03587         reg_cx,
03588         reg_dx,
03589         reg_si,
03590         reg_di,
03591         SegValue(ds),
03592         SegValue(es));
03593 
03594     return CBRET_NONE;
03595 }
03596 
03597 static Bitu INT1A_PC98_Handler(void) {
03598     /* HACK: This makes the "test" program in DOSLIB work.
03599      *       We'll remove this when we implement INT 1Ah */
03600     if (reg_ax == 0x1000) {
03601         CALLBACK_SCF(false);
03602         reg_ax = 0;
03603     }
03604 
03605     LOG_MSG("PC-98 INT 1Ah unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03606         reg_ax,
03607         reg_bx,
03608         reg_cx,
03609         reg_dx,
03610         reg_si,
03611         reg_di,
03612         SegValue(ds),
03613         SegValue(es));
03614 
03615     return CBRET_NONE;
03616 }
03617 
03618 static Bitu INT1B_PC98_Handler(void) {
03619     /* As BIOS interfaces for disk I/O go, this is fairly unusual */
03620     switch (reg_al & 0xF0) {
03621         /* floppy disk access */
03622         /* AL bits[1:0] = floppy drive number */
03623         /* Uses INT42 if high density, INT41 if double density */
03624         /* AH bits[3:0] = command */
03625         case 0x90: /* 1.2MB HD */
03626             PC98_BIOS_FDC_CALL(PC98_FLOPPY_HIGHDENSITY|PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_3MODE);
03627             break;
03628         case 0x30: /* 1.44MB HD (NTS: not supported until the early 1990s) */
03629         case 0xB0:
03630             PC98_BIOS_FDC_CALL(PC98_FLOPPY_HIGHDENSITY|PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_IBMPC);
03631             break;
03632         case 0x70: /* 720KB DD (??) */
03633         case 0xF0:
03634             PC98_BIOS_FDC_CALL(PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_3MODE); // FIXME, correct??
03635             break;
03636         case 0x20: /* SCSI hard disk BIOS */
03637         case 0xA0: /* SCSI hard disk BIOS */
03638         case 0x00: /* SASI hard disk BIOS */
03639         case 0x80: /* SASI hard disk BIOS */
03640             PC98_BIOS_SCSI_CALL();
03641             break;
03642         /* TODO: Other disk formats */
03643         /* TODO: Future SASI/SCSI BIOS emulation for hard disk images */
03644         default:
03645             LOG_MSG("PC-98 INT 1Bh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03646                     reg_ax,
03647                     reg_bx,
03648                     reg_cx,
03649                     reg_dx,
03650                     reg_si,
03651                     reg_di,
03652                     SegValue(ds),
03653                     SegValue(es));
03654             CALLBACK_SCF(true);
03655             break;
03656     };
03657 
03658     return CBRET_NONE;
03659 }
03660 
03661 void PC98_Interval_Timer_Continue(void) {
03662     /* assume: interrupts are disabled */
03663     IO_WriteB(0x71,0x00);
03664     // TODO: What time interval is this supposed to be?
03665     if (PIT_TICK_RATE == PIT_TICK_RATE_PC98_8MHZ)
03666         IO_WriteB(0x71,0x4E);
03667     else
03668         IO_WriteB(0x71,0x60);
03669 
03670     IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/0u))); // unmask IRQ0
03671 }
03672 
03673 unsigned char pc98_dec2bcd(unsigned char c) {
03674     return ((c / 10u) << 4u) + (c % 10u);
03675 }
03676 
03677 static Bitu INT1C_PC98_Handler(void) {
03678     if (reg_ah == 0x00) { /* get time and date */
03679         time_t curtime;
03680         struct tm *loctime;
03681         curtime = time (NULL);
03682         loctime = localtime (&curtime);
03683 
03684         unsigned char tmp[6];
03685 
03686         tmp[0] = pc98_dec2bcd((unsigned int)loctime->tm_year % 100u);
03687         tmp[1] = (((unsigned int)loctime->tm_mon + 1u) << 4u) + (unsigned int)loctime->tm_wday;
03688         tmp[2] = pc98_dec2bcd(loctime->tm_mday);
03689         tmp[3] = pc98_dec2bcd(loctime->tm_hour);
03690         tmp[4] = pc98_dec2bcd(loctime->tm_min);
03691         tmp[5] = pc98_dec2bcd(loctime->tm_sec);
03692 
03693         unsigned long mem = ((unsigned int)SegValue(es) << 4u) + reg_bx;
03694 
03695         for (unsigned int i=0;i < 6;i++)
03696             mem_writeb(mem+i,tmp[i]);
03697     }
03698     else if (reg_ah == 0x02) { /* set interval timer (single event) */
03699         /* es:bx = interrupt handler to execute
03700          * cx = timer interval in ticks (FIXME: what units of time?) */
03701         mem_writew(0x1C,reg_bx);
03702         mem_writew(0x1E,SegValue(es));
03703         mem_writew(0x58A,reg_cx);
03704 
03705         IO_WriteB(0x77,0x36);   /* mode 3, binary, low-byte high-byte 16-bit counter */
03706 
03707         PC98_Interval_Timer_Continue();
03708     }
03709     else if (reg_ah == 0x03) { /* continue interval timer */
03710         PC98_Interval_Timer_Continue();
03711     }
03712     else {
03713         LOG_MSG("PC-98 INT 1Ch unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03714                 reg_ax,
03715                 reg_bx,
03716                 reg_cx,
03717                 reg_dx,
03718                 reg_si,
03719                 reg_di,
03720                 SegValue(ds),
03721                 SegValue(es));
03722     }
03723 
03724     return CBRET_NONE;
03725 }
03726 
03727 static Bitu INT1D_PC98_Handler(void) {
03728     LOG_MSG("PC-98 INT 1Dh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03729         reg_ax,
03730         reg_bx,
03731         reg_cx,
03732         reg_dx,
03733         reg_si,
03734         reg_di,
03735         SegValue(ds),
03736         SegValue(es));
03737 
03738     return CBRET_NONE;
03739 }
03740 
03741 static Bitu INT1E_PC98_Handler(void) {
03742     LOG_MSG("PC-98 INT 1Eh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03743         reg_ax,
03744         reg_bx,
03745         reg_cx,
03746         reg_dx,
03747         reg_si,
03748         reg_di,
03749         SegValue(ds),
03750         SegValue(es));
03751 
03752     return CBRET_NONE;
03753 }
03754 
03755 void PC98_EXTMEMCPY(void) {
03756     bool enabled = MEM_A20_Enabled();
03757     MEM_A20_Enable(true);
03758 
03759     Bitu   bytes    = ((reg_cx - 1u) & 0xFFFFu) + 1u; // bytes, except that 0 == 64KB
03760     PhysPt data     = SegPhys(es)+reg_bx;
03761     PhysPt source   = (mem_readd(data+0x12u) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x17u)<<24u);
03762     PhysPt dest     = (mem_readd(data+0x1Au) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x1Fu)<<24u);
03763 
03764     LOG_MSG("PC-98 memcpy: src=0x%x dst=0x%x data=0x%x count=0x%x",
03765         (unsigned int)source,(unsigned int)dest,(unsigned int)data,(unsigned int)bytes);
03766 
03767     MEM_BlockCopy(dest,source,bytes);
03768     MEM_A20_Enable(enabled);
03769     Segs.limit[cs] = 0xFFFF;
03770     Segs.limit[ds] = 0xFFFF;
03771     Segs.limit[es] = 0xFFFF;
03772     Segs.limit[ss] = 0xFFFF;
03773 
03774     CALLBACK_SCF(false);
03775 }
03776 
03777 static Bitu INT1F_PC98_Handler(void) {
03778     switch (reg_ah) {
03779         case 0x90:
03780             /* Copy extended memory */
03781             PC98_EXTMEMCPY();
03782             break;
03783         default:
03784             LOG_MSG("PC-98 INT 1Fh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03785                     reg_ax,
03786                     reg_bx,
03787                     reg_cx,
03788                     reg_dx,
03789                     reg_si,
03790                     reg_di,
03791                     SegValue(ds),
03792                     SegValue(es));
03793             CALLBACK_SCF(true);
03794             break;
03795     };
03796 
03797     return CBRET_NONE;
03798 }
03799 
03800 static Bitu INTGEN_PC98_Handler(void) {
03801     LOG_MSG("PC-98 INT stub unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03802         reg_ax,
03803         reg_bx,
03804         reg_cx,
03805         reg_dx,
03806         reg_si,
03807         reg_di,
03808         SegValue(ds),
03809         SegValue(es));
03810 
03811     return CBRET_NONE;
03812 }
03813 
03814 /* This interrupt should only exist while the DOS kernel is active.
03815  * On actual PC-98 MS-DOS this is a direct interface to MS-DOS's built-in ANSI CON driver.
03816  *
03817  * CL = major function call number
03818  * AL = minor function call number
03819  * DX = data?? */
03820 extern bool dos_kernel_disabled;
03821 
03822 void PC98_INTDC_WriteChar(unsigned char b);
03823 
03824 static Bitu INTDC_PC98_Handler(void) {
03825     if (dos_kernel_disabled) goto unknown;
03826 
03827     switch (reg_cl) {
03828         case 0x10:
03829             if (reg_ah == 0x00) { /* CL=0x10 AH=0x00 DL=char write char to CON */
03830                 PC98_INTDC_WriteChar(reg_dl);
03831                 goto done;
03832             }
03833             else if (reg_ah == 0x01) { /* CL=0x10 AH=0x01 DS:DX write string to CON */
03834                 /* According to the example at http://tepe.tec.fukuoka-u.ac.jp/HP98/studfile/grth/gt10.pdf
03835                  * the string ends in '$' just like the main DOS string output function. */
03836                 Bit16u ofs = reg_dx;
03837                 do {
03838                     unsigned char c = real_readb(SegValue(ds),ofs++);
03839                     if (c == '$') break;
03840                     PC98_INTDC_WriteChar(c);
03841                 } while (1);
03842                 goto done;
03843             }
03844             else if (reg_ah == 0x02) { /* CL=0x10 AH=0x02 DL=attribute set console output attribute */
03845                 /* Ref: https://nas.jmc/jmcs/docs/browse/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/Undocumented%209801%2c%209821%20Volume%202%20%28webtech.co.jp%29%20English%20translation/memdos%2eenglish%2dgoogle%2dtranslate%2etxt
03846                  *
03847                  * DL is the attribute byte (in the format written directly to video RAM, not the ANSI code) */
03848                 mem_writeb(0x71D,reg_dl);   /* 60:11D */
03849                 goto done;
03850             }
03851             goto unknown;
03852         default: /* some compilers don't like not having a default case */
03853             goto unknown;
03854     };
03855 
03856 done:
03857     return CBRET_NONE;
03858 
03859 unknown:
03860     LOG_MSG("PC-98 INT DCh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03861         reg_ax,
03862         reg_bx,
03863         reg_cx,
03864         reg_dx,
03865         reg_si,
03866         reg_di,
03867         SegValue(ds),
03868         SegValue(es));
03869 
03870     return CBRET_NONE;
03871 }
03872 
03873 static Bitu INTF2_PC98_Handler(void) {
03874     LOG_MSG("PC-98 INT F2h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03875         reg_ax,
03876         reg_bx,
03877         reg_cx,
03878         reg_dx,
03879         reg_si,
03880         reg_di,
03881         SegValue(ds),
03882         SegValue(es));
03883 
03884     return CBRET_NONE;
03885 }
03886 
03887 static Bitu PC98_BIOS_LIO(void) {
03888     /* on entry, AL (from our BIOS code) is set to the call number that lead here */
03889     LOG_MSG("PC-98 BIOS LIO graphics call 0x%02x with AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
03890         reg_al,
03891         reg_ax,
03892         reg_bx,
03893         reg_cx,
03894         reg_dx,
03895         reg_si,
03896         reg_di,
03897         SegValue(ds),
03898         SegValue(es));
03899 
03900     // from Yksoft1's patch
03901     reg_ah = 0;
03902 
03903     return CBRET_NONE;
03904 }
03905 
03906 static Bitu INT11_Handler(void) {
03907     reg_ax=mem_readw(BIOS_CONFIGURATION);
03908     return CBRET_NONE;
03909 }
03910 /* 
03911  * Define the following define to 1 if you want dosbox to check 
03912  * the system time every 5 seconds and adjust 1/2 a second to sync them.
03913  */
03914 #ifndef DOSBOX_CLOCKSYNC
03915 #define DOSBOX_CLOCKSYNC 0
03916 #endif
03917 
03918 static void BIOS_HostTimeSync() {
03919     /* Setup time and date */
03920     struct timeb timebuffer;
03921     ftime(&timebuffer);
03922     
03923     struct tm *loctime;
03924     loctime = localtime (&timebuffer.time);
03925 
03926     /*
03927     loctime->tm_hour = 23;
03928     loctime->tm_min = 59;
03929     loctime->tm_sec = 45;
03930     loctime->tm_mday = 28;
03931     loctime->tm_mon = 2-1;
03932     loctime->tm_year = 2007 - 1900;
03933     */
03934 
03935     dos.date.day=(Bit8u)loctime->tm_mday;
03936     dos.date.month=(Bit8u)loctime->tm_mon+1;
03937     dos.date.year=(Bit16u)loctime->tm_year+1900;
03938 
03939     Bit32u ticks=(Bit32u)(((double)(
03940         loctime->tm_hour*3600*1000+
03941         loctime->tm_min*60*1000+
03942         loctime->tm_sec*1000+
03943         timebuffer.millitm))*(((double)PIT_TICK_RATE/65536.0)/1000.0));
03944     mem_writed(BIOS_TIMER,ticks);
03945 }
03946 
03947 // TODO: make option
03948 bool enable_bios_timer_synchronize_keyboard_leds = true;
03949 
03950 void KEYBOARD_SetLEDs(Bit8u bits);
03951 
03952 void BIOS_KEYBOARD_SetLEDs(Bitu state) {
03953     Bitu x = mem_readb(BIOS_KEYBOARD_LEDS);
03954 
03955     x &= ~7u;
03956     x |= (state & 7u);
03957     mem_writeb(BIOS_KEYBOARD_LEDS,x);
03958     KEYBOARD_SetLEDs(state);
03959 }
03960 
03961 /* PC-98 IRQ 0 system timer */
03962 static Bitu INT8_PC98_Handler(void) {
03963     Bit16u counter = mem_readw(0x58A) - 1;
03964     mem_writew(0x58A,counter);
03965 
03966     /* NTS 2018/02/23: I just confirmed from the ROM BIOS of an actual
03967      *                 PC-98 system that this implementation and Neko Project II
03968      *                 are 100% accurate to what the BIOS actually does.
03969      *                 INT 07h really is the "timer tick" interrupt called
03970      *                 from INT 08h / IRQ 0, and the BIOS really does call
03971      *                 INT 1Ch AH=3 from INT 08h if the tick count has not
03972      *                 yet reached zero.
03973      *
03974      *                 I'm guessing NEC's BIOS developers invented this prior
03975      *                 to the Intel 80286 and it's INT 07h
03976      *                 "Coprocessor not present" exception. */
03977 
03978     if (counter == 0) {
03979         /* mask IRQ 0 */
03980         IO_WriteB(0x02,IO_ReadB(0x02) | 0x01);
03981         /* ack IRQ 0 */
03982         IO_WriteB(0x00,0x20);
03983         /* INT 07h */
03984         CPU_Interrupt(7,CPU_INT_SOFTWARE,reg_eip);
03985     }
03986     else {
03987         /* ack IRQ 0 */
03988         IO_WriteB(0x00,0x20);
03989         /* make sure it continues ticking */
03990         PC98_Interval_Timer_Continue();
03991     }
03992 
03993     return CBRET_NONE;
03994 }
03995 
03996 static Bitu INT8_Handler(void) {
03997     /* Increase the bios tick counter */
03998     Bit32u value = mem_readd(BIOS_TIMER) + 1;
03999     if(value >= 0x1800B0) {
04000         // time wrap at midnight
04001         mem_writeb(BIOS_24_HOURS_FLAG,mem_readb(BIOS_24_HOURS_FLAG)+1);
04002         value=0;
04003     }
04004 
04005     /* Legacy BIOS behavior: This isn't documented at all but most BIOSes
04006        check the BIOS data area for LED keyboard status. If it sees that
04007        value change, then it sends it to the keyboard. This is why on
04008        older DOS machines you could change LEDs by writing to 40:17.
04009        We have to emulate this also because Windows 3.1/9x seems to rely on
04010        it when handling the keyboard from it's own driver. Their driver does
04011        hook the keyboard and handles keyboard I/O by itself, but it still
04012        allows the BIOS to do the keyboard magic from IRQ 0 (INT 8h). Yech. */
04013     if (enable_bios_timer_synchronize_keyboard_leds) {
04014         Bitu should_be = (mem_readb(BIOS_KEYBOARD_STATE) >> 4) & 7;
04015         Bitu led_state = (mem_readb(BIOS_KEYBOARD_LEDS) & 7);
04016 
04017         if (should_be != led_state)
04018             BIOS_KEYBOARD_SetLEDs(should_be);
04019     }
04020 
04021 #if DOSBOX_CLOCKSYNC
04022     static bool check = false;
04023     if((value %50)==0) {
04024         if(((value %100)==0) && check) {
04025             check = false;
04026             time_t curtime;struct tm *loctime;
04027             curtime = time (NULL);loctime = localtime (&curtime);
04028             Bit32u ticksnu = (Bit32u)((loctime->tm_hour*3600+loctime->tm_min*60+loctime->tm_sec)*(float)PIT_TICK_RATE/65536.0);
04029             Bit32s bios = value;Bit32s tn = ticksnu;
04030             Bit32s diff = tn - bios;
04031             if(diff>0) {
04032                 if(diff < 18) { diff  = 0; } else diff = 9;
04033             } else {
04034                 if(diff > -18) { diff = 0; } else diff = -9;
04035             }
04036          
04037             value += diff;
04038         } else if((value%100)==50) check = true;
04039     }
04040 #endif
04041     mem_writed(BIOS_TIMER,value);
04042 
04043     /* decrease floppy motor timer */
04044     Bit8u val = mem_readb(BIOS_DISK_MOTOR_TIMEOUT);
04045     if (val) mem_writeb(BIOS_DISK_MOTOR_TIMEOUT,val-1);
04046     /* and running drive */
04047     mem_writeb(BIOS_DRIVE_RUNNING,mem_readb(BIOS_DRIVE_RUNNING) & 0xF0);
04048     return CBRET_NONE;
04049 }
04050 #undef DOSBOX_CLOCKSYNC
04051 
04052 static Bitu INT1C_Handler(void) {
04053     return CBRET_NONE;
04054 }
04055 
04056 static Bitu INT12_Handler(void) {
04057     reg_ax=mem_readw(BIOS_MEMORY_SIZE);
04058     return CBRET_NONE;
04059 }
04060 
04061 static Bitu INT17_Handler(void) {
04062     if (reg_ah > 0x2 || reg_dx > 0x2) { // 0-2 printer port functions
04063                                         // and no more than 3 parallel ports
04064         LOG_MSG("BIOS INT17: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx);
04065         return CBRET_NONE;
04066     }
04067 
04068     switch(reg_ah) {
04069     case 0x00:      // PRINTER: Write Character
04070         if(parallelPortObjects[reg_dx]!=0) {
04071             if(parallelPortObjects[reg_dx]->Putchar(reg_al))
04072                 reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus();
04073             else reg_ah=1;
04074         }
04075         break;
04076     case 0x01:      // PRINTER: Initialize port
04077         if(parallelPortObjects[reg_dx]!= 0) {
04078             parallelPortObjects[reg_dx]->initialize();
04079             reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus();
04080         }
04081         break;
04082     case 0x02:      // PRINTER: Get Status
04083         if(parallelPortObjects[reg_dx] != 0)
04084             reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus();
04085         //LOG_MSG("printer status: %x",reg_ah);
04086         break;
04087     };
04088     return CBRET_NONE;
04089 }
04090 
04091 static bool INT14_Wait(Bit16u port, Bit8u mask, Bit8u timeout, Bit8u* retval) {
04092     double starttime = PIC_FullIndex();
04093     double timeout_f = timeout * 1000.0;
04094     while (((*retval = IO_ReadB(port)) & mask) != mask) {
04095         if (starttime < (PIC_FullIndex() - timeout_f)) {
04096             return false;
04097         }
04098         CALLBACK_Idle();
04099     }
04100     return true;
04101 }
04102 
04103 static Bitu INT4B_Handler(void) {
04104     /* TODO: This is where the Virtual DMA specification is accessed on modern systems.
04105      *       When we implement that, move this to EMM386 emulation code. */
04106 
04107     if (reg_ax >= 0x8102 && reg_ax <= 0x810D) {
04108         LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted Virtual DMA specification call (INT 4Bh AX=%04x BX=%04x CX=%04x DX=%04x",
04109             reg_ax,reg_bx,reg_cx,reg_dx);
04110     }
04111     else if (reg_ah == 0x80) {
04112         LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted IBM SCSI interface call");
04113     }
04114     else if (reg_ah <= 0x02) {
04115         LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted TI Professional PC parallel port function AH=%02x",reg_ah);
04116     }
04117     else {
04118         LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted unknown INT 4Bh call AX=%04x",reg_ax);
04119     }
04120     
04121     /* Oh, I'm just a BIOS that doesn't know what the hell you're doing. CF=1 */
04122     CALLBACK_SCF(true);
04123     return CBRET_NONE;
04124 }
04125 
04126 static Bitu INT14_Handler(void) {
04127     if (reg_ah > 0x3 || reg_dx > 0x3) { // 0-3 serial port functions
04128                                         // and no more than 4 serial ports
04129         LOG_MSG("BIOS INT14: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx);
04130         return CBRET_NONE;
04131     }
04132     
04133     Bit16u port = real_readw(0x40,reg_dx * 2u); // DX is always port number
04134     Bit8u timeout = mem_readb((PhysPt)((unsigned int)BIOS_COM1_TIMEOUT + (unsigned int)reg_dx));
04135     if (port==0)    {
04136         LOG(LOG_BIOS,LOG_NORMAL)("BIOS INT14: port %d does not exist.",reg_dx);
04137         return CBRET_NONE;
04138     }
04139     switch (reg_ah) {
04140     case 0x00:  {
04141         // Initialize port
04142         // Parameters:              Return:
04143         // AL: port parameters      AL: modem status
04144         //                          AH: line status
04145 
04146         // set baud rate
04147         Bitu baudrate = 9600u;
04148         Bit16u baudresult;
04149         Bitu rawbaud=(Bitu)reg_al>>5u;
04150         
04151         if (rawbaud==0){ baudrate=110u;}
04152         else if (rawbaud==1){ baudrate=150u;}
04153         else if (rawbaud==2){ baudrate=300u;}
04154         else if (rawbaud==3){ baudrate=600u;}
04155         else if (rawbaud==4){ baudrate=1200u;}
04156         else if (rawbaud==5){ baudrate=2400u;}
04157         else if (rawbaud==6){ baudrate=4800u;}
04158         else if (rawbaud==7){ baudrate=9600u;}
04159 
04160         baudresult = (Bit16u)(115200u / baudrate);
04161 
04162         IO_WriteB(port+3u, 0x80u);    // enable divider access
04163         IO_WriteB(port, (Bit8u)baudresult&0xffu);
04164         IO_WriteB(port+1u, (Bit8u)(baudresult>>8u));
04165 
04166         // set line parameters, disable divider access
04167         IO_WriteB(port+3u, reg_al&0x1Fu); // LCR
04168         
04169         // disable interrupts
04170         IO_WriteB(port+1u, 0u); // IER
04171 
04172         // get result
04173         reg_ah=(Bit8u)(IO_ReadB(port+5u)&0xffu);
04174         reg_al=(Bit8u)(IO_ReadB(port+6u)&0xffu);
04175         CALLBACK_SCF(false);
04176         break;
04177     }
04178     case 0x01: // Transmit character
04179         // Parameters:              Return:
04180         // AL: character            AL: unchanged
04181         // AH: 0x01                 AH: line status from just before the char was sent
04182         //                              (0x80 | unpredicted) in case of timeout
04183         //                      [undoc] (0x80 | line status) in case of tx timeout
04184         //                      [undoc] (0x80 | modem status) in case of dsr/cts timeout
04185 
04186         // set DTR & RTS on
04187         IO_WriteB(port+4u,0x3u);
04188         // wait for DSR & CTS
04189         if (INT14_Wait(port+6u, 0x30u, timeout, &reg_ah)) {
04190             // wait for TX buffer empty
04191             if (INT14_Wait(port+5u, 0x20u, timeout, &reg_ah)) {
04192                 // fianlly send the character
04193                 IO_WriteB(port,reg_al);
04194             } else
04195                 reg_ah |= 0x80u;
04196         } else // timed out
04197             reg_ah |= 0x80u;
04198 
04199         CALLBACK_SCF(false);
04200         break;
04201     case 0x02: // Read character
04202         // Parameters:              Return:
04203         // AH: 0x02                 AL: received character
04204         //                      [undoc] will be trashed in case of timeout
04205         //                          AH: (line status & 0x1E) in case of success
04206         //                              (0x80 | unpredicted) in case of timeout
04207         //                      [undoc] (0x80 | line status) in case of rx timeout
04208         //                      [undoc] (0x80 | modem status) in case of dsr timeout
04209 
04210         // set DTR on
04211         IO_WriteB(port+4u,0x1u);
04212 
04213         // wait for DSR
04214         if (INT14_Wait(port+6, 0x20, timeout, &reg_ah)) {
04215             // wait for character to arrive
04216             if (INT14_Wait(port+5, 0x01, timeout, &reg_ah)) {
04217                 reg_ah &= 0x1E;
04218                 reg_al = IO_ReadB(port);
04219             } else
04220                 reg_ah |= 0x80;
04221         } else
04222             reg_ah |= 0x80;
04223 
04224         CALLBACK_SCF(false);
04225         break;
04226     case 0x03: // get status
04227         reg_ah=(Bit8u)(IO_ReadB(port+5u)&0xffu);
04228         reg_al=(Bit8u)(IO_ReadB(port+6u)&0xffu);
04229         CALLBACK_SCF(false);
04230         break;
04231 
04232     }
04233     return CBRET_NONE;
04234 }
04235 
04236 Bits HLT_Decode(void);
04237 void KEYBOARD_AUX_Write(Bitu val);
04238 unsigned char KEYBOARD_AUX_GetType();
04239 unsigned char KEYBOARD_AUX_DevStatus();
04240 unsigned char KEYBOARD_AUX_Resolution();
04241 unsigned char KEYBOARD_AUX_SampleRate();
04242 void KEYBOARD_ClrBuffer(void);
04243 
04244 static Bitu INT15_Handler(void) {
04245     if( ( machine==MCH_AMSTRAD ) && ( reg_ah<0x07 ) ) {
04246         switch(reg_ah) {
04247             case 0x00:
04248                 // Read/Reset Mouse X/Y Counts.
04249                 // CX = Signed X Count.
04250                 // DX = Signed Y Count.
04251                 // CC.
04252             case 0x01:
04253                 // Write NVR Location.
04254                 // AL = NVR Address to be written (0-63).
04255                 // BL = NVR Data to be written.
04256 
04257                 // AH = Return Code.
04258                 // 00 = NVR Written Successfully.
04259                 // 01 = NVR Address out of range.
04260                 // 02 = NVR Data write error.
04261                 // CC.
04262             case 0x02:
04263                 // Read NVR Location.
04264                 // AL = NVR Address to be read (0-63).
04265 
04266                 // AH = Return Code.
04267                 // 00 = NVR read successfully.
04268                 // 01 = NVR Address out of range.
04269                 // 02 = NVR checksum error.
04270                 // AL = Byte read from NVR.
04271                 // CC.
04272             default:
04273                 LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unsupported PC1512 Call %02X",reg_ah);
04274                 return CBRET_NONE;
04275             case 0x03:
04276                 // Write VDU Colour Plane Write Register.
04277                 vga.amstrad.write_plane = reg_al & 0x0F;
04278                 CALLBACK_SCF(false);
04279                 break;
04280             case 0x04:
04281                 // Write VDU Colour Plane Read Register.
04282                 vga.amstrad.read_plane = reg_al & 0x03;
04283                 CALLBACK_SCF(false);
04284                 break;
04285             case 0x05:
04286                 // Write VDU Graphics Border Register.
04287                 vga.amstrad.border_color = reg_al & 0x0F;
04288                 CALLBACK_SCF(false);
04289                 break;
04290             case 0x06:
04291                 // Return ROS Version Number.
04292                 reg_bx = 0x0001;
04293                 CALLBACK_SCF(false);
04294                 break;
04295         }
04296     }
04297     switch (reg_ah) {
04298     case 0x06:
04299         LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unkown Function 6 (Amstrad?)");
04300         break;
04301     case 0xC0:  /* Get Configuration*/
04302         CPU_SetSegGeneral(es,biosConfigSeg);
04303         reg_bx = 0;
04304         reg_ah = 0;
04305         CALLBACK_SCF(false);
04306         break;
04307     case 0x4f:  /* BIOS - Keyboard intercept */
04308         /* Carry should be set but let's just set it just in case */
04309         CALLBACK_SCF(true);
04310         break;
04311     case 0x83:  /* BIOS - SET EVENT WAIT INTERVAL */
04312         {
04313             if(reg_al == 0x01) { /* Cancel it */
04314                 mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0);
04315                 IO_Write(0x70,0xb);
04316                 IO_Write(0x71,IO_Read(0x71)&~0x40);
04317                 CALLBACK_SCF(false);
04318                 break;
04319             }
04320             if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) {
04321                 reg_ah=0x80;
04322                 CALLBACK_SCF(true);
04323                 break;
04324             }
04325             Bit32u count=((Bit32u)reg_cx<<16u)|reg_dx;
04326             mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(SegValue(es),reg_bx));
04327             mem_writed(BIOS_WAIT_FLAG_COUNT,count);
04328             mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1);
04329             /* Reprogram RTC to start */
04330             IO_Write(0x70,0xb);
04331             IO_Write(0x71,IO_Read(0x71)|0x40);
04332             CALLBACK_SCF(false);
04333         }
04334         break;
04335     case 0x84:  /* BIOS - JOYSTICK SUPPORT (XT after 11/8/82,AT,XT286,PS) */
04336         if (reg_dx == 0x0000) {
04337             // Get Joystick button status
04338             if (JOYSTICK_IsEnabled(0) || JOYSTICK_IsEnabled(1)) {
04339                 reg_al = IO_ReadB(0x201)&0xf0;
04340                 CALLBACK_SCF(false);
04341             } else {
04342                 // dos values
04343                 reg_ax = 0x00f0; reg_dx = 0x0201;
04344                 CALLBACK_SCF(true);
04345             }
04346         } else if (reg_dx == 0x0001) {
04347             if (JOYSTICK_IsEnabled(0)) {
04348                 reg_ax = (Bit16u)(JOYSTICK_GetMove_X(0)*127+128);
04349                 reg_bx = (Bit16u)(JOYSTICK_GetMove_Y(0)*127+128);
04350                 if(JOYSTICK_IsEnabled(1)) {
04351                     reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128);
04352                     reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128);
04353                 }
04354                 else {
04355                     reg_cx = reg_dx = 0;
04356                 }
04357                 CALLBACK_SCF(false);
04358             } else if (JOYSTICK_IsEnabled(1)) {
04359                 reg_ax = reg_bx = 0;
04360                 reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128);
04361                 reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128);
04362                 CALLBACK_SCF(false);
04363             } else {            
04364                 reg_ax = reg_bx = reg_cx = reg_dx = 0;
04365                 CALLBACK_SCF(true);
04366             }
04367         } else {
04368             LOG(LOG_BIOS,LOG_ERROR)("INT15:84:Unknown Bios Joystick functionality.");
04369         }
04370         break;
04371     case 0x86:  /* BIOS - WAIT (AT,PS) */
04372         {
04373             if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) {
04374                 reg_ah=0x83;
04375                 CALLBACK_SCF(true);
04376                 break;
04377             }
04378             Bit8u t;
04379             Bit32u count=((Bit32u)reg_cx<<16u)|reg_dx;
04380             mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP));
04381             mem_writed(BIOS_WAIT_FLAG_COUNT,count);
04382             mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1);
04383 
04384             /* if the user has not set the option, warn if IRQs are masked */
04385             if (!int15_wait_force_unmask_irq) {
04386                 /* make sure our wait function works by unmasking IRQ 2, and IRQ 8.
04387                  * (bugfix for 1993 demo Yodel "Mayday" demo. this demo keeps masking IRQ 2 for some stupid reason.) */
04388                 if ((t=IO_Read(0x21)) & (1 << 2)) {
04389                     LOG(LOG_BIOS,LOG_ERROR)("INT15:86:Wait: IRQ 2 masked during wait. "
04390                         "Consider adding 'int15 wait force unmask irq=true' to your dosbox.conf");
04391                 }
04392                 if ((t=IO_Read(0xA1)) & (1 << 0)) {
04393                     LOG(LOG_BIOS,LOG_ERROR)("INT15:86:Wait: IRQ 8 masked during wait. "
04394                         "Consider adding 'int15 wait force unmask irq=true' to your dosbox.conf");
04395                 }
04396             }
04397 
04398             /* Reprogram RTC to start */
04399             IO_Write(0x70,0xb);
04400             IO_Write(0x71,IO_Read(0x71)|0x40);
04401             while (mem_readd(BIOS_WAIT_FLAG_COUNT)) {
04402                 if (int15_wait_force_unmask_irq) {
04403                     /* make sure our wait function works by unmasking IRQ 2, and IRQ 8.
04404                      * (bugfix for 1993 demo Yodel "Mayday" demo. this demo keeps masking IRQ 2 for some stupid reason.) */
04405                     if ((t=IO_Read(0x21)) & (1 << 2)) {
04406                         LOG(LOG_BIOS,LOG_WARN)("INT15:86:Wait: IRQ 2 masked during wait. "
04407                             "This condition might result in an infinite wait on "
04408                             "some BIOSes. Unmasking IRQ to keep things moving along.");
04409                         IO_Write(0x21,t & ~(1 << 2));
04410 
04411                     }
04412                     if ((t=IO_Read(0xA1)) & (1 << 0)) {
04413                         LOG(LOG_BIOS,LOG_WARN)("INT15:86:Wait: IRQ 8 masked during wait. "
04414                             "This condition might result in an infinite wait on some "
04415                             "BIOSes. Unmasking IRQ to keep things moving along.");
04416                         IO_Write(0xA1,t & ~(1 << 0));
04417                     }
04418                 }
04419 
04420                 CALLBACK_Idle();
04421             }
04422             CALLBACK_SCF(false);
04423             break;
04424         }
04425     case 0x87:  /* Copy extended memory */
04426         {
04427             bool enabled = MEM_A20_Enabled();
04428             MEM_A20_Enable(true);
04429             Bitu   bytes    = reg_cx * 2u;
04430             PhysPt data     = SegPhys(es)+reg_si;
04431             PhysPt source   = (mem_readd(data+0x12u) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x17u)<<24u);
04432             PhysPt dest     = (mem_readd(data+0x1Au) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x1Fu)<<24u);
04433             MEM_BlockCopy(dest,source,bytes);
04434             reg_ax = 0x00;
04435             MEM_A20_Enable(enabled);
04436             Segs.limit[cs] = 0xFFFF;
04437             Segs.limit[ds] = 0xFFFF;
04438             Segs.limit[es] = 0xFFFF;
04439             Segs.limit[ss] = 0xFFFF;
04440             CALLBACK_SCF(false);
04441             break;
04442         }   
04443     case 0x88:  /* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */
04444         /* This uses the 16-bit value read back from CMOS which is capped at 64MB */
04445         reg_ax=other_memsystems?0:size_extended;
04446         LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function 0x88 Remaining %04X kb",reg_ax);
04447         CALLBACK_SCF(false);
04448         break;
04449     case 0x89:  /* SYSTEM - SWITCH TO PROTECTED MODE */
04450         {
04451             IO_Write(0x20,0x10);IO_Write(0x21,reg_bh);IO_Write(0x21,0);IO_Write(0x21,0xFF);
04452             IO_Write(0xA0,0x10);IO_Write(0xA1,reg_bl);IO_Write(0xA1,0);IO_Write(0xA1,0xFF);
04453             MEM_A20_Enable(true);
04454             PhysPt table=SegPhys(es)+reg_si;
04455             CPU_LGDT(mem_readw(table+0x8),mem_readd(table+0x8+0x2) & 0xFFFFFF);
04456             CPU_LIDT(mem_readw(table+0x10),mem_readd(table+0x10+0x2) & 0xFFFFFF);
04457             CPU_SET_CRX(0,CPU_GET_CRX(0)|1);
04458             CPU_SetSegGeneral(ds,0x18);
04459             CPU_SetSegGeneral(es,0x20);
04460             CPU_SetSegGeneral(ss,0x28);
04461             Bitu ret = mem_readw(SegPhys(ss)+reg_sp);
04462             reg_sp+=6;          //Clear stack of interrupt frame
04463             CPU_SetFlags(0,FMASK_ALL);
04464             reg_ax=0;
04465             CPU_JMP(false,0x30,ret,0);
04466         }
04467         break;
04468     case 0x8A:  /* EXTENDED MEMORY SIZE */
04469         {
04470             Bitu sz = MEM_TotalPages()*4;
04471             if (sz >= 1024) sz -= 1024;
04472             else sz = 0;
04473             reg_ax = sz & 0xFFFF;
04474             reg_dx = sz >> 16;
04475             CALLBACK_SCF(false);
04476         }
04477         break;
04478     case 0x90:  /* OS HOOK - DEVICE BUSY */
04479         CALLBACK_SCF(false);
04480         reg_ah=0;
04481         break;
04482     case 0x91:  /* OS HOOK - DEVICE POST */
04483         CALLBACK_SCF(false);
04484         reg_ah=0;
04485         break;
04486     case 0xc2:  /* BIOS PS2 Pointing Device Support */
04487             /* TODO: Our reliance on AUX emulation means that at some point, AUX emulation
04488              *       must always be enabled if BIOS PS/2 emulation is enabled. Future planned change:
04489              *
04490              *       If biosps2=true and aux=true, carry on what we're already doing now: emulate INT 15h by
04491              *         directly writing to the AUX port of the keyboard controller.
04492              *
04493              *       If biosps2=false, the aux= setting enables/disables AUX emulation as it already does now.
04494              *         biosps2=false implies that we're emulating a keyboard controller with AUX but no BIOS
04495              *         support for it (however rare that might be). This gives the user of DOSBox-X the means
04496              *         to test that scenario especially in case he/she is developing a homebrew OS and needs
04497              *         to ensure their code can handle cases like that gracefully.
04498              *
04499              *       If biosps2=true and aux=false, AUX emulation is enabled anyway, but the keyboard emulation
04500              *         must act as if the AUX port is not there so the guest OS cannot control it. Again, not
04501              *         likely on real hardware, but a useful test case for homebrew OS developers.
04502              *
04503              *       If you the user set aux=false, then you obviously want to test a system configuration
04504              *       where the keyboard controller has no AUX port. If you set biosps2=true, then you want to
04505              *       test an OS that uses BIOS functions to setup the "pointing device" but you do not want the
04506              *       guest OS to talk directly to the AUX port on the keyboard controller.
04507              *
04508              *       Logically that's not likely to happen on real hardware, but we like giving the end-user
04509              *       options to play with, so instead, if aux=false and biosps2=true, DOSBox-X should print
04510              *       a warning stating that INT 15h mouse emulation with a PS/2 port is nonstandard and may
04511              *       cause problems with OSes that need to talk directly to hardware.
04512              *
04513              *       It is noteworthy that PS/2 mouse support in MS-DOS mouse drivers as well as Windows 3.x,
04514              *       Windows 95, and Windows 98, is carried out NOT by talking directly to the AUX port but
04515              *       instead by relying on the BIOS INT 15h functions here to do the dirty work. For those
04516              *       scenarios, biosps2=true and aux=false is perfectly safe and does not cause issues.
04517              *
04518              *       OSes that communicate directly with the AUX port however (Linux, Windows NT) will not work
04519              *       unless aux=true. */
04520         if (en_bios_ps2mouse) {
04521 //          LOG_MSG("INT 15h AX=%04x BX=%04x\n",reg_ax,reg_bx);
04522             switch (reg_al) {
04523                 case 0x00:      // enable/disable
04524                     if (reg_bh==0) {    // disable
04525                         KEYBOARD_AUX_Write(0xF5);
04526                         Mouse_SetPS2State(false);
04527                         reg_ah=0;
04528                         CALLBACK_SCF(false);
04529                         KEYBOARD_ClrBuffer();
04530                     } else if (reg_bh==0x01) {  //enable
04531                         if (!Mouse_SetPS2State(true)) {
04532                             reg_ah=5;
04533                             CALLBACK_SCF(true);
04534                             break;
04535                         }
04536                         KEYBOARD_AUX_Write(0xF4);
04537                         KEYBOARD_ClrBuffer();
04538                         reg_ah=0;
04539                         CALLBACK_SCF(false);
04540                     } else {
04541                         CALLBACK_SCF(true);
04542                         reg_ah=1;
04543                     }
04544                     break;
04545                 case 0x01:      // reset
04546                     KEYBOARD_AUX_Write(0xFF);
04547                     Mouse_SetPS2State(false);
04548                     KEYBOARD_ClrBuffer();
04549                     reg_bx=0x00aa;  // mouse
04550                     // fall through
04551                 case 0x05:      // initialize
04552                     if (reg_bh >= 3 && reg_bh <= 4) {
04553                         /* TODO: BIOSes remember this value as the number of bytes to store before
04554                          *       calling the device callback. Setting this value to "1" is perfectly
04555                          *       valid if you want a byte-stream like mode (at the cost of one
04556                          *       interrupt per byte!). Most OSes will call this with BH=3 for standard
04557                          *       PS/2 mouse protocol. You can also call this with BH=4 for Intellimouse
04558                          *       protocol support, though testing (so far with VirtualBox) shows the
04559                          *       device callback still only gets the first three bytes on the stack.
04560                          *       Contrary to what you might think, the BIOS does not interpret the
04561                          *       bytes at all.
04562                          *
04563                          *       The source code of CuteMouse 1.9 seems to suggest some BIOSes take
04564                          *       pains to repack the 4th byte in the upper 8 bits of one of the WORDs
04565                          *       on the stack in Intellimouse mode at the cost of shifting the W and X
04566                          *       fields around. I can't seem to find any source on who does that or
04567                          *       if it's even true, so I disregard the example at this time.
04568                          *
04569                          *       Anyway, you need to store off this value somewhere and make use of
04570                          *       it in src/ints/mouse.cpp device callback emulation to reframe the
04571                          *       PS/2 mouse bytes coming from AUX (if aux=true) or emulate the
04572                          *       re-framing if aux=false to emulate this protocol fully. */
04573                         LOG_MSG("INT 15h mouse initialized to %u-byte protocol\n",reg_bh);
04574                         KEYBOARD_AUX_Write(0xF6); /* set defaults */
04575                         Mouse_SetPS2State(false);
04576                         KEYBOARD_ClrBuffer();
04577                         CALLBACK_SCF(false);
04578                         reg_ah=0;
04579                     }
04580                     else {
04581                         CALLBACK_SCF(false);
04582                         reg_ah=0x02; /* invalid input */
04583                     }
04584                     break;
04585                 case 0x02: {        // set sampling rate
04586                     static const unsigned char tbl[7] = {10,20,40,60,80,100,200};
04587                     KEYBOARD_AUX_Write(0xF3);
04588                     if (reg_bl > 6) reg_bl = 6;
04589                     KEYBOARD_AUX_Write(tbl[reg_bh]);
04590                     KEYBOARD_ClrBuffer();
04591                     CALLBACK_SCF(false);
04592                     reg_ah=0;
04593                     } break;
04594                 case 0x03:      // set resolution
04595                     KEYBOARD_AUX_Write(0xE8);
04596                     KEYBOARD_AUX_Write(reg_bh&3);
04597                     KEYBOARD_ClrBuffer();
04598                     CALLBACK_SCF(false);
04599                     reg_ah=0;
04600                     break;
04601                 case 0x04:      // get type
04602                     reg_bh=KEYBOARD_AUX_GetType();  // ID
04603                     LOG_MSG("INT 15h reporting mouse device ID 0x%02x\n",reg_bh);
04604                     KEYBOARD_ClrBuffer();
04605                     CALLBACK_SCF(false);
04606                     reg_ah=0;
04607                     break;
04608                 case 0x06:      // extended commands
04609                     if (reg_bh == 0x00) {
04610                         /* Read device status and info.
04611                          * Windows 9x does not appear to use this, but Windows NT 3.1 does (prior to entering
04612                          * full 32-bit protected mode) */
04613                         CALLBACK_SCF(false);
04614                         reg_bx=KEYBOARD_AUX_DevStatus();
04615                         reg_cx=KEYBOARD_AUX_Resolution();
04616                         reg_dx=KEYBOARD_AUX_SampleRate();
04617                         KEYBOARD_ClrBuffer();
04618                         reg_ah=0;
04619                     }
04620                     else if ((reg_bh==0x01) || (reg_bh==0x02)) { /* set scaling */
04621                         KEYBOARD_AUX_Write(0xE6u+reg_bh-1u); /* 0xE6 1:1   or 0xE7 2:1 */
04622                         KEYBOARD_ClrBuffer();
04623                         CALLBACK_SCF(false); 
04624                         reg_ah=0;
04625                     } else {
04626                         CALLBACK_SCF(true);
04627                         reg_ah=1;
04628                     }
04629                     break;
04630                 case 0x07:      // set callback
04631                     Mouse_ChangePS2Callback(SegValue(es),reg_bx);
04632                     CALLBACK_SCF(false);
04633                     reg_ah=0;
04634                     break;
04635                 default:
04636                     LOG_MSG("INT 15h unknown mouse call AX=%04x\n",reg_ax);
04637                     CALLBACK_SCF(true);
04638                     reg_ah=1;
04639                     break;
04640             }
04641         }
04642         else {
04643             reg_ah=0x86;
04644             CALLBACK_SCF(true);
04645             if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA)) {
04646                 /* relict from comparisons, as int15 exits with a retf2 instead of an iret */
04647                 CALLBACK_SZF(false);
04648             }
04649         }
04650         break;
04651     case 0xc3:      /* set carry flag so BorlandRTM doesn't assume a VECTRA/PS2 */
04652         reg_ah=0x86;
04653         CALLBACK_SCF(true);
04654         break;
04655     case 0xc4:  /* BIOS POS Programm option Select */
04656         LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function %X called, bios mouse not supported",reg_ah);
04657         CALLBACK_SCF(true);
04658         break;
04659     case 0x53: // APM BIOS
04660         if (APMBIOS) {
04661             LOG(LOG_BIOS,LOG_DEBUG)("APM BIOS call AX=%04x BX=0x%04x CX=0x%04x\n",reg_ax,reg_bx,reg_cx);
04662             switch(reg_al) {
04663                 case 0x00: // installation check
04664                     reg_ah = 1;             // major
04665                     reg_al = APM_BIOS_minor_version;    // minor
04666                     reg_bx = 0x504d;            // 'PM'
04667                     reg_cx = (APMBIOS_allow_prot16?0x01:0x00) + (APMBIOS_allow_prot32?0x02:0x00);
04668                     // 32-bit interface seems to be needed for standby in win95
04669                     CALLBACK_SCF(false);
04670                     break;
04671                 case 0x01: // connect real mode interface
04672                     if(!APMBIOS_allow_realmode) {
04673                         LOG_MSG("APM BIOS: OS attemped real-mode connection, which is disabled in your dosbox.conf\n");
04674                         reg_ah = 0x86;  // APM not present
04675                         CALLBACK_SCF(true);         
04676                         break;
04677                     }
04678                     if(reg_bx != 0x0) {
04679                         reg_ah = 0x09;  // unrecognized device ID
04680                         CALLBACK_SCF(true);         
04681                         break;
04682                     }
04683                     if(!apm_realmode_connected) { // not yet connected
04684                         LOG_MSG("APM BIOS: Connected to real-mode interface\n");
04685                         CALLBACK_SCF(false);
04686                         APMBIOS_connect_mode = APMBIOS_CONNECT_REAL;
04687                         apm_realmode_connected=true;
04688                     } else {
04689                         LOG_MSG("APM BIOS: OS attempted to connect to real-mode interface when already connected\n");
04690                         reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect
04691                         CALLBACK_SCF(true);         
04692                     }
04693                     APM_BIOS_connected_minor_version = 0;
04694                     break;
04695                 case 0x02: // connect 16-bit protected mode interface
04696                     if(!APMBIOS_allow_prot16) {
04697                         LOG_MSG("APM BIOS: OS attemped 16-bit protected mode connection, which is disabled in your dosbox.conf\n");
04698                         reg_ah = 0x06;  // not supported
04699                         CALLBACK_SCF(true);         
04700                         break;
04701                     }
04702                     if(reg_bx != 0x0) {
04703                         reg_ah = 0x09;  // unrecognized device ID
04704                         CALLBACK_SCF(true);         
04705                         break;
04706                     }
04707                     if(!apm_realmode_connected) { // not yet connected
04708                         /* NTS: We use the same callback address for both 16-bit and 32-bit
04709                          *      because only the DOS callback and RETF instructions are involved,
04710                          *      which can be executed as either 16-bit or 32-bit code without problems. */
04711                         LOG_MSG("APM BIOS: Connected to 16-bit protected mode interface\n");
04712                         CALLBACK_SCF(false);
04713                         reg_ax = INT15_apm_pmentry >> 16;   // AX = 16-bit code segment (real mode base)
04714                         reg_bx = INT15_apm_pmentry & 0xFFFF;    // BX = offset of entry point
04715                         reg_cx = INT15_apm_pmentry >> 16;   // CX = 16-bit data segment (NTS: doesn't really matter)
04716                         reg_si = 0xFFFF;            // SI = code segment length
04717                         reg_di = 0xFFFF;            // DI = data segment length
04718                         APMBIOS_connect_mode = APMBIOS_CONNECT_PROT16;
04719                         apm_realmode_connected=true;
04720                     } else {
04721                         LOG_MSG("APM BIOS: OS attempted to connect to 16-bit protected mode interface when already connected\n");
04722                         reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect
04723                         CALLBACK_SCF(true);         
04724                     }
04725                     APM_BIOS_connected_minor_version = 0;
04726                     break;
04727                 case 0x03: // connect 32-bit protected mode interface
04728                     // Note that Windows 98 will NOT talk to the APM BIOS unless the 32-bit protected mode connection is available.
04729                     // And if you lie about it in function 0x00 and then fail, Windows 98 will fail with a "Windows protection error".
04730                     if(!APMBIOS_allow_prot32) {
04731                         LOG_MSG("APM BIOS: OS attemped 32-bit protected mode connection, which is disabled in your dosbox.conf\n");
04732                         reg_ah = 0x08;  // not supported
04733                         CALLBACK_SCF(true);         
04734                         break;
04735                     }
04736                     if(reg_bx != 0x0) {
04737                         reg_ah = 0x09;  // unrecognized device ID
04738                         CALLBACK_SCF(true);         
04739                         break;
04740                     }
04741                     if(!apm_realmode_connected) { // not yet connected
04742                         LOG_MSG("APM BIOS: Connected to 32-bit protected mode interface\n");
04743                         CALLBACK_SCF(false);
04744                         /* NTS: We use the same callback address for both 16-bit and 32-bit
04745                          *      because only the DOS callback and RETF instructions are involved,
04746                          *      which can be executed as either 16-bit or 32-bit code without problems. */
04747                         reg_ax = INT15_apm_pmentry >> 16;   // AX = 32-bit code segment (real mode base)
04748                         reg_ebx = INT15_apm_pmentry & 0xFFFF;   // EBX = offset of entry point
04749                         reg_cx = INT15_apm_pmentry >> 16;   // CX = 16-bit code segment (real mode base)
04750                         reg_dx = INT15_apm_pmentry >> 16;   // DX = data segment (real mode base) (?? what size?)
04751                         reg_esi = 0xFFFFFFFF;           // ESI = upper word: 16-bit code segment len  lower word: 32-bit code segment length
04752                         reg_di = 0xFFFF;            // DI = data segment length
04753                         APMBIOS_connect_mode = APMBIOS_CONNECT_PROT32;
04754                         apm_realmode_connected=true;
04755                     } else {
04756                         LOG_MSG("APM BIOS: OS attempted to connect to 32-bit protected mode interface when already connected\n");
04757                         reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect
04758                         CALLBACK_SCF(true);         
04759                     }
04760                     APM_BIOS_connected_minor_version = 0;
04761                     break;
04762                 case 0x04: // DISCONNECT INTERFACE
04763                     if(reg_bx != 0x0) {
04764                         reg_ah = 0x09;  // unrecognized device ID
04765                         CALLBACK_SCF(true);         
04766                         break;
04767                     }
04768                     if(apm_realmode_connected) {
04769                         LOG_MSG("APM BIOS: OS disconnected\n");
04770                         CALLBACK_SCF(false);
04771                         apm_realmode_connected=false;
04772                     } else {
04773                         reg_ah = 0x03;  // interface not connected
04774                         CALLBACK_SCF(true);         
04775                     }
04776                     APM_BIOS_connected_minor_version = 0;
04777                     break;
04778                 case 0x05: // CPU IDLE
04779                     if(!apm_realmode_connected) {
04780                         reg_ah = 0x03;
04781                         CALLBACK_SCF(true);
04782                         break;
04783                     }
04784 
04785                     // Trigger CPU HLT instruction.
04786                     // NTS: For whatever weird reason, NOT emulating HLT makes Windows 95
04787                     //      crashy when the APM driver is active! There's something within
04788                     //      the Win95 kernel that apparently screws up really badly if
04789                     //      the APM IDLE call returns immediately. The best case scenario
04790                     //      seems to be that Win95's APM driver has some sort of timing
04791                     //      logic to it that if it detects an immediate return, immediately
04792                     //      shuts down and powers off the machine. Windows 98 also seems
04793                     //      to require a HLT, and will act erratically without it.
04794                     //
04795                     //      Also need to note that the choice of "HLT" is not arbitrary
04796                     //      at all. The APM BIOS standard mentions CPU IDLE either stopping
04797                     //      the CPU clock temporarily or issuing HLT as a valid method.
04798                     //
04799                     // TODO: Make this a dosbox.conf configuration option: what do we do
04800                     //       on APM idle calls? Allow selection between "nothing" "hlt"
04801                     //       and "software delay".
04802                     if (!(reg_flags&0x200)) {
04803                         LOG_MSG("APM BIOS warning: CPU IDLE called with IF=0, not HLTing\n");
04804                     }
04805                     else if (cpudecoder == &HLT_Decode) { /* do not re-execute HLT, it makes DOSBox hang */
04806                         LOG_MSG("APM BIOS warning: CPU IDLE HLT within HLT (DOSBox core failure)\n");
04807                     }
04808                     else {
04809                         CPU_HLT(reg_eip);
04810                     }
04811                     break;
04812                 case 0x06: // CPU BUSY
04813                     if(!apm_realmode_connected) {
04814                         reg_ah = 0x03;
04815                         CALLBACK_SCF(true);
04816                         break;
04817                     }
04818 
04819                     /* OK. whatever. system no longer idle */
04820                     CALLBACK_SCF(false);
04821                     break;
04822                 case 0x07:
04823                     if(reg_bx != 0x1) {
04824                         reg_ah = 0x09;  // wrong device ID
04825                         CALLBACK_SCF(true);         
04826                         break;
04827                     }
04828                     if(!apm_realmode_connected) {
04829                         reg_ah = 0x03;
04830                         CALLBACK_SCF(true);
04831                         break;
04832                     }
04833                     switch(reg_cx) {
04834                         case 0x3: // power off
04835                             throw(0);
04836                             break;
04837                         default:
04838                             reg_ah = 0x0A; // invalid parameter value in CX
04839                             CALLBACK_SCF(true);
04840                             break;
04841                     }
04842                     break;
04843                 case 0x08: // ENABLE/DISABLE POWER MANAGEMENT
04844                     if(reg_bx != 0x0 && reg_bx != 0x1) {
04845                         reg_ah = 0x09;  // unrecognized device ID
04846                         CALLBACK_SCF(true);         
04847                         break;
04848                     } else if(!apm_realmode_connected) {
04849                         reg_ah = 0x03;
04850                         CALLBACK_SCF(true);
04851                         break;
04852                     }
04853                     if(reg_cx==0x0) LOG_MSG("disable APM for device %4x",reg_bx);
04854                     else if(reg_cx==0x1) LOG_MSG("enable APM for device %4x",reg_bx);
04855                     else {
04856                         reg_ah = 0x0A; // invalid parameter value in CX
04857                         CALLBACK_SCF(true);
04858                     }
04859                     break;
04860                 case 0x0a: // GET POWER STATUS
04861                     if (!apm_realmode_connected) {
04862                         reg_ah = 0x03;  // interface not connected
04863                         CALLBACK_SCF(true);
04864                         break;
04865                     }
04866                     if (reg_bx != 0x0001 && reg_bx != 0x8001) {
04867                         reg_ah = 0x09;  // unrecognized device ID
04868                         CALLBACK_SCF(true);         
04869                         break;
04870                     }
04871                     /* FIXME: Allow configuration and shell commands to dictate whether or
04872                      *        not we emulate a laptop with a battery */
04873                     reg_bh = 0x01;      // AC line status (1=on-line)
04874                     reg_bl = 0xFF;      // Battery status (unknown)
04875                     reg_ch = 0x80;      // Battery flag (no system battery)
04876                     reg_cl = 0xFF;      // Remaining battery charge (unknown)
04877                     reg_dx = 0xFFFF;    // Remaining battery life (unknown)
04878                     reg_si = 0;     // Number of battery units (if called with reg_bx == 0x8001)
04879                     CALLBACK_SCF(false);
04880                     break;
04881                 case 0x0b: // GET PM EVENT
04882                     if (!apm_realmode_connected) {
04883                         reg_ah = 0x03;  // interface not connected
04884                         CALLBACK_SCF(true);
04885                         break;
04886                     }
04887                     reg_ah = 0x80; // no power management events pending
04888                     CALLBACK_SCF(true);
04889                     break;
04890                 case 0x0d:
04891                     // NTS: NOT implementing this call can cause Windows 98's APM driver to crash on startup
04892                     if(reg_bx != 0x0 && reg_bx != 0x1) {
04893                         reg_ah = 0x09;  // unrecognized device ID
04894                         CALLBACK_SCF(true);
04895                         break;
04896                     } else if(!apm_realmode_connected) {
04897                         reg_ah = 0x03;
04898                         CALLBACK_SCF(true);
04899                         break;
04900                     }
04901                     if(reg_cx==0x0) {
04902                         LOG_MSG("disable APM for device %4x",reg_bx);
04903                         CALLBACK_SCF(false);
04904                     }
04905                     else if(reg_cx==0x1) {
04906                         LOG_MSG("enable APM for device %4x",reg_bx);
04907                         CALLBACK_SCF(false);
04908                     }
04909                     else {
04910                         reg_ah = 0x0A; // invalid parameter value in CX
04911                         CALLBACK_SCF(true);
04912                     }
04913                     break;
04914                 case 0x0e:
04915                     if (APM_BIOS_minor_version != 0) { // APM 1.1 or higher only
04916                         if(reg_bx != 0x0) {
04917                             reg_ah = 0x09;  // unrecognized device ID
04918                             CALLBACK_SCF(true);
04919                             break;
04920                         } else if(!apm_realmode_connected) {
04921                             reg_ah = 0x03;  // interface not connected
04922                             CALLBACK_SCF(true);
04923                             break;
04924                         }
04925                         reg_ah = reg_ch; /* we are called with desired version in CH,CL, return actual version in AH,AL */
04926                         reg_al = reg_cl;
04927                         if(reg_ah != 1) reg_ah = 1;                     // major
04928                         if(reg_al > APM_BIOS_minor_version) reg_al = APM_BIOS_minor_version;    // minor
04929                         APM_BIOS_connected_minor_version = reg_al;              // what we decided becomes the interface we emulate
04930                         LOG_MSG("APM BIOS negotiated to v1.%u",APM_BIOS_connected_minor_version);
04931                         CALLBACK_SCF(false);
04932                     }
04933                     else { // APM 1.0 does not recognize this call
04934                         reg_ah = 0x0C; // function not supported
04935                         CALLBACK_SCF(true);
04936                     }
04937                     break;
04938                 case 0x0f:
04939                     if(reg_bx != 0x0 && reg_bx != 0x1) {
04940                         reg_ah = 0x09;  // unrecognized device ID
04941                         CALLBACK_SCF(true);         
04942                         break;
04943                     } else if(!apm_realmode_connected) {
04944                         reg_ah = 0x03;
04945                         CALLBACK_SCF(true);
04946                         break;
04947                     }
04948                     if(reg_cx==0x0) {
04949                         LOG_MSG("disengage APM for device %4x",reg_bx);
04950                         CALLBACK_SCF(false);
04951                     }
04952                     else if(reg_cx==0x1) {
04953                         LOG_MSG("engage APM for device %4x",reg_bx);
04954                         CALLBACK_SCF(false);
04955                     }
04956                     else {
04957                         reg_ah = 0x0A; // invalid parameter value in CX
04958                         CALLBACK_SCF(true);
04959                     }
04960                     break;
04961                 case 0x10:
04962                     if (!apm_realmode_connected) {
04963                         reg_ah = 0x03;  // interface not connected
04964                         CALLBACK_SCF(true);
04965                         break;
04966                     }
04967                     if (reg_bx != 0) {
04968                         reg_ah = 0x09;  // unrecognized device ID
04969                         CALLBACK_SCF(true);
04970                         break;
04971                     }
04972                     reg_ah = 0;
04973                     reg_bl = 0; // number of battery units
04974                     reg_cx = 0x03; // can enter suspend/standby and will post standby/resume events
04975                     CALLBACK_SCF(false);
04976                     break;
04977                 case 0x13://enable/disable/query timer based requests
04978                     // NTS: NOT implementing this call can cause Windows 98's APM driver to crash on startup
04979                     if (!apm_realmode_connected) {
04980                         reg_ah = 0x03;  // interface not connected
04981                         CALLBACK_SCF(true);
04982                         break;
04983                     }
04984                     if (reg_bx != 0) {
04985                         reg_ah = 0x09;  // unrecognized device ID
04986                         CALLBACK_SCF(true);
04987                         break;
04988                     }
04989 
04990                     if (reg_cx == 0) { // disable
04991                         APM_inactivity_timer = false;
04992                         reg_cx = 0;
04993                         CALLBACK_SCF(false);
04994                     }
04995                     else if (reg_cx == 1) { // enable
04996                         APM_inactivity_timer = true;
04997                         reg_cx = 1;
04998                         CALLBACK_SCF(false);
04999                     }
05000                     else if (reg_cx == 2) { // get enabled status
05001                         reg_cx = APM_inactivity_timer ? 1 : 0;
05002                         CALLBACK_SCF(false);
05003                     }
05004                     else {
05005                         reg_ah = 0x0A; // invalid parameter value in CX
05006                         CALLBACK_SCF(true);
05007                     }
05008                     break;
05009                 default:
05010                     LOG_MSG("Unknown APM BIOS call AX=%04x\n",reg_ax);
05011                     if (!apm_realmode_connected) {
05012                         reg_ah = 0x03;  // interface not connected
05013                         CALLBACK_SCF(true);
05014                         break;
05015                     }
05016                     reg_ah = 0x0C; // function not supported
05017                     CALLBACK_SCF(true);
05018                     break;
05019             }
05020         }
05021         else {
05022             reg_ah=0x86;
05023             CALLBACK_SCF(true);
05024             LOG_MSG("APM BIOS call attempted. set apmbios=1 if you want power management\n");
05025             if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) {
05026                 /* relict from comparisons, as int15 exits with a retf2 instead of an iret */
05027                 CALLBACK_SZF(false);
05028             }
05029         }
05030         break;
05031     case 0xe8:
05032         switch (reg_al) {
05033             case 0x01: { /* E801: memory size */
05034                     Bitu sz = MEM_TotalPages()*4;
05035                     if (sz >= 1024) sz -= 1024;
05036                     else sz = 0;
05037                     reg_ax = reg_cx = (sz > 0x3C00) ? 0x3C00 : sz; /* extended memory between 1MB and 16MB in KBs */
05038                     sz -= reg_ax;
05039                     sz /= 64;   /* extended memory size from 16MB in 64KB blocks */
05040                     if (sz > 65535) sz = 65535;
05041                     reg_bx = reg_dx = sz;
05042                     CALLBACK_SCF(false);
05043                 }
05044                 break;
05045             case 0x20: { /* E820: MEMORY LISTING */
05046                     if (reg_edx == 0x534D4150 && reg_ecx >= 20 && (MEM_TotalPages()*4) >= 24000) {
05047                         /* return a minimalist list:
05048                          *
05049                          *    0) 0x000000-0x09EFFF       Free memory
05050                          *    1) 0x0C0000-0x0FFFFF       Reserved
05051                          *    2) 0x100000-...            Free memory (no ACPI tables) */
05052                         if (reg_ebx < 3) {
05053                             uint32_t base,len,type;
05054                             Bitu seg = SegValue(es);
05055 
05056                             assert((MEM_TotalPages()*4096) >= 0x100000);
05057 
05058                             switch (reg_ebx) {
05059                                 case 0: base=0x000000; len=0x09F000; type=1; break;
05060                                 case 1: base=0x0C0000; len=0x040000; type=2; break;
05061                                 case 2: base=0x100000; len=(MEM_TotalPages()*4096)-0x100000; type=1; break;
05062                                 default: E_Exit("Despite checks EBX is wrong value"); /* BUG! */
05063                             };
05064 
05065                             /* write to ES:DI */
05066                             real_writed(seg,reg_di+0x00,base);
05067                             real_writed(seg,reg_di+0x04,0);
05068                             real_writed(seg,reg_di+0x08,len);
05069                             real_writed(seg,reg_di+0x0C,0);
05070                             real_writed(seg,reg_di+0x10,type);
05071                             reg_ecx = 20;
05072 
05073                             /* return EBX pointing to next entry. wrap around, as most BIOSes do.
05074                              * the program is supposed to stop on CF=1 or when we return EBX == 0 */
05075                             if (++reg_ebx >= 3) reg_ebx = 0;
05076                         }
05077                         else {
05078                             CALLBACK_SCF(true);
05079                         }
05080 
05081                         reg_eax = 0x534D4150;
05082                     }
05083                     else {
05084                         reg_eax = 0x8600;
05085                         CALLBACK_SCF(true);
05086                     }
05087                 }
05088                 break;
05089             default:
05090                 LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call ah=E8, al=%2X",reg_al);
05091                 reg_ah=0x86;
05092                 CALLBACK_SCF(true);
05093                 if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) {
05094                     /* relict from comparisons, as int15 exits with a retf2 instead of an iret */
05095                     CALLBACK_SZF(false);
05096                 }
05097         }
05098         break;
05099     default:
05100         LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call ax=%4X",reg_ax);
05101         reg_ah=0x86;
05102         CALLBACK_SCF(true);
05103         if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) {
05104             /* relict from comparisons, as int15 exits with a retf2 instead of an iret */
05105             CALLBACK_SZF(false);
05106         }
05107     }
05108     return CBRET_NONE;
05109 }
05110 
05111 void BIOS_UnsetupKeyboard(void);
05112 void BIOS_SetupKeyboard(void);
05113 void BIOS_UnsetupDisks(void);
05114 void BIOS_SetupDisks(void);
05115 void CPU_Snap_Back_To_Real_Mode();
05116 void CPU_Snap_Back_Restore();
05117 
05118 void restart_program(std::vector<std::string> & parameters);
05119 
05120 static Bitu IRQ14_Dummy(void) {
05121     /* FIXME: That's it? Don't I EOI the PIC? */
05122     return CBRET_NONE;
05123 }
05124 
05125 static Bitu IRQ15_Dummy(void) {
05126     /* FIXME: That's it? Don't I EOI the PIC? */
05127     return CBRET_NONE;
05128 }
05129 
05130 void On_Software_CPU_Reset();
05131 
05132 static Bitu INT18_Handler(void) {
05133     LOG_MSG("Restart by INT 18h requested\n");
05134     On_Software_CPU_Reset();
05135     /* does not return */
05136     return CBRET_NONE;
05137 }
05138 
05139 static Bitu INT19_Handler(void) {
05140     LOG_MSG("Restart by INT 19h requested\n");
05141     /* FIXME: INT 19h is sort of a BIOS boot BIOS reset-ish thing, not really a CPU reset at all. */
05142     On_Software_CPU_Reset();
05143     /* does not return */
05144     return CBRET_NONE;
05145 }
05146 
05147 void bios_enable_ps2() {
05148     mem_writew(BIOS_CONFIGURATION,
05149         mem_readw(BIOS_CONFIGURATION)|0x04); /* PS/2 mouse */
05150 }
05151 
05152 void BIOS_ZeroExtendedSize(bool in) {
05153     if(in) other_memsystems++; 
05154     else other_memsystems--;
05155     if(other_memsystems < 0) other_memsystems=0;
05156 
05157     if (IS_PC98_ARCH) {
05158         Bitu mempages = MEM_TotalPages(); /* in 4KB pages */
05159 
05160         /* What applies to IBM PC/AT (zeroing out the extended memory size)
05161          * also applies to PC-98, when HIMEM.SYS is loaded */
05162         if (in) mempages = 0;
05163 
05164         /* extended memory size (286 systems, below 16MB) */
05165         if (mempages > (1024UL/4UL)) {
05166             unsigned int ext = ((mempages - (1024UL/4UL)) * 4096UL) / (128UL * 1024UL); /* convert to 128KB units */
05167 
05168             /* extended memory, up to 16MB capacity (for 286 systems?)
05169              *
05170              * MS-DOS drivers will "allocate" for themselves by taking from the top of
05171              * extended memory then subtracting from this value.
05172              *
05173              * capacity does not include conventional memory below 1MB, nor any memory
05174              * above 16MB.
05175              *
05176              * PC-98 systems may reserve the top 1MB, limiting the top to 15MB instead.
05177              *
05178              * 0x70 = 128KB * 0x70 = 14MB
05179              * 0x78 = 128KB * 0x70 = 15MB */
05180             if (ext > 0x78) ext = 0x78;
05181 
05182             mem_writeb(0x401,ext);
05183         }
05184         else {
05185             mem_writeb(0x401,0x00);
05186         }
05187 
05188         /* extended memory size (386 systems, at or above 16MB) */
05189         if (mempages > ((1024UL*16UL)/4UL)) {
05190             unsigned int ext = ((mempages - ((1024UL*16UL)/4UL)) * 4096UL) / (1024UL * 1024UL); /* convert to MB */
05191 
05192             /* extended memory, at or above 16MB capacity (for 386+ systems?)
05193              *
05194              * MS-DOS drivers will "allocate" for themselves by taking from the top of
05195              * extended memory then subtracting from this value.
05196              *
05197              * capacity does not include conventional memory below 1MB, nor any memory
05198              * below 16MB. */
05199             if (ext > 0xFFFE) ext = 0xFFFE;
05200 
05201             mem_writew(0x594,ext);
05202         }
05203         else {
05204             mem_writew(0x594,0x00);
05205         }
05206     }
05207 }
05208 
05209 unsigned char do_isapnp_chksum(unsigned char *d,int i) {
05210     unsigned char sum = 0;
05211 
05212     while (i-- > 0)
05213         sum += *d++;
05214 
05215     return (0x100 - sum) & 0xFF;
05216 }
05217 
05218 void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages);
05219 
05220 unsigned int dos_conventional_limit = 0;
05221 
05222 bool AdapterROM_Read(Bitu address,unsigned long *size) {
05223     unsigned char chksum=0;
05224     unsigned char c[3];
05225     unsigned int i;
05226 
05227     if ((address & 0x1FF) != 0) {
05228         LOG(LOG_MISC,LOG_DEBUG)("AdapterROM_Read: Caller attempted ROM scan not aligned to 512-byte boundary");
05229         return false;
05230     }
05231 
05232     for (i=0;i < 3;i++)
05233         c[i] = mem_readb(address+i);
05234 
05235     if (c[0] == 0x55 && c[1] == 0xAA) {
05236         *size = (unsigned long)c[2] * 512UL;
05237         for (i=0;i < (unsigned int)(*size);i++) chksum += mem_readb(address+i);
05238         if (chksum != 0) {
05239             LOG(LOG_MISC,LOG_WARN)("AdapterROM_Read: Found ROM at 0x%lx but checksum failed\n",(unsigned long)address);
05240             return false;
05241         }
05242 
05243         return true;
05244     }
05245 
05246     return false;
05247 }
05248 
05249 #include "src/gui/dosbox.vga16.bmp.h"
05250 #include "src/gui/dosbox.cga640.bmp.h"
05251 
05252 void DrawDOSBoxLogoCGA6(unsigned int x,unsigned int y) {
05253     unsigned char *s = dosbox_cga640_bmp;
05254     unsigned char *sf = s + sizeof(dosbox_cga640_bmp);
05255     uint32_t width,height;
05256     unsigned int dx,dy;
05257     uint32_t vram;
05258     uint32_t off;
05259     uint32_t sz;
05260 
05261     if (memcmp(s,"BM",2)) return;
05262     sz = host_readd(s+2); // size of total bitmap
05263     off = host_readd(s+10); // offset of bitmap
05264     if ((s+sz) > sf) return;
05265     if ((s+14+40) > sf) return;
05266 
05267     sz = host_readd(s+34); // biSize
05268     if ((s+off+sz) > sf) return;
05269     if (host_readw(s+26) != 1) return; // biBitPlanes
05270     if (host_readw(s+28) != 1)  return; // biBitCount
05271 
05272     width = host_readd(s+18);
05273     height = host_readd(s+22);
05274     if (width > (640-x) || height > (200-y)) return;
05275 
05276     LOG(LOG_MISC,LOG_DEBUG)("Drawing CGA logo (%u x %u)",(int)width,(int)height);
05277     for (dy=0;dy < height;dy++) {
05278         vram  = ((y+dy) >> 1) * 80;
05279         vram += ((y+dy) & 1) * 0x2000;
05280         vram += (x / 8);
05281         s = dosbox_cga640_bmp + off + ((height-(dy+1))*((width+7)/8));
05282         for (dx=0;dx < width;dx += 8) {
05283             mem_writeb(0xB8000+vram,*s);
05284             vram++;
05285             s++;
05286         }
05287     }
05288 }
05289 
05290 /* HACK: Re-use the VGA logo */
05291 void DrawDOSBoxLogoPC98(unsigned int x,unsigned int y) {
05292     unsigned char *s = dosbox_vga16_bmp;
05293     unsigned char *sf = s + sizeof(dosbox_vga16_bmp);
05294     unsigned int bit,dx,dy;
05295     uint32_t width,height;
05296     unsigned char p[4];
05297     unsigned char c;
05298     uint32_t vram;
05299     uint32_t off;
05300     uint32_t sz;
05301 
05302     if (memcmp(s,"BM",2)) return;
05303     sz = host_readd(s+2); // size of total bitmap
05304     off = host_readd(s+10); // offset of bitmap
05305     if ((s+sz) > sf) return;
05306     if ((s+14+40) > sf) return;
05307 
05308     sz = host_readd(s+34); // biSize
05309     if ((s+off+sz) > sf) return;
05310     if (host_readw(s+26) != 1) return; // biBitPlanes
05311     if (host_readw(s+28) != 4)  return; // biBitCount
05312 
05313     width = host_readd(s+18);
05314     height = host_readd(s+22);
05315     if (width > (640-x) || height > (350-y)) return;
05316 
05317     // EGA/VGA Write Mode 2
05318     LOG(LOG_MISC,LOG_DEBUG)("Drawing VGA logo as PC-98 (%u x %u)",(int)width,(int)height);
05319     for (dy=0;dy < height;dy++) {
05320         vram = ((y+dy) * 80) + (x / 8);
05321         s = dosbox_vga16_bmp + off + ((height-(dy+1))*((width+1)/2));
05322         for (dx=0;dx < width;dx += 8) {
05323             p[0] = p[1] = p[2] = p[3] = 0;
05324             for (bit=0;bit < 8;) {
05325                 c = (*s >> 4);
05326                 p[0] |= ((c >> 0) & 1) << (7 - bit);
05327                 p[1] |= ((c >> 1) & 1) << (7 - bit);
05328                 p[2] |= ((c >> 2) & 1) << (7 - bit);
05329                 p[3] |= ((c >> 3) & 1) << (7 - bit);
05330                 bit++;
05331 
05332                 c = (*s++) & 0xF;
05333                 p[0] |= ((c >> 0) & 1) << (7 - bit);
05334                 p[1] |= ((c >> 1) & 1) << (7 - bit);
05335                 p[2] |= ((c >> 2) & 1) << (7 - bit);
05336                 p[3] |= ((c >> 3) & 1) << (7 - bit);
05337                 bit++;
05338             }
05339 
05340             mem_writeb(0xA8000+vram,p[0]);
05341             mem_writeb(0xB0000+vram,p[1]);
05342             mem_writeb(0xB8000+vram,p[2]);
05343             mem_writeb(0xE0000+vram,p[3]);
05344             vram++;
05345         }
05346     }
05347 }
05348 
05349 void DrawDOSBoxLogoVGA(unsigned int x,unsigned int y) {
05350     unsigned char *s = dosbox_vga16_bmp;
05351     unsigned char *sf = s + sizeof(dosbox_vga16_bmp);
05352     unsigned int bit,dx,dy;
05353     uint32_t width,height;
05354     uint32_t vram;
05355     uint32_t off;
05356     uint32_t sz;
05357 
05358     if (memcmp(s,"BM",2)) return;
05359     sz = host_readd(s+2); // size of total bitmap
05360     off = host_readd(s+10); // offset of bitmap
05361     if ((s+sz) > sf) return;
05362     if ((s+14+40) > sf) return;
05363 
05364     sz = host_readd(s+34); // biSize
05365     if ((s+off+sz) > sf) return;
05366     if (host_readw(s+26) != 1) return; // biBitPlanes
05367     if (host_readw(s+28) != 4)  return; // biBitCount
05368 
05369     width = host_readd(s+18);
05370     height = host_readd(s+22);
05371     if (width > (640-x) || height > (350-y)) return;
05372 
05373     // EGA/VGA Write Mode 2
05374     LOG(LOG_MISC,LOG_DEBUG)("Drawing VGA logo (%u x %u)",(int)width,(int)height);
05375     IO_Write(0x3CE,0x05); // graphics mode
05376     IO_Write(0x3CF,0x02); // read=0 write=2 odd/even=0 shift=0 shift256=0
05377     IO_Write(0x3CE,0x03); // data rotate
05378     IO_Write(0x3CE,0x00); // no rotate, no XOP
05379     for (bit=0;bit < 8;bit++) {
05380         const unsigned char shf = ((bit & 1) ^ 1) * 4;
05381 
05382         IO_Write(0x3CE,0x08); // bit mask
05383         IO_Write(0x3CF,0x80 >> bit);
05384 
05385         for (dy=0;dy < height;dy++) {
05386             vram = ((y+dy) * 80) + (x / 8);
05387             s = dosbox_vga16_bmp + off + (bit/2) + ((height-(dy+1))*((width+1)/2));
05388             for (dx=bit;dx < width;dx += 8) {
05389                 mem_readb(0xA0000+vram); // load VGA latches
05390                 mem_writeb(0xA0000+vram,(*s >> shf) & 0xF);
05391                 vram++;
05392                 s += 4;
05393             }
05394         }
05395     }
05396     // restore write mode 0
05397     IO_Write(0x3CE,0x05); // graphics mode
05398     IO_Write(0x3CF,0x00); // read=0 write=0 odd/even=0 shift=0 shift256=0
05399     IO_Write(0x3CE,0x08); // bit mask
05400     IO_Write(0x3CF,0xFF);
05401 }
05402 
05403 static int bios_pc98_posx = 0;
05404 
05405 static void BIOS_Int10RightJustifiedPrint(const int x,int &y,const char *msg) {
05406     const char *s = msg;
05407 
05408     if (machine != MCH_PC98) {
05409         while (*s != 0) {
05410             if (*s == '\n') {
05411                 y++;
05412                 reg_eax = 0x0200u;   // set cursor pos
05413                 reg_ebx = 0;        // page zero
05414                 reg_dh = y;     // row 4
05415                 reg_dl = x;     // column 20
05416                 CALLBACK_RunRealInt(0x10);
05417                 s++;
05418             }
05419             else {
05420                 reg_eax = 0x0E00u | ((unsigned char)(*s++));
05421                 reg_ebx = 0x07u;
05422                 CALLBACK_RunRealInt(0x10);
05423             }
05424         }
05425     }
05426     else {
05427         unsigned int bo;
05428 
05429         while (*s != 0) {
05430             if (*s == '\n') {
05431                 y++;
05432                 s++;
05433                 bios_pc98_posx = x;
05434 
05435                 bo = (((unsigned int)y * 80u) + (unsigned int)bios_pc98_posx) * 2u;
05436             }
05437             else if (*s == '\r') {
05438                 s++; /* ignore */
05439                 continue;
05440             }
05441             else {
05442                 bo = (((unsigned int)y * 80u) + (unsigned int)(bios_pc98_posx++)) * 2u; /* NTS: note the post increment */
05443 
05444                 mem_writew(0xA0000+bo,(unsigned char)(*s++));
05445                 mem_writeb(0xA2000+bo,0xE1);
05446 
05447                 bo += 2; /* and keep the cursor following the text */
05448             }
05449 
05450             reg_eax = 0x1300;   // set cursor pos (PC-98)
05451             reg_edx = bo;       // byte position
05452             CALLBACK_RunRealInt(0x18);
05453         }
05454     }
05455 }
05456 
05457 static Bitu ulimit = 0;
05458 static Bitu t_conv = 0;
05459 static bool bios_first_init=true;
05460 static bool bios_has_exec_vga_bios=false;
05461 static Bitu adapter_scan_start;
05462 
05463 /* FIXME: At global scope their destructors are called after the rest of DOSBox has shut down. Move back into BIOS scope. */
05464 static CALLBACK_HandlerObject int4b_callback;
05465 static CALLBACK_HandlerObject callback[20]; /* <- fixme: this is stupid. just declare one per interrupt. */
05466 static CALLBACK_HandlerObject cb_bios_post;
05467 static CALLBACK_HandlerObject callback_pc98_lio;
05468 
05469 Bitu call_pnp_r = ~0UL;
05470 Bitu call_pnp_rp = 0;
05471 
05472 Bitu call_pnp_p = ~0UL;
05473 Bitu call_pnp_pp = 0;
05474 
05475 Bitu isapnp_biosstruct_base = 0;
05476 
05477 Bitu BIOS_boot_code_offset = 0;
05478 Bitu BIOS_bootfail_code_offset = 0;
05479 
05480 bool bios_user_reset_vector_blob_run = false;
05481 Bitu bios_user_reset_vector_blob = 0;
05482 
05483 Bitu bios_user_boot_hook = 0;
05484 
05485 void CALLBACK_DeAllocate(Bitu in);
05486 
05487 void BIOS_OnResetComplete(Section *x);
05488 
05489 Bitu call_irq0 = 0;
05490 Bitu call_irq07default = 0;
05491 Bitu call_irq815default = 0;
05492 
05493 void write_FFFF_PC98_signature() {
05494     /* this may overwrite the existing signature.
05495      * PC-98 systems DO NOT have an ASCII date at F000:FFF5
05496      * and the WORD value at F000:FFFE is said to be a checksum of the BIOS */
05497 
05498     // The farjump at the processor reset entry point (jumps to POST routine)
05499     phys_writeb(0xffff0,0xEA);                  // FARJMP
05500     phys_writew(0xffff1,RealOff(BIOS_DEFAULT_RESET_LOCATION));  // offset
05501     phys_writew(0xffff3,RealSeg(BIOS_DEFAULT_RESET_LOCATION));  // segment
05502 
05503     // write nothing (not used)
05504     for(Bitu i = 0; i < 9; i++) phys_writeb(0xffff5+i,0);
05505 
05506     // fake BIOS checksum
05507     phys_writew(0xffffe,0xABCD);
05508 }
05509 
05510 void gdc_egc_enable_update_vars(void) {
05511     unsigned char b;
05512 
05513     b = mem_readb(0x54D);
05514     b &= ~0x40;
05515     if (enable_pc98_egc) b |= 0x40;
05516     mem_writeb(0x54D,b);
05517 
05518     b = mem_readb(0x597);
05519     b &= ~0x04;
05520     if (enable_pc98_egc) b |= 0x04;
05521     mem_writeb(0x597,b);
05522 
05523     if (!enable_pc98_egc)
05524         pc98_gdc_vramop &= ~(1 << VOPBIT_EGC);
05525 }
05526 
05527 void gdc_grcg_enable_update_vars(void) {
05528     unsigned char b;
05529 
05530     b = mem_readb(0x54C);
05531     b &= ~0x02;
05532     if (enable_pc98_grcg) b |= 0x02;
05533     mem_writeb(0x54C,b);
05534     
05535     //TODO: How to reset GRCG?
05536 }
05537 
05538 
05539 void gdc_16color_enable_update_vars(void) {
05540     unsigned char b;
05541 
05542     b = mem_readb(0x54C);
05543     b &= ~0x04;
05544     if (enable_pc98_16color) b |= 0x04;
05545     mem_writeb(0x54C,b);
05546     
05547     if(!enable_pc98_16color) {//force switch to 8-colors mode
05548         void pc98_port6A_command_write(unsigned char b);
05549         pc98_port6A_command_write(0x00);
05550     }
05551 }
05552 
05553 /* NTS: Remember the 8259 is non-sentient, and the term "slave" is used in a computer programming context */
05554 static Bitu Default_IRQ_Handler_Cooperative_Slave_Pic(void) {
05555     /* PC-98 style IRQ 8-15 handling.
05556      *
05557      * This mimics the recommended procedure [https://www.webtech.co.jp/company/doc/undocumented_mem/io_pic.txt]
05558      *
05559      *  mov al,20h      ;Send EOI to SLAVE
05560      *  out 0008h,al
05561      *  jmp $+2         ;I/O WAIT
05562      *  mov al,0Bh      ;ISR read mode set(slave)
05563      *  out 0008h,al
05564      *  jmp $+2         ;I/O WAIT
05565      *  in  al,0008h    ;ISR read(slave)
05566      *  cmp al,00h      ;slave pic in-service ?
05567      *  jne EoiEnd
05568      *  mov al,20h      ;Send EOI to MASTER
05569      *  out 0000h,al
05570      */
05571     IO_WriteB(IS_PC98_ARCH ? 0x08 : 0xA0,0x20); // send EOI to slave
05572     IO_WriteB(IS_PC98_ARCH ? 0x08 : 0xA0,0x0B); // ISR read mode set
05573     if (IO_ReadB(IS_PC98_ARCH ? 0x08 : 0xA0) == 0) // if slave pic in service..
05574         IO_WriteB(IS_PC98_ARCH ? 0x00 : 0x20,0x20); // then EOI the master
05575 
05576     return CBRET_NONE;
05577 }
05578 
05579 class BIOS:public Module_base{
05580 private:
05581     static Bitu cb_bios_post__func(void) {
05582         void TIMER_BIOS_INIT_Configure();
05583 #if C_DEBUG
05584         void DEBUG_CheckCSIP();
05585 #endif
05586 
05587 #if C_HEAVY_DEBUG
05588         /* the game/app obviously crashed, which is way more important
05589          * to log than what we do here in the BIOS at POST */
05590         void DEBUG_StopLog(void);
05591         DEBUG_StopLog();
05592 #endif
05593 
05594         if (bios_first_init) {
05595             /* clear the first 1KB-32KB */
05596             for (Bit16u i=0x400;i<0x8000;i++) real_writeb(0x0,i,0);
05597         }
05598 
05599         if (IS_PC98_ARCH) {
05600             for (unsigned int i=0;i < 20;i++) callback[i].Uninstall();
05601 
05602             /* clear out 0x50 segment (TODO: 0x40 too?) */
05603             for (unsigned int i=0;i < 0x100;i++) phys_writeb(0x500+i,0);
05604 
05605             write_FFFF_PC98_signature();
05606             BIOS_ZeroExtendedSize(false);
05607 
05608             unsigned char memsize_real_code = 0;
05609             Bitu mempages = MEM_TotalPages(); /* in 4KB pages */
05610 
05611             /* NTS: Fill in the 3-bit code in FLAGS1 that represents
05612              *      how much lower conventional memory is in the system.
05613              *
05614              *      Note that MEM.EXE requires this value, or else it
05615              *      will complain about broken UMB linkage and fail
05616              *      to show anything else. */
05617             /* TODO: In the event we eventually support "high resolution mode"
05618              *       we can indicate 768KB here, code == 5, meaning that
05619              *       the RAM extends up to 0xBFFFF instead of 0x9FFFF */
05620             if (mempages >= (640UL/4UL))        /* 640KB */
05621                 memsize_real_code = 4;
05622             else if (mempages >= (512UL/4UL))   /* 512KB */
05623                 memsize_real_code = 3;
05624             else if (mempages >= (384UL/4UL))   /* 384KB */
05625                 memsize_real_code = 2;
05626             else if (mempages >= (256UL/4UL))   /* 256KB */
05627                 memsize_real_code = 1;
05628             else                                /* 128KB */
05629                 memsize_real_code = 0;
05630 
05631             void pc98_msw3_set_ramsize(const unsigned char b);
05632             pc98_msw3_set_ramsize(memsize_real_code);
05633 
05634             /* CRT status */
05635             /* bit[7:6] = 00=conventional compatible  01=extended attr JEH  10=extended attr EGH
05636              * bit[5:5] = Single event timer in use flag    1=busy  0=not used
05637              * bit[4:4] = ?
05638              * bit[3:3] = raster scan    1=non-interlaced     0=interlaced
05639              * bit[2:2] = Content ruled line color  1=I/O set value    0=attributes of VRAM
05640              * bit[1:1] = ?
05641              * bit[0:0] = 480-line mode    1=640x480     0=640x400 or 640x200 */
05642             mem_writeb(0x459,0x08/*non-interlaced*/);
05643 
05644             /* CPU/Display */
05645             /* bit[7:7] = 486SX equivalent (?)                                                                      1=yes
05646              * bit[6:6] = PC-9821 Extended Graph Architecture supported (FIXME: Is this the same as having EGC?)    1=yes
05647              * bit[5:5] = LCD display is color                                                                      1=yes 0=no
05648              * bit[4:4] = ?
05649              * bit[3:3] = ROM drive allow writing
05650              * bit[2:2] = 98 NOTE PC-9801N-08 expansion I/O box connected
05651              * bit[1:1] = 98 NOTE prohibit transition to power saving mode
05652              * bit[0:0] = 98 NOTE coprocessor function available */
05653             mem_writeb(0x45C,(enable_pc98_egc ? 0x40/*Extended Graphics*/ : 0x00));
05654 
05655             /* BIOS flags */
05656             /* bit[7:7] = Startup            1=hot start    0=cold start
05657              * bit[6:6] = BASIC type         ??
05658              * bit[5:5] = Keyboard beep      1=don't beep   0=beep          ... when buffer full
05659              * bit[4:4] = Expansion conv RAM 1=present      0=absent
05660              * bit[3:3] = ??
05661              * bit[2:2] = ??
05662              * bit[1:1] = HD mode            1=1MB mode     0=640KB mode    ... of the floppy drive
05663              * bit[0:0] = Model              1=other        0=PC-9801 original */
05664             /* NTS: MS-DOS 5.0 appears to reduce it's BIOS calls and render the whole
05665              *      console as green IF bit 0 is clear.
05666              *
05667              *      If bit 0 is set, INT 1Ah will be hooked by MS-DOS and, for some odd reason,
05668              *      MS-DOS's hook proc will call to our INT 1Ah + 0x19 bytes. */
05669             mem_writeb(0x500,0x01 | 0x02/*high density drive*/);
05670 
05671             /* BIOS flags */
05672             /* timer setup will set/clear bit 7 */
05673             /* bit[7:7] = system clock freq  1=8MHz         0=5/10Mhz
05674              *          = timer clock freq   1=1.9968MHz    0=2.4576MHz
05675              * bit[6:6] = CPU                1=V30          0=Intel (8086 through Pentium)
05676              * bit[5:5] = Model info         1=Other model  0=PC-9801 Muji, PC-98XA
05677              * bit[4:4] = Model info         ...
05678              * bit[3:3] = Model info         1=High res     0=normal
05679              * bit[2:0] = Realmode memsize
05680              *                             000=128KB      001=256KB
05681              *                             010=384KB      011=512KB
05682              *                             100=640KB      101=768KB
05683              *
05684              * Ref: http://hackipedia.org/browse/Computer/Platform/PC,%20NEC%20PC-98/Collections/Undocumented%209801,%209821%20Volume%202%20(webtech.co.jp)/memsys.txt */
05685             /* NTS: High resolution means 640x400, not the 1120x750 mode known as super high resolution mode.
05686              *      DOSBox-X does not yet emulate super high resolution nor does it emulate the 15khz 200-line "standard" mode.
05687              *      ref: https://github.com/joncampbell123/dosbox-x/issues/906#issuecomment-434513930
05688              *      ref: https://jisho.org/search?utf8=%E2%9C%93&keyword=%E8%B6%85 */
05689             mem_writeb(0x501,0x20 | memsize_real_code);
05690 
05691             /* keyboard buffer */
05692             mem_writew(0x524/*tail*/,0x502);
05693             mem_writew(0x526/*tail*/,0x502);
05694 
05695             /* number of scanlines per text row - 1 */
05696             mem_writeb(0x53B,0x0F); // CRT_RASTER, 640x400 24.83KHz-hsync 56.42Hz-vsync
05697 
05698             /* Text screen status.
05699              * Note that most of the bits are used verbatim in INT 18h AH=0Ah/AH=0Bh */
05700             /* bit[7:7] = High resolution display                   1=yes           0=no (standard)         NOT super high res
05701              * bit[6:6] = vsync                                     1=VSYNC wait    0=end of vsync handling
05702              * bit[5:5] = unused
05703              * bit[4:4] = Number of lines                           1=30 lines      0=20/25 lines
05704              * bit[3:3] = K-CG access mode                          1=dot access    0=code access
05705              * bit[2:2] = Attribute mode (how to handle bit 4)      1=Simp. graphic 0=Vertical line
05706              * bit[1:1] = Number of columns                         1=40 cols       0=80 cols
05707              * bit[0:0] = Number of lines                           1=20/30 lines   0=25 lines */
05708             mem_writeb(0x53C,(true/*TODO*/ ? 0x80/*high res*/ : 0x00/*standard*/));
05709 
05710             /* BIOS raster location */
05711             mem_writew(0x54A,0x1900);
05712 
05713             /* BIOS flags */
05714             /* bit[7:7] = Graphics display state                    1=Visible       0=Blanked (hidden)
05715              * bit[6:6] = CRT type                                  1=high res      0=standard              NOT super high res
05716              * bit[5:5] = Horizontal sync rate                      1=31.47KHz      0=24.83KHz
05717              * bit[4:4] = CRT line mode                             1=480-line      0=400-line
05718              * bit[3:3] = Number of user-defined characters         1=188+          0=63
05719              * bit[2:2] = Extended graphics RAM (for 16-color)      1=present       0=absent
05720              * bit[1:1] = Graphics Charger is present               1=present       0=absent
05721              * bit[0:0] = DIP switch 1-8 at startup                 1=ON            0=OFF (?) */
05722             mem_writeb(0x54C,(true/*TODO*/ ? 0x40/*high res*/ : 0x00/*standard*/) | (enable_pc98_grcg ? 0x02 : 0x00) | (enable_pc98_16color ? 0x04 : 0x00) | (pc98_31khz_mode ? 0x20/*31khz*/ : 0x00/*24khz*/) | (enable_pc98_188usermod ? 0x08 : 0x00)); // PRXCRT, 16-color G-VRAM, GRCG
05723 
05724             /* BIOS flags */
05725             /* bit[7:7] = 256-color board present (PC-H98)
05726              * bit[6:6] = Enhanced Graphics Charger (EGC) is present
05727              * bit[5:5] = GDC at 5.0MHz at boot up (copy of DIP switch 2-8 at startup)      1=yes 0=no
05728              * bit[4:4] = Always "flickerless" drawing mode
05729              * bit[3:3] = Drawing mode with flicker
05730              * bit[2:2] = GDC clock                                                         1=5MHz 0=2.5MHz
05731              * bit[1:0] = Drawing mode of the GDC
05732              *              00 = REPLACE
05733              *              01 = COMPLEMENT
05734              *              10 = CLEAR
05735              *              11 = SET */
05736             mem_writeb(0x54D,(enable_pc98_egc ? 0x40 : 0x00) | (gdc_5mhz_mode ? 0x20 : 0x00) | (gdc_5mhz_mode ? 0x04 : 0x00)); // EGC
05737 
05738             /* BIOS flags */
05739             /* bit[7:7] = INT 18h AH=30h/31h support enabled
05740              * bit[6:3] = 0 (unused)
05741              * bit[2:2] = Enhanced Graphics Mode (EGC) supported
05742              * bit[1:0] = Graphic resolution
05743              *             00 = 640x200 upper half  (2/8/16-color mode)
05744              *             01 = 640x200 lower half  (2/8/16-color mode)
05745              *             10 = 640x400             (2/8/16/256-color mode)
05746              *             11 = 640x480             256-color mode */
05747             mem_writeb(0x597,(enable_pc98_egc ? 0x04 : 0x00)/*EGC*/ |
05748                              (enable_pc98_egc ? 0x80 : 0x00)/*supports INT 18h AH=30h and AH=31h*/ |
05749                              2/*640x400*/);
05750             /* TODO: I would like to eventually add a dosbox.conf option that controls whether INT 18h AH=30h and 31h
05751              *       are enabled, so that retro-development can test code to see how it acts on a newer PC-9821
05752              *       that supports it vs an older PC-9821 that doesn't.
05753              *
05754              *       If the user doesn't set the option, then it is "auto" and determined by machine= PC-98 model and
05755              *       by another option in dosbox.conf that determines whether 31khz support is enabled.
05756              *
05757              *       NOTED: Neko Project II determines INT 18h AH=30h availability by whether or not it was compiled
05758              *              with 31khz hsync support (SUPPORT_CRT31KHZ) */
05759         }
05760 
05761         if (bios_user_reset_vector_blob != 0 && !bios_user_reset_vector_blob_run) {
05762             LOG_MSG("BIOS POST: Running user reset vector blob at 0x%lx",(unsigned long)bios_user_reset_vector_blob);
05763             bios_user_reset_vector_blob_run = true;
05764 
05765             assert((bios_user_reset_vector_blob&0xF) == 0); /* must be page aligned */
05766 
05767             SegSet16(cs,bios_user_reset_vector_blob>>4);
05768             reg_eip = 0;
05769 
05770 #if C_DEBUG
05771             /* help the debugger reflect the new instruction pointer */
05772             DEBUG_CheckCSIP();
05773 #endif
05774 
05775             return CBRET_NONE;
05776         }
05777 
05778         if (cpu.pmode) E_Exit("BIOS error: POST function called while in protected/vm86 mode");
05779 
05780         CPU_CLI();
05781 
05782         /* we need A20 enabled for BIOS boot-up */
05783         void A20Gate_OverrideOn(Section *sec);
05784         void MEM_A20_Enable(bool enabled);
05785         A20Gate_OverrideOn(NULL);
05786         MEM_A20_Enable(true);
05787 
05788         BIOS_OnResetComplete(NULL);
05789 
05790         adapter_scan_start = 0xC0000;
05791         bios_has_exec_vga_bios = false;
05792         LOG(LOG_MISC,LOG_DEBUG)("BIOS: executing POST routine");
05793 
05794         // TODO: Anything we can test in the CPU here?
05795 
05796         // initialize registers
05797         SegSet16(ds,0x0000);
05798         SegSet16(es,0x0000);
05799         SegSet16(fs,0x0000);
05800         SegSet16(gs,0x0000);
05801         SegSet16(ss,0x0000);
05802 
05803         {
05804             Bitu sz = MEM_TotalPages();
05805 
05806             /* The standard BIOS is said to put it's stack (at least at OS boot time) 512 bytes past the end of the boot sector
05807              * meaning that the boot sector loads to 0000:7C00 and the stack is set grow downward from 0000:8000 */
05808 
05809             if (sz > 8) sz = 8; /* 4KB * 8 = 32KB = 0x8000 */
05810             sz *= 4096;
05811             reg_esp = sz - 4;
05812             reg_ebp = 0;
05813             LOG(LOG_MISC,LOG_DEBUG)("BIOS: POST stack set to 0000:%04x",reg_esp);
05814         }
05815 
05816         if (dosbox_int_iocallout != IO_Callout_t_none) {
05817             IO_FreeCallout(dosbox_int_iocallout);
05818             dosbox_int_iocallout = IO_Callout_t_none;
05819         }
05820 
05821         if (isapnp_biosstruct_base != 0) {
05822             ROMBIOS_FreeMemory(isapnp_biosstruct_base);
05823             isapnp_biosstruct_base = 0;
05824         }
05825 
05826         if (BOCHS_PORT_E9) {
05827             delete BOCHS_PORT_E9;
05828             BOCHS_PORT_E9=NULL;
05829         }
05830         if (ISAPNP_PNP_ADDRESS_PORT) {
05831             delete ISAPNP_PNP_ADDRESS_PORT;
05832             ISAPNP_PNP_ADDRESS_PORT=NULL;
05833         }
05834         if (ISAPNP_PNP_DATA_PORT) {
05835             delete ISAPNP_PNP_DATA_PORT;
05836             ISAPNP_PNP_DATA_PORT=NULL;
05837         }
05838         if (ISAPNP_PNP_READ_PORT) {
05839             delete ISAPNP_PNP_READ_PORT;
05840             ISAPNP_PNP_READ_PORT=NULL;
05841         }
05842 
05843         extern Bitu call_default,call_default2;
05844 
05845         if (IS_PC98_ARCH) {
05846             /* INT 00h-FFh generic stub routine */
05847             /* NTS: MS-DOS on PC-98 will fill all yet-unused interrupt vectors with a stub.
05848              *      No vector is left at 0000:0000. On a related note, PC-98 games apparently
05849              *      like to call INT 33h (mouse functions) without first checking that the
05850              *      vector is non-null. */
05851             callback[18].Uninstall();
05852             callback[18].Install(&INTGEN_PC98_Handler,CB_IRET,"Int stub ???");
05853             for (unsigned int i=0x00;i < 0x100;i++) RealSetVec(i,callback[18].Get_RealPointer());
05854 
05855             for (unsigned int i=0x00;i < 0x08;i++)
05856                 real_writed(0,i*4,CALLBACK_RealPointer(call_default));
05857         }
05858         else {
05859             /* Clear the vector table */
05860             for (Bit16u i=0x70*4;i<0x400;i++) real_writeb(0x00,i,0);
05861 
05862             /* Only setup default handler for first part of interrupt table */
05863             for (Bit16u ct=0;ct<0x60;ct++) {
05864                 real_writed(0,ct*4,CALLBACK_RealPointer(call_default));
05865             }
05866             for (Bit16u ct=0x68;ct<0x70;ct++) {
05867                 real_writed(0,ct*4,CALLBACK_RealPointer(call_default));
05868             }
05869 
05870             // default handler for IRQ 2-7
05871             for (Bit16u ct=0x0A;ct <= 0x0F;ct++)
05872                 RealSetVec(ct,BIOS_DEFAULT_IRQ07_DEF_LOCATION);
05873         }
05874 
05875         if (unhandled_irq_method == UNHANDLED_IRQ_COOPERATIVE_2ND) {
05876             // PC-98 style: Master PIC ack with 0x20 for IRQ 0-7.
05877             //              For the slave PIC, ack with 0x20 on the slave, then only ack the master (cascade interrupt)
05878             //              if the ISR register on the slave indicates none are in service.
05879             CALLBACK_Setup(call_irq07default,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ07_DEF_LOCATION),"bios irq 0-7 default handler");
05880             CALLBACK_Setup(call_irq815default,Default_IRQ_Handler_Cooperative_Slave_Pic,CB_IRET,Real2Phys(BIOS_DEFAULT_IRQ815_DEF_LOCATION),"bios irq 8-15 default handler");
05881         }
05882         else {
05883             // IBM PC style: Master PIC ack with 0x20, slave PIC ack with 0x20, no checking
05884             CALLBACK_Setup(call_irq07default,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ07_DEF_LOCATION),"bios irq 0-7 default handler");
05885             CALLBACK_Setup(call_irq815default,NULL,CB_IRET_EOI_PIC2,Real2Phys(BIOS_DEFAULT_IRQ815_DEF_LOCATION),"bios irq 8-15 default handler");
05886         }
05887 
05888         if (IS_PC98_ARCH) {
05889             BIOS_UnsetupKeyboard();
05890             BIOS_UnsetupDisks();
05891 
05892             /* no such INT 4Bh */
05893             int4b_callback.Uninstall();
05894 
05895             /* remove some IBM-style BIOS interrupts that don't exist on PC-98 */
05896             /* IRQ to INT arrangement
05897              *
05898              * IBM          PC-98           IRQ
05899              * --------------------------------
05900              * 0x08         0x08            0
05901              * 0x09         0x09            1
05902              * 0x0A CASCADE 0x0A            2
05903              * 0x0B         0x0B            3
05904              * 0x0C         0x0C            4
05905              * 0x0D         0x0D            5
05906              * 0x0E         0x0E            6
05907              * 0x0F         0x0F CASCADE    7
05908              * 0x70         0x10            8
05909              * 0x71         0x11            9
05910              * 0x72         0x12            10
05911              * 0x73         0x13            11
05912              * 0x74         0x14            12
05913              * 0x75         0x15            13
05914              * 0x76         0x16            14
05915              * 0x77         0x17            15
05916              *
05917              * As part of the change the IRQ cascade emulation needs to change for PC-98 as well.
05918              * IBM uses IRQ 2 for cascade.
05919              * PC-98 uses IRQ 7 for cascade. */
05920 
05921             void INT10_EnterPC98(Section *sec);
05922             INT10_EnterPC98(NULL); /* INT 10h */
05923 
05924             callback_pc98_lio.Uninstall();
05925 
05926             callback[1].Uninstall(); /* INT 11h */
05927             callback[2].Uninstall(); /* INT 12h */
05928             callback[3].Uninstall(); /* INT 14h */
05929             callback[4].Uninstall(); /* INT 15h */
05930             callback[5].Uninstall(); /* INT 17h */
05931             callback[6].Uninstall(); /* INT 1Ah */
05932             callback[7].Uninstall(); /* INT 1Ch */
05933             callback[10].Uninstall(); /* INT 19h */
05934             callback[11].Uninstall(); /* INT 76h: IDE IRQ 14 */
05935             callback[12].Uninstall(); /* INT 77h: IDE IRQ 15 */
05936             callback[15].Uninstall(); /* INT 18h: Enter BASIC */
05937 
05938             /* IRQ 6 is nothing special */
05939             callback[13].Uninstall(); /* INT 0Eh: IDE IRQ 6 */
05940             callback[13].Install(NULL,CB_IRET_EOI_PIC1,"irq 6");
05941 
05942             /* IRQ 8 is nothing special */
05943             callback[8].Uninstall();
05944             callback[8].Install(NULL,CB_IRET_EOI_PIC2,"irq 8");
05945 
05946             /* IRQ 9 is nothing special */
05947             callback[9].Uninstall();
05948             callback[9].Install(NULL,CB_IRET_EOI_PIC2,"irq 9");
05949 
05950             /* INT 18h keyboard and video display functions */
05951             callback[1].Install(&INT18_PC98_Handler,CB_INT16,"Int 18 keyboard and display");
05952             callback[1].Set_RealVec(0x18,/*reinstall*/true);
05953 
05954             /* INT 19h *STUB* */
05955             callback[2].Install(&INT19_PC98_Handler,CB_IRET,"Int 19 ???");
05956             callback[2].Set_RealVec(0x19,/*reinstall*/true);
05957 
05958             /* INT 1Ah *STUB* */
05959             callback[3].Install(&INT1A_PC98_Handler,CB_IRET,"Int 1A ???");
05960             callback[3].Set_RealVec(0x1A,/*reinstall*/true);
05961 
05962             /* MS-DOS 5.0 FIXUP:
05963              * - For whatever reason, if we set bits in the BIOS data area that
05964              *   indicate we're NOT the original model of the PC-98, MS-DOS will
05965              *   hook our INT 1Ah and then call down to 0x19 bytes into our
05966              *   INT 1Ah procedure. If anyone can explain this, I'd like to hear it. --J.C.
05967              *
05968              * NTS: On real hardware, the BIOS appears to have an INT 1Ah, a bunch of NOPs,
05969              *      then at 0x19 bytes into the procedure, the actual handler. This is what
05970              *      MS-DOS is pointing at.
05971              *
05972              *      But wait, there's more.
05973              *
05974              *      MS-DOS calldown pushes DS and DX onto the stack (after the IRET frame)
05975              *      before JMPing into the BIOS.
05976              *
05977              *      Apparently the function at INT 1Ah + 0x19 is expected to do this:
05978              *
05979              *      <function code>
05980              *      POP     DX
05981              *      POP     DS
05982              *      IRET
05983              *
05984              *      I can only imaging what a headache this might have caused NEC when
05985              *      maintaining the platform and compatibility! */
05986             {
05987                 Bitu addr = callback[3].Get_RealPointer();
05988                 addr = ((addr >> 16) << 4) + (addr & 0xFFFF);
05989 
05990                 /* to make this work, we need to pop the two regs, then JMP to our
05991                  * callback and proceed as normal. */
05992                 phys_writeb(addr + 0x19,0x5A);       // POP DX
05993                 phys_writeb(addr + 0x1A,0x1F);       // POP DS
05994                 phys_writeb(addr + 0x1B,0xEB);       // jmp short ...
05995                 phys_writeb(addr + 0x1C,0x100 - 0x1D);
05996             }
05997 
05998             /* INT 1Bh *STUB* */
05999             callback[4].Install(&INT1B_PC98_Handler,CB_IRET,"Int 1B ???");
06000             callback[4].Set_RealVec(0x1B,/*reinstall*/true);
06001 
06002             /* INT 1Ch *STUB* */
06003             callback[5].Install(&INT1C_PC98_Handler,CB_IRET,"Int 1C ???");
06004             callback[5].Set_RealVec(0x1C,/*reinstall*/true);
06005 
06006             /* INT 1Dh *STUB* */
06007             /* Place it in the PC-98 int vector area at FD80:0000 to satisfy some DOS games
06008              * that detect PC-98 from the segment value of the vector (issue #927).
06009              * Note that on real hardware (PC-9821) INT 1Dh appears to be a stub that IRETs immediately. */
06010             callback[6].Install(&INT1D_PC98_Handler,CB_IRET,"Int 1D ???");
06011 //            callback[6].Set_RealVec(0x1D,/*reinstall*/true);
06012             {
06013                 Bitu ofs = 0xFD813; /* 0xFD80:0013 try not to look like a phony address */
06014                 unsigned int vec = 0x1D;
06015                 Bit32u target = callback[6].Get_RealPointer();
06016 
06017                 phys_writeb(ofs+0,0xEA);        // JMP FAR <callback>
06018                 phys_writed(ofs+1,target);
06019 
06020                 phys_writew((vec*4)+0,(ofs-0xFD800));
06021                 phys_writew((vec*4)+2,0xFD80);
06022             }
06023 
06024             /* INT 1Eh *STUB* */
06025             callback[7].Install(&INT1E_PC98_Handler,CB_IRET,"Int 1E ???");
06026             callback[7].Set_RealVec(0x1E,/*reinstall*/true);
06027 
06028             /* INT 1Fh *STUB* */
06029             callback[10].Install(&INT1F_PC98_Handler,CB_IRET,"Int 1F ???");
06030             callback[10].Set_RealVec(0x1F,/*reinstall*/true);
06031 
06032             /* INT DCh *STUB* */
06033             callback[16].Install(&INTDC_PC98_Handler,CB_IRET,"Int DC ???");
06034             callback[16].Set_RealVec(0xDC,/*reinstall*/true);
06035 
06036             /* INT F2h *STUB* */
06037             callback[17].Install(&INTF2_PC98_Handler,CB_IRET,"Int F2 ???");
06038             callback[17].Set_RealVec(0xF2,/*reinstall*/true);
06039 
06040             // default handler for IRQ 2-7
06041             for (Bit16u ct=0x0A;ct <= 0x0F;ct++)
06042                 RealSetVec(ct,BIOS_DEFAULT_IRQ07_DEF_LOCATION);
06043 
06044             // default handler for IRQ 8-15
06045             for (Bit16u ct=0;ct < 8;ct++)
06046                 RealSetVec(ct+(IS_PC98_ARCH ? 0x10 : 0x70),BIOS_DEFAULT_IRQ815_DEF_LOCATION);
06047 
06048             // LIO graphics interface (number of entry points, unknown WORD value and offset into the segment).
06049             {
06050                 callback_pc98_lio.Install(&PC98_BIOS_LIO,CB_IRET,"LIO graphics library");
06051 
06052                 Bitu ofs = 0xF990u << 4u; // F990:0000...
06053                 unsigned int entrypoints = 0x11;
06054                 Bitu final_addr = callback_pc98_lio.Get_RealPointer();
06055 
06056                 /* NTS: Based on GAME/MAZE 999 behavior, these numbers are interrupt vector numbers.
06057                  *      The entry point marked 0xA0 is copied by the game to interrupt vector A0 and
06058                  *      then called with INT A0h even though it blindly assumes the numbers are
06059                  *      sequential from 0xA0-0xAF. */
06060                 unsigned char entrypoint_indexes[0x11] = {
06061                     0xA0,   0xA1,   0xA2,   0xA3,       // +0x00
06062                     0xA4,   0xA5,   0xA6,   0xA7,       // +0x04
06063                     0xA8,   0xA9,   0xAA,   0xAB,       // +0x08
06064                     0xAC,   0xAD,   0xAE,   0xAF,       // +0x0C
06065                     0xCE                                // +0x10
06066                 };
06067 
06068                 assert(((entrypoints * 4) + 4) <= 0x50);
06069                 assert((50 + (entrypoints * 7)) <= 0x100); // a 256-byte region is set aside for this!
06070 
06071                 phys_writed(ofs+0,entrypoints);
06072                 for (unsigned int ent=0;ent < entrypoints;ent++) {
06073                     /* each entry point is "MOV AL,<entrypoint> ; JMP FAR <callback>" */
06074                     /* Yksoft1's patch suggests a segment offset of 0x50 which I'm OK with */
06075                     unsigned int ins_ofs = ofs + 0x50 + (ent * 7);
06076 
06077                     phys_writew(ofs+4+(ent*4)+0,entrypoint_indexes[ent]);
06078                     phys_writew(ofs+4+(ent*4)+2,ins_ofs - ofs);
06079 
06080                     phys_writeb(ins_ofs+0,0xB0);                        // MOV AL,(entrypoint index)
06081                     phys_writeb(ins_ofs+1,entrypoint_indexes[ent]);
06082                     phys_writeb(ins_ofs+2,0xEA);                        // JMP FAR <callback>
06083                     phys_writed(ins_ofs+3,final_addr);
06084                     // total:   ins_ofs+7
06085                 }
06086             }
06087         }
06088 
06089         // setup a few interrupt handlers that point to bios IRETs by default
06090         if (!IS_PC98_ARCH)
06091             real_writed(0,0x0e*4,CALLBACK_RealPointer(call_default2));  //design your own railroad
06092 
06093         if (IS_PC98_ARCH) {
06094             real_writew(0,0x58A,0x0000U); // countdown timer value
06095             PIC_SetIRQMask(0,true); /* PC-98 keeps the timer off unless INT 1Ch is called to set a timer interval */
06096         }
06097 
06098         real_writed(0,0x66*4,CALLBACK_RealPointer(call_default));   //war2d
06099         real_writed(0,0x67*4,CALLBACK_RealPointer(call_default));
06100         real_writed(0,0x68*4,CALLBACK_RealPointer(call_default));
06101         real_writed(0,0x5c*4,CALLBACK_RealPointer(call_default));   //Network stuff
06102         //real_writed(0,0xf*4,0); some games don't like it
06103 
06104         bios_first_init = false;
06105 
06106         DispatchVMEvent(VM_EVENT_BIOS_INIT);
06107 
06108         TIMER_BIOS_INIT_Configure();
06109 
06110         void INT10_Startup(Section *sec);
06111         INT10_Startup(NULL);
06112 
06113         if (!IS_PC98_ARCH) {
06114             extern Bit8u BIOS_tandy_D4_flag;
06115             real_writeb(0x40,0xd4,BIOS_tandy_D4_flag);
06116         }
06117 
06118         /* INT 13 Bios Disk Support */
06119         BIOS_SetupDisks();
06120 
06121         /* INT 16 Keyboard handled in another file */
06122         BIOS_SetupKeyboard();
06123 
06124         if (!IS_PC98_ARCH) {
06125             int4b_callback.Set_RealVec(0x4B,/*reinstall*/true);
06126             callback[1].Set_RealVec(0x11,/*reinstall*/true);
06127             callback[2].Set_RealVec(0x12,/*reinstall*/true);
06128             callback[3].Set_RealVec(0x14,/*reinstall*/true);
06129             callback[4].Set_RealVec(0x15,/*reinstall*/true);
06130             callback[5].Set_RealVec(0x17,/*reinstall*/true);
06131             callback[6].Set_RealVec(0x1A,/*reinstall*/true);
06132             callback[7].Set_RealVec(0x1C,/*reinstall*/true);
06133             callback[8].Set_RealVec(0x70,/*reinstall*/true);
06134             callback[9].Set_RealVec(0x71,/*reinstall*/true);
06135             callback[10].Set_RealVec(0x19,/*reinstall*/true);
06136             callback[11].Set_RealVec(0x76,/*reinstall*/true);
06137             callback[12].Set_RealVec(0x77,/*reinstall*/true);
06138             callback[13].Set_RealVec(0x0E,/*reinstall*/true);
06139             callback[15].Set_RealVec(0x18,/*reinstall*/true);
06140         }
06141 
06142         // FIXME: We're using IBM PC memory size storage even in PC-98 mode.
06143         //        This cannot be removed, because the DOS kernel uses this variable even in PC-98 mode.
06144         mem_writew(BIOS_MEMORY_SIZE,t_conv);
06145 
06146         RealSetVec(0x08,BIOS_DEFAULT_IRQ0_LOCATION);
06147         // pseudocode for CB_IRQ0:
06148         //  sti
06149         //  callback INT8_Handler
06150         //  push ds,ax,dx
06151         //  int 0x1c
06152         //  cli
06153         //  mov al, 0x20
06154         //  out 0x20, al
06155         //  pop dx,ax,ds
06156         //  iret
06157 
06158         if (!IS_PC98_ARCH) {
06159             mem_writed(BIOS_TIMER,0);           //Calculate the correct time
06160             phys_writew(Real2Phys(RealGetVec(0x12))+0x12,0x20); //Hack for Jurresic
06161         }
06162 
06163         phys_writeb(Real2Phys(BIOS_DEFAULT_HANDLER_LOCATION),0xcf); /* bios default interrupt vector location -> IRET */
06164 
06165         if (!IS_PC98_ARCH) {
06166             // tandy DAC setup
06167             bool use_tandyDAC=(real_readb(0x40,0xd4)==0xff);
06168 
06169             tandy_sb.port=0;
06170             tandy_dac.port=0;
06171             if (use_tandyDAC) {
06172                 /* tandy DAC sound requested, see if soundblaster device is available */
06173                 Bitu tandy_dac_type = 0;
06174                 if (Tandy_InitializeSB()) {
06175                     tandy_dac_type = 1;
06176                 } else if (Tandy_InitializeTS()) {
06177                     tandy_dac_type = 2;
06178                 }
06179                 if (tandy_dac_type) {
06180                     real_writew(0x40,0xd0,0x0000);
06181                     real_writew(0x40,0xd2,0x0000);
06182                     real_writeb(0x40,0xd4,0xff);    /* tandy DAC init value */
06183                     real_writed(0x40,0xd6,0x00000000);
06184                     /* install the DAC callback handler */
06185                     tandy_DAC_callback[0]=new CALLBACK_HandlerObject();
06186                     tandy_DAC_callback[1]=new CALLBACK_HandlerObject();
06187                     tandy_DAC_callback[0]->Install(&IRQ_TandyDAC,CB_IRET,"Tandy DAC IRQ");
06188                     tandy_DAC_callback[1]->Install(NULL,CB_TDE_IRET,"Tandy DAC end transfer");
06189                     // pseudocode for CB_TDE_IRET:
06190                     //  push ax
06191                     //  mov ax, 0x91fb
06192                     //  int 15
06193                     //  cli
06194                     //  mov al, 0x20
06195                     //  out 0x20, al
06196                     //  pop ax
06197                     //  iret
06198 
06199                     Bit8u tandy_irq = 7;
06200                     if (tandy_dac_type==1) tandy_irq = tandy_sb.irq;
06201                     else if (tandy_dac_type==2) tandy_irq = tandy_dac.irq;
06202                     Bit8u tandy_irq_vector = tandy_irq;
06203                     if (tandy_irq_vector<8) tandy_irq_vector += 8;
06204                     else tandy_irq_vector += (0x70-8);
06205 
06206                     RealPt current_irq=RealGetVec(tandy_irq_vector);
06207                     real_writed(0x40,0xd6,current_irq);
06208                     for (Bit16u i=0; i<0x10; i++) phys_writeb(PhysMake(0xf000,0xa084+i),0x80);
06209                 } else real_writeb(0x40,0xd4,0x00);
06210             }
06211         }
06212 
06213         if (!IS_PC98_ARCH) {
06214             /* Setup some stuff in 0x40 bios segment */
06215 
06216             // Disney workaround
06217             //      Bit16u disney_port = mem_readw(BIOS_ADDRESS_LPT1);
06218             // port timeouts
06219             // always 1 second even if the port does not exist
06220             //      BIOS_SetLPTPort(0, disney_port);
06221             for(Bitu i = 1; i < 3; i++) BIOS_SetLPTPort(i, 0);
06222             mem_writeb(BIOS_COM1_TIMEOUT,1);
06223             mem_writeb(BIOS_COM2_TIMEOUT,1);
06224             mem_writeb(BIOS_COM3_TIMEOUT,1);
06225             mem_writeb(BIOS_COM4_TIMEOUT,1);
06226 
06227             void BIOS_Post_register_parports();
06228             BIOS_Post_register_parports();
06229 
06230             void BIOS_Post_register_comports();
06231             BIOS_Post_register_comports();
06232         }
06233 
06234         if (!IS_PC98_ARCH) {
06235             /* Setup equipment list */
06236             // look http://www.bioscentral.com/misc/bda.htm
06237 
06238             //Bit16u config=0x4400; //1 Floppy, 2 serial and 1 parallel 
06239             Bit16u config = 0x0;
06240 
06241             config |= bios_post_parport_count() << 14;
06242             config |= bios_post_comport_count() << 9;
06243 
06244 #if (C_FPU)
06245             extern bool enable_fpu;
06246 
06247             //FPU
06248             if (enable_fpu)
06249                 config|=0x2;
06250 #endif
06251             switch (machine) {
06252                 case MCH_MDA:
06253                 case MCH_HERC:
06254                     //Startup monochrome
06255                     config|=0x30;
06256                     break;
06257                 case EGAVGA_ARCH_CASE:
06258                 case MCH_CGA:
06259                 case MCH_MCGA:
06260                 case TANDY_ARCH_CASE:
06261                 case MCH_AMSTRAD:
06262                     //Startup 80x25 color
06263                     config|=0x20;
06264                     break;
06265                 default:
06266                     //EGA VGA
06267                     config|=0;
06268                     break;
06269             }
06270 
06271             // PS2 mouse
06272             if (KEYBOARD_Report_BIOS_PS2Mouse())
06273                 config |= 0x04;
06274 
06275             // Gameport
06276             config |= 0x1000;
06277             mem_writew(BIOS_CONFIGURATION,config);
06278             CMOS_SetRegister(0x14,(Bit8u)(config&0xff)); //Should be updated on changes
06279         }
06280 
06281         if (!IS_PC98_ARCH) {
06282             /* Setup extended memory size */
06283             IO_Write(0x70,0x30);
06284             size_extended=IO_Read(0x71);
06285             IO_Write(0x70,0x31);
06286             size_extended|=(IO_Read(0x71) << 8);
06287             BIOS_HostTimeSync();
06288         }
06289         else {
06290             /* Provide a valid memory size anyway */
06291             size_extended=MEM_TotalPages()*4;
06292             if (size_extended >= 1024) size_extended -= 1024;
06293             else size_extended = 0;
06294         }
06295 
06296         if (!IS_PC98_ARCH) {
06297             /* PS/2 mouse */
06298             void BIOS_PS2Mouse_Startup(Section *sec);
06299             BIOS_PS2Mouse_Startup(NULL);
06300         }
06301 
06302         if (!IS_PC98_ARCH) {
06303             /* this belongs HERE not on-demand from INT 15h! */
06304             biosConfigSeg = ROMBIOS_GetMemory(16/*one paragraph*/,"BIOS configuration (INT 15h AH=0xC0)",/*paragraph align*/16)>>4;
06305             if (biosConfigSeg != 0) {
06306                 PhysPt data = PhysMake(biosConfigSeg,0);
06307                 phys_writew(data,8);                        // 8 Bytes following
06308                 if (IS_TANDY_ARCH) {
06309                     if (machine==MCH_TANDY) {
06310                         // Model ID (Tandy)
06311                         phys_writeb(data+2,0xFF);
06312                     } else {
06313                         // Model ID (PCJR)
06314                         phys_writeb(data+2,0xFD);
06315                     }
06316                     phys_writeb(data+3,0x0A);                   // Submodel ID
06317                     phys_writeb(data+4,0x10);                   // Bios Revision
06318                     /* Tandy doesn't have a 2nd PIC, left as is for now */
06319                     phys_writeb(data+5,(1<<6)|(1<<5)|(1<<4));   // Feature Byte 1
06320                 } else {
06321                     if (machine==MCH_MCGA) {
06322                         /* PC/2 model 30 model */
06323                         phys_writeb(data+2,0xFA);
06324                         phys_writeb(data+3,0x00);                   // Submodel ID (PS/2) model 30
06325                     } else if (PS1AudioCard) { /* FIXME: Won't work because BIOS_Init() comes before PS1SOUND_Init() */
06326                         phys_writeb(data+2,0xFC);                   // Model ID (PC)
06327                         phys_writeb(data+3,0x0B);                   // Submodel ID (PS/1).
06328                     } else {
06329                         phys_writeb(data+2,0xFC);                   // Model ID (PC)
06330                         phys_writeb(data+3,0x00);                   // Submodel ID
06331                     }
06332                     phys_writeb(data+4,0x01);                   // Bios Revision
06333                     phys_writeb(data+5,(1<<6)|(1<<5)|(1<<4));   // Feature Byte 1
06334                 }
06335                 phys_writeb(data+6,(1<<6));             // Feature Byte 2
06336                 phys_writeb(data+7,0);                  // Feature Byte 3
06337                 phys_writeb(data+8,0);                  // Feature Byte 4
06338                 phys_writeb(data+9,0);                  // Feature Byte 5
06339             }
06340         }
06341 
06342         // ISA Plug & Play I/O ports
06343         if (!IS_PC98_ARCH) {
06344             ISAPNP_PNP_ADDRESS_PORT = new IO_WriteHandleObject;
06345             ISAPNP_PNP_ADDRESS_PORT->Install(0x279,isapnp_write_port,IO_MB);
06346             ISAPNP_PNP_DATA_PORT = new IO_WriteHandleObject;
06347             ISAPNP_PNP_DATA_PORT->Install(0xA79,isapnp_write_port,IO_MB);
06348             ISAPNP_PNP_READ_PORT = new IO_ReadHandleObject;
06349             ISAPNP_PNP_READ_PORT->Install(ISA_PNP_WPORT,isapnp_read_port,IO_MB);
06350             LOG(LOG_MISC,LOG_DEBUG)("Registered ISA PnP read port at 0x%03x",ISA_PNP_WPORT);
06351         }
06352 
06353         if (enable_integration_device) {
06354             /* integration device callout */
06355             if (dosbox_int_iocallout == IO_Callout_t_none)
06356                 dosbox_int_iocallout = IO_AllocateCallout(IO_TYPE_MB);
06357             if (dosbox_int_iocallout == IO_Callout_t_none)
06358                 E_Exit("Failed to get dosbox integration IO callout handle");
06359 
06360             {
06361                 IO_CalloutObject *obj = IO_GetCallout(dosbox_int_iocallout);
06362                 if (obj == NULL) E_Exit("Failed to get dosbox integration IO callout");
06363                 obj->Install(0x28,IOMASK_Combine(IOMASK_FULL,IOMASK_Range(4)),dosbox_integration_cb_port_r,dosbox_integration_cb_port_w);
06364                 IO_PutCallout(obj);
06365             }
06366 
06367             /* DOSBox integration device */
06368             if (!IS_PC98_ARCH && isapnpigdevice == NULL && enable_integration_device_pnp) {
06369                 isapnpigdevice = new ISAPnPIntegrationDevice;
06370                 ISA_PNP_devreg(isapnpigdevice);
06371             }
06372         }
06373 
06374         // ISA Plug & Play BIOS entrypoint
06375         // NTS: Apparently, Windows 95, 98, and ME will re-enumerate and re-install PnP devices if our entry point changes it's address.
06376         if (!IS_PC98_ARCH && ISAPNPBIOS) {
06377             Bitu base;
06378             unsigned int i;
06379             unsigned char c,tmp[256];
06380 
06381             isapnp_biosstruct_base = base = ROMBIOS_GetMemory(0x21,"ISA Plug & Play BIOS struct",/*paragraph alignment*/0x10);
06382 
06383             if (base == 0) E_Exit("Unable to allocate ISA PnP struct");
06384             LOG_MSG("ISA Plug & Play BIOS enabled");
06385 
06386             call_pnp_r = CALLBACK_Allocate();
06387             call_pnp_rp = PNPentry_real = CALLBACK_RealPointer(call_pnp_r);
06388             CALLBACK_Setup(call_pnp_r,ISAPNP_Handler_RM,CB_RETF,"ISA Plug & Play entry point (real)");
06389             //LOG_MSG("real entry pt=%08lx\n",PNPentry_real);
06390 
06391             call_pnp_p = CALLBACK_Allocate();
06392             call_pnp_pp = PNPentry_prot = CALLBACK_RealPointer(call_pnp_p);
06393             CALLBACK_Setup(call_pnp_p,ISAPNP_Handler_PM,CB_RETF,"ISA Plug & Play entry point (protected)");
06394             //LOG_MSG("prot entry pt=%08lx\n",PNPentry_prot);
06395 
06396             phys_writeb(base+0,'$');
06397             phys_writeb(base+1,'P');
06398             phys_writeb(base+2,'n');
06399             phys_writeb(base+3,'P');
06400             phys_writeb(base+4,0x10);       /* Version:     1.0 */
06401             phys_writeb(base+5,0x21);       /* Length:      0x21 bytes */
06402             phys_writew(base+6,0x0000);     /* Control field:   Event notification not supported */
06403             /* skip checksum atm */
06404             phys_writed(base+9,0);          /* Event notify flag addr: (none) */
06405             phys_writed(base+0xD,call_pnp_rp);  /* Real-mode entry point */
06406             phys_writew(base+0x11,call_pnp_pp&0xFFFF); /* Protected mode offset */
06407             phys_writed(base+0x13,(call_pnp_pp >> 12) & 0xFFFF0); /* Protected mode code segment base */
06408             phys_writed(base+0x17,ISAPNP_ID('D','O','S',0,7,4,0));      /* OEM device identifier (DOSBox 0.740, get it?) */
06409             phys_writew(base+0x1B,0xF000);      /* real-mode data segment */
06410             phys_writed(base+0x1D,0xF0000);     /* protected mode data segment address */
06411             /* run checksum */
06412             c=0;
06413             for (i=0;i < 0x21;i++) {
06414                 if (i != 8) c += phys_readb(base+i);
06415             }
06416             phys_writeb(base+8,0x100-c);        /* checksum value: set so that summing bytes across the struct == 0 */
06417 
06418             /* input device (keyboard) */
06419             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Keyboard,sizeof(ISAPNP_sysdev_Keyboard),true))
06420                 LOG_MSG("ISAPNP register failed\n");
06421 
06422             /* input device (mouse) */
06423             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Mouse,sizeof(ISAPNP_sysdev_Mouse),true))
06424                 LOG_MSG("ISAPNP register failed\n");
06425 
06426             /* DMA controller */
06427             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_DMA_Controller,sizeof(ISAPNP_sysdev_DMA_Controller),true))
06428                 LOG_MSG("ISAPNP register failed\n");
06429 
06430             /* Interrupt controller */
06431             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PIC,sizeof(ISAPNP_sysdev_PIC),true))
06432                 LOG_MSG("ISAPNP register failed\n");
06433 
06434             /* Timer */
06435             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Timer,sizeof(ISAPNP_sysdev_Timer),true))
06436                 LOG_MSG("ISAPNP register failed\n");
06437 
06438             /* Realtime clock */
06439             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_RTC,sizeof(ISAPNP_sysdev_RTC),true))
06440                 LOG_MSG("ISAPNP register failed\n");
06441 
06442             /* PC speaker */
06443             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PC_Speaker,sizeof(ISAPNP_sysdev_PC_Speaker),true))
06444                 LOG_MSG("ISAPNP register failed\n");
06445 
06446             /* System board */
06447             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_System_Board,sizeof(ISAPNP_sysdev_System_Board),true))
06448                 LOG_MSG("ISAPNP register failed\n");
06449 
06450             /* Motherboard PNP resources and general */
06451             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_General_ISAPNP,sizeof(ISAPNP_sysdev_General_ISAPNP),true))
06452                 LOG_MSG("ISAPNP register failed\n");
06453 
06454             /* ISA bus, meaning, a computer with ISA slots.
06455              * The purpose of this device is to convince Windows 95 to automatically install it's
06456              * "ISA Plug and Play bus" so that PnP devices are recognized automatically */
06457             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_ISA_BUS,sizeof(ISAPNP_sysdev_ISA_BUS),true))
06458                 LOG_MSG("ISAPNP register failed\n");
06459 
06460             if (pcibus_enable) {
06461                 /* PCI bus, meaning, a computer with PCI slots.
06462                  * The purpose of this device is to tell Windows 95 that a PCI bus is present. Without
06463                  * this entry, PCI devices will not be recognized until you manually install the PCI driver. */
06464                 if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PCI_BUS,sizeof(ISAPNP_sysdev_PCI_BUS),true))
06465                     LOG_MSG("ISAPNP register failed\n");
06466             }
06467 
06468             /* APM BIOS device. To help Windows 95 see our APM BIOS. */
06469             if (APMBIOS && APMBIOS_pnp) {
06470                 LOG_MSG("Registering APM BIOS as ISA Plug & Play BIOS device node");
06471                 if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_APM_BIOS,sizeof(ISAPNP_sysdev_APM_BIOS),true))
06472                     LOG_MSG("ISAPNP register failed\n");
06473             }
06474 
06475 #if (C_FPU)
06476             /* Numeric Coprocessor */
06477             if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Numeric_Coprocessor,sizeof(ISAPNP_sysdev_Numeric_Coprocessor),true))
06478                 LOG_MSG("ISAPNP register failed\n");
06479 #endif
06480 
06481             /* RAM resources. we have to construct it */
06482             /* NTS: We don't do this here, but I have an old Toshiba laptop who's PnP BIOS uses
06483              *      this device ID to report both RAM and ROM regions. */
06484             {
06485                 Bitu max = MEM_TotalPages() * 4096;
06486                 const unsigned char h1[9] = {
06487                     ISAPNP_SYSDEV_HEADER(
06488                         ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x1), /* PNP0C01 System device, motherboard resources */
06489                         ISAPNP_TYPE(0x05,0x00,0x00),        /* type: Memory, RAM, general */
06490                         0x0001 | 0x0002)
06491                 };
06492 
06493                 i = 0;
06494                 memcpy(tmp+i,h1,9); i += 9;         /* can't disable, can't configure */
06495                 /*----------allocated--------*/
06496                 tmp[i+0] = 0x80 | 6;                /* 32-bit memory range */
06497                 tmp[i+1] = 9;                   /* length=9 */
06498                 tmp[i+2] = 0;
06499                 tmp[i+3] = 0x01;                /* writeable, no cache, 8-bit, not shadowable, not ROM */
06500                 host_writed(tmp+i+4,0x00000);           /* base */
06501                 host_writed(tmp+i+8,max > 0xA0000 ? 0xA0000 : 0x00000); /* length */
06502                 i += 9+3;
06503 
06504                 if (max > 0x100000) {
06505                     tmp[i+0] = 0x80 | 6;                /* 32-bit memory range */
06506                     tmp[i+1] = 9;                   /* length=9 */
06507                     tmp[i+2] = 0;
06508                     tmp[i+3] = 0x01;
06509                     host_writed(tmp+i+4,0x100000);          /* base */
06510                     host_writed(tmp+i+8,max-0x100000);      /* length */
06511                     i += 9+3;
06512                 }
06513 
06514                 tmp[i+0] = 0x79;                /* END TAG */
06515                 tmp[i+1] = 0x00;
06516                 i += 2;
06517                 /*-------------possible-----------*/
06518                 tmp[i+0] = 0x79;                /* END TAG */
06519                 tmp[i+1] = 0x00;
06520                 i += 2;
06521                 /*-------------compatible---------*/
06522                 tmp[i+0] = 0x79;                /* END TAG */
06523                 tmp[i+1] = 0x00;
06524                 i += 2;
06525 
06526                 if (!ISAPNP_RegisterSysDev(tmp,i))
06527                     LOG_MSG("ISAPNP register failed\n");
06528             }
06529 
06530             /* register parallel ports */
06531             for (Bitu portn=0;portn < 3;portn++) {
06532                 Bitu port = mem_readw(BIOS_ADDRESS_LPT1+(portn*2));
06533                 if (port != 0) {
06534                     const unsigned char h1[9] = {
06535                         ISAPNP_SYSDEV_HEADER(
06536                             ISAPNP_ID('P','N','P',0x0,0x4,0x0,0x0), /* PNP0400 Standard LPT printer port */
06537                             ISAPNP_TYPE(0x07,0x01,0x00),        /* type: General parallel port */
06538                             0x0001 | 0x0002)
06539                     };
06540 
06541                     i = 0;
06542                     memcpy(tmp+i,h1,9); i += 9;         /* can't disable, can't configure */
06543                     /*----------allocated--------*/
06544                     tmp[i+0] = (8 << 3) | 7;            /* IO resource */
06545                     tmp[i+1] = 0x01;                /* 16-bit decode */
06546                     host_writew(tmp+i+2,port);          /* min */
06547                     host_writew(tmp+i+4,port);          /* max */
06548                     tmp[i+6] = 0x10;                /* align */
06549                     tmp[i+7] = 0x03;                /* length */
06550                     i += 7+1;
06551 
06552                     /* TODO: If/when LPT emulation handles the IRQ, add IRQ resource here */
06553 
06554                     tmp[i+0] = 0x79;                /* END TAG */
06555                     tmp[i+1] = 0x00;
06556                     i += 2;
06557                     /*-------------possible-----------*/
06558                     tmp[i+0] = 0x79;                /* END TAG */
06559                     tmp[i+1] = 0x00;
06560                     i += 2;
06561                     /*-------------compatible---------*/
06562                     tmp[i+0] = 0x79;                /* END TAG */
06563                     tmp[i+1] = 0x00;
06564                     i += 2;
06565 
06566                     if (!ISAPNP_RegisterSysDev(tmp,i))
06567                         LOG_MSG("ISAPNP register failed\n");
06568                 }
06569             }
06570 
06571             void BIOS_Post_register_comports_PNP();
06572             BIOS_Post_register_comports_PNP();
06573 
06574             void BIOS_Post_register_IDE();
06575             BIOS_Post_register_IDE();
06576 
06577             void BIOS_Post_register_FDC();
06578             BIOS_Post_register_FDC();
06579         }
06580 
06581         if (IS_PC98_ARCH) {
06582             /* initialize IRQ0 timer to default tick interval.
06583              * PC-98 does not pre-initialize timer 0 of the PIT to 0xFFFF the way IBM PC/XT/AT do */
06584             PC98_Interval_Timer_Continue();
06585             PIC_SetIRQMask(0,true); /* PC-98 keeps the timer off unless INT 1Ch is called to set a timer interval */
06586         }
06587 
06588         CPU_STI();
06589 
06590         return CBRET_NONE;
06591     }
06592     CALLBACK_HandlerObject cb_bios_scan_video_bios;
06593     static Bitu cb_bios_scan_video_bios__func(void) {
06594         unsigned long size;
06595 
06596         /* NTS: As far as I can tell, video is integrated into the PC-98 BIOS and there is no separate BIOS */
06597         if (IS_PC98_ARCH) return CBRET_NONE;
06598 
06599         if (cpu.pmode) E_Exit("BIOS error: VIDEO BIOS SCAN function called while in protected/vm86 mode");
06600 
06601         if (!bios_has_exec_vga_bios) {
06602             bios_has_exec_vga_bios = true;
06603             if (IS_EGAVGA_ARCH) {
06604                 /* make sure VGA BIOS is there at 0xC000:0x0000 */
06605                 if (AdapterROM_Read(0xC0000,&size)) {
06606                     LOG(LOG_MISC,LOG_DEBUG)("BIOS VIDEO ROM SCAN found VGA BIOS (size=%lu)",size);
06607                     adapter_scan_start = 0xC0000 + size;
06608 
06609                     // step back into the callback instruction that triggered this call
06610                     reg_eip -= 4;
06611 
06612                     // FAR CALL into the VGA BIOS
06613                     CPU_CALL(false,0xC000,0x0003,reg_eip);
06614                     return CBRET_NONE;
06615                 }
06616                 else {
06617                     LOG(LOG_MISC,LOG_WARN)("BIOS VIDEO ROM SCAN did not find VGA BIOS");
06618                 }
06619             }
06620             else {
06621                 // CGA, MDA, Tandy, PCjr. No video BIOS to scan for
06622             }
06623         }
06624 
06625         return CBRET_NONE;
06626     }
06627     CALLBACK_HandlerObject cb_bios_adapter_rom_scan;
06628     static Bitu cb_bios_adapter_rom_scan__func(void) {
06629         unsigned long size;
06630         Bit32u c1;
06631 
06632         /* FIXME: I have no documentation on how PC-98 scans for adapter ROM or even if it supports it */
06633         if (IS_PC98_ARCH) return CBRET_NONE;
06634 
06635         if (cpu.pmode) E_Exit("BIOS error: ADAPTER ROM function called while in protected/vm86 mode");
06636 
06637         while (adapter_scan_start < 0xF0000) {
06638             if (AdapterROM_Read(adapter_scan_start,&size)) {
06639                 Bit16u segm = (Bit16u)(adapter_scan_start >> 4);
06640 
06641                 LOG(LOG_MISC,LOG_DEBUG)("BIOS ADAPTER ROM scan found ROM at 0x%lx (size=%lu)",(unsigned long)adapter_scan_start,size);
06642 
06643                 c1 = mem_readd(adapter_scan_start+3);
06644                 adapter_scan_start += size;
06645                 if (c1 != 0UL) {
06646                     LOG(LOG_MISC,LOG_DEBUG)("Running ADAPTER ROM entry point");
06647 
06648                     // step back into the callback instruction that triggered this call
06649                     reg_eip -= 4;
06650 
06651                     // FAR CALL into the VGA BIOS
06652                     CPU_CALL(false,segm,0x0003,reg_eip);
06653                     return CBRET_NONE;
06654                 }
06655                 else {
06656                     LOG(LOG_MISC,LOG_DEBUG)("FIXME: ADAPTER ROM entry point does not exist");
06657                 }
06658             }
06659             else {
06660                 if (IS_EGAVGA_ARCH) // supposedly newer systems only scan on 2KB boundaries by standard? right?
06661                     adapter_scan_start = (adapter_scan_start | 2047UL) + 1UL;
06662                 else // while older PC/XT systems scanned on 512-byte boundaries? right?
06663                     adapter_scan_start = (adapter_scan_start | 511UL) + 1UL;
06664             }
06665         }
06666 
06667         LOG(LOG_MISC,LOG_DEBUG)("BIOS ADAPTER ROM scan complete");
06668         return CBRET_NONE;
06669     }
06670     CALLBACK_HandlerObject cb_bios_startup_screen;
06671     static Bitu cb_bios_startup_screen__func(void) {
06672         const char *msg = PACKAGE_STRING " (C) 2002-" COPYRIGHT_END_YEAR " The DOSBox Team\nA fork of DOSBox 0.74 by TheGreatCodeholio\nFor more info visit http://dosbox-x.com\nBased on DOSBox (http://dosbox.com)\n\n";
06673         int logo_x,logo_y,x,y,rowheight=8;
06674 
06675         y = 2;
06676         x = 2;
06677         logo_y = 2;
06678         logo_x = 80 - 2 - (224/8);
06679 
06680         if (cpu.pmode) E_Exit("BIOS error: STARTUP function called while in protected/vm86 mode");
06681 
06682         // TODO: For those who would rather not use the VGA graphical modes, add a configuration option to "disable graphical splash".
06683         //       We would then revert to a plain text copyright and status message here (and maybe an ASCII art version of the DOSBox logo).
06684         if (IS_VGA_ARCH) {
06685             rowheight = 16;
06686             reg_eax = 18;       // 640x480 16-color
06687             CALLBACK_RunRealInt(0x10);
06688             DrawDOSBoxLogoVGA((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight);
06689         }
06690         else if (machine == MCH_EGA) {
06691             rowheight = 14;
06692             reg_eax = 16;       // 640x350 16-color
06693             CALLBACK_RunRealInt(0x10);
06694 
06695             // color correction: change Dark Puke Yellow to brown
06696             IO_Read(0x3DA); IO_Read(0x3BA);
06697             IO_Write(0x3C0,0x06);
06698             IO_Write(0x3C0,0x14); // red=1 green=1 blue=0
06699             IO_Read(0x3DA); IO_Read(0x3BA);
06700             IO_Write(0x3C0,0x20);
06701 
06702             DrawDOSBoxLogoVGA((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight);
06703         }
06704         else if (machine == MCH_CGA || machine == MCH_MCGA || machine == MCH_PCJR || machine == MCH_AMSTRAD || machine == MCH_TANDY) {
06705             rowheight = 8;
06706             reg_eax = 6;        // 640x200 2-color
06707             CALLBACK_RunRealInt(0x10);
06708 
06709             DrawDOSBoxLogoCGA6((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight);
06710         }
06711         else if (machine == MCH_PC98) {
06712             // clear the graphics layer
06713             for (unsigned int i=0;i < (80*400);i++) {
06714                 mem_writeb(0xA8000+i,0);        // B
06715                 mem_writeb(0xB0000+i,0);        // G
06716                 mem_writeb(0xB8000+i,0);        // R
06717                 mem_writeb(0xE0000+i,0);        // E
06718             }
06719 
06720             reg_eax = 0x0C00;   // enable text layer (PC-98)
06721             CALLBACK_RunRealInt(0x18);
06722 
06723             reg_eax = 0x1100;   // show cursor (PC-98)
06724             CALLBACK_RunRealInt(0x18);
06725 
06726             reg_eax = 0x1300;   // set cursor pos (PC-98)
06727             reg_edx = 0x0000;   // byte position
06728             CALLBACK_RunRealInt(0x18);
06729 
06730             bios_pc98_posx = x;
06731 
06732             reg_eax = 0x4200;   // setup 640x400 graphics
06733             reg_ecx = 0xC000;
06734             CALLBACK_RunRealInt(0x18);
06735 
06736             // enable the 4th bitplane, for 16-color analog graphics mode.
06737             // TODO: When we allow the user to emulate only the 8-color BGR digital mode,
06738             //       logo drawing should use an alternate drawing method.
06739             IO_Write(0x6A,0x01);    // enable 16-color analog mode (this makes the 4th bitplane appear)
06740             IO_Write(0x6A,0x04);    // but we don't need the EGC graphics
06741             // If we caught a game mid-page flip, set the display and VRAM pages back to zero
06742             IO_Write(0xA4,0x00);    // display page 0
06743             IO_Write(0xA6,0x00);    // write to page 0
06744 
06745             // program a VGA-like color palette so we can re-use the VGA logo
06746             for (unsigned int i=0;i < 16;i++) {
06747                 unsigned int bias = (i & 8) ? 0x5 : 0x0;
06748 
06749                 IO_Write(0xA8,i);   // DAC index
06750                 if (i != 6) {
06751                     IO_Write(0xAA,((i & 2) ? 0xA : 0x0) + bias);    // green
06752                     IO_Write(0xAC,((i & 4) ? 0xA : 0x0) + bias);    // red
06753                     IO_Write(0xAE,((i & 1) ? 0xA : 0x0) + bias);    // blue
06754                 }
06755                 else { // brown #6 instead of puke yellow
06756                     IO_Write(0xAA,((i & 2) ? 0x5 : 0x0) + bias);    // green
06757                     IO_Write(0xAC,((i & 4) ? 0xA : 0x0) + bias);    // red
06758                     IO_Write(0xAE,((i & 1) ? 0xA : 0x0) + bias);    // blue
06759                 }
06760             }
06761 
06762             DrawDOSBoxLogoPC98((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight);
06763 
06764             reg_eax = 0x4000;   // show the graphics layer (PC-98) so we can render the DOSBox logo
06765             CALLBACK_RunRealInt(0x18);
06766         }
06767         else {
06768             reg_eax = 3;        // 80x25 text
06769             CALLBACK_RunRealInt(0x10);
06770 
06771             // TODO: For CGA, PCjr, and Tandy, we could render a 4-color CGA version of the same logo.
06772             //       And for MDA/Hercules, we could render a monochromatic ASCII art version.
06773         }
06774 
06775         if (machine != MCH_PC98) {
06776             reg_eax = 0x0200;   // set cursor pos
06777             reg_ebx = 0;        // page zero
06778             reg_dh = y;     // row 4
06779             reg_dl = x;     // column 20
06780             CALLBACK_RunRealInt(0x10);
06781         }
06782 
06783         BIOS_Int10RightJustifiedPrint(x,y,msg);
06784 
06785         {
06786             uint64_t sz = (uint64_t)MEM_TotalPages() * (uint64_t)4096;
06787             char tmp[512];
06788 
06789             if (sz >= ((uint64_t)128 << (uint64_t)20))
06790                 sprintf(tmp,"%uMB memory installed\r\n",(unsigned int)(sz >> (uint64_t)20));
06791             else
06792                 sprintf(tmp,"%uKB memory installed\r\n",(unsigned int)(sz >> (uint64_t)10));
06793 
06794             BIOS_Int10RightJustifiedPrint(x,y,tmp);
06795         }
06796 
06797         {
06798             char tmp[512];
06799             const char *card = "?";
06800 
06801             switch (machine) {
06802                 case MCH_CGA:
06803                     card = "IBM Color Graphics Adapter";
06804                     break;
06805                 case MCH_MCGA:
06806                     card = "IBM Multi Color Graphics Adapter";
06807                     break;
06808                 case MCH_MDA:
06809                     card = "IBM Monochrome Display Adapter";
06810                     break;
06811                 case MCH_HERC:
06812                     card = "IBM Monochrome Display Adapter (Hercules)";
06813                     break;
06814                 case MCH_EGA:
06815                     card = "IBM Enhanced Graphics Adapter";
06816                     break;
06817                 case MCH_PCJR:
06818                     card = "PCjr graohics adapter";
06819                     break;
06820                 case MCH_TANDY:
06821                     card = "Tandy graohics adapter";
06822                     break;
06823                 case MCH_VGA:
06824                     switch (svgaCard) {
06825                         case SVGA_TsengET4K:
06826                             card = "Tseng ET4000 SVGA";
06827                             break;
06828                         case SVGA_TsengET3K:
06829                             card = "Tseng ET3000 SVGA";
06830                             break;
06831                         case SVGA_ParadisePVGA1A:
06832                             card = "Paradise SVGA";
06833                             break;
06834                         case SVGA_S3Trio:
06835                             card = "S3 Trio SVGA";
06836                             break;
06837                         default:
06838                             card = "Standard VGA";
06839                             break;
06840                     }
06841 
06842                     break;
06843                 case MCH_PC98:
06844                     card = "PC98 graphics";
06845                     break;
06846                 case MCH_AMSTRAD:
06847                     card = "Amstrad graphics";
06848                     break;
06849                 default:
06850                     abort(); // should not happen
06851                     break;
06852             };
06853 
06854             sprintf(tmp,"Video card is %s\n",card);
06855             BIOS_Int10RightJustifiedPrint(x,y,tmp);
06856         }
06857 
06858         {
06859             char tmp[512];
06860             const char *cpu = "?";
06861 
06862             switch (CPU_ArchitectureType) {
06863                 case CPU_ARCHTYPE_8086:
06864                     cpu = "8086";
06865                     break;
06866                 case CPU_ARCHTYPE_80186:
06867                     cpu = "80186";
06868                     break;
06869                 case CPU_ARCHTYPE_286:
06870                     cpu = "286";
06871                     break;
06872                 case CPU_ARCHTYPE_386:
06873                     cpu = "386";
06874                     break;
06875                 case CPU_ARCHTYPE_486OLD:
06876                     cpu = "486 (older generation)";
06877                     break;
06878                 case CPU_ARCHTYPE_486NEW:
06879                     cpu = "486 (later generation)";
06880                     break;
06881                 case CPU_ARCHTYPE_PENTIUM:
06882                     cpu = "Pentium";
06883                     break;
06884                 case CPU_ARCHTYPE_P55CSLOW:
06885                     cpu = "Pentium MMX";
06886                     break;
06887             };
06888 
06889             extern bool enable_fpu;
06890 
06891             sprintf(tmp,"%s CPU present",cpu);
06892             BIOS_Int10RightJustifiedPrint(x,y,tmp);
06893             if (enable_fpu) BIOS_Int10RightJustifiedPrint(x,y," with FPU");
06894             BIOS_Int10RightJustifiedPrint(x,y,"\n");
06895         }
06896 
06897         if (APMBIOS) {
06898             BIOS_Int10RightJustifiedPrint(x,y,"Advanced Power Management interface active\n");
06899         }
06900 
06901         if (ISAPNPBIOS) {
06902             BIOS_Int10RightJustifiedPrint(x,y,"ISA Plug & Play BIOS active\n");
06903         }
06904 
06905 #if !defined(C_EMSCRIPTEN)
06906         BIOS_Int10RightJustifiedPrint(x,y,"\nHit SPACEBAR to pause at this screen\n");
06907         y--; /* next message should overprint */
06908         if (machine != MCH_PC98) {
06909             reg_eax = 0x0200;   // set cursor pos
06910             reg_ebx = 0;        // page zero
06911             reg_dh = y;     // row 4
06912             reg_dl = x;     // column 20
06913             CALLBACK_RunRealInt(0x10);
06914         }
06915         else {
06916             reg_eax = 0x1300u;   // set cursor pos (PC-98)
06917             reg_edx = (((unsigned int)y * 80u) + (unsigned int)x) * 2u; // byte position
06918             CALLBACK_RunRealInt(0x18);
06919         }
06920 #endif
06921 
06922         // TODO: Then at this screen, we can print messages demonstrating the detection of
06923         //       IDE devices, floppy, ISA PnP initialization, anything of importance.
06924         //       I also envision adding the ability to hit DEL or F2 at this point to enter
06925         //       a "BIOS setup" screen where all DOSBox configuration options can be
06926         //       modified, with the same look and feel of an old BIOS.
06927 
06928 #if C_EMSCRIPTEN
06929         Bit32u lasttick=GetTicks();
06930         while ((GetTicks()-lasttick)<1000) {
06931             void CALLBACK_Idle(void);
06932             CALLBACK_Idle();
06933             emscripten_sleep_with_yield(100);
06934         }
06935 #else
06936         if (!control->opt_fastbioslogo) {
06937             bool wait_for_user = false;
06938             Bit32u lasttick=GetTicks();
06939             while ((GetTicks()-lasttick)<1000) {
06940                 if (machine == MCH_PC98) {
06941                     reg_eax = 0x0100;   // sense key
06942                     CALLBACK_RunRealInt(0x18);
06943                     SETFLAGBIT(ZF,reg_bh == 0);
06944                 }
06945                 else {
06946                     reg_eax = 0x0100;
06947                     CALLBACK_RunRealInt(0x16);
06948                 }
06949 
06950                 if (!GETFLAG(ZF)) {
06951                     if (machine == MCH_PC98) {
06952                         reg_eax = 0x0000;   // read key
06953                         CALLBACK_RunRealInt(0x18);
06954                     }
06955                     else {
06956                         reg_eax = 0x0000;
06957                         CALLBACK_RunRealInt(0x16);
06958                     }
06959 
06960                     if (reg_al == 32) { // user hit space
06961                         BIOS_Int10RightJustifiedPrint(x,y,"Hit ENTER or ESC to continue                    \n"); // overprint
06962                         wait_for_user = true;
06963                         break;
06964                     }
06965                 }
06966             }
06967 
06968             while (wait_for_user) {
06969                 if (machine == MCH_PC98) {
06970                     reg_eax = 0x0000;   // read key
06971                     CALLBACK_RunRealInt(0x18);
06972                 }
06973                 else {
06974                     reg_eax = 0x0000;
06975                     CALLBACK_RunRealInt(0x16);
06976                 }
06977 
06978                 if (reg_al == 27/*ESC*/ || reg_al == 13/*ENTER*/)
06979                     break;
06980             }
06981         }
06982 #endif
06983 
06984         if (machine == MCH_PC98) {
06985             reg_eax = 0x4100;   // hide the graphics layer (PC-98)
06986             CALLBACK_RunRealInt(0x18);
06987 
06988             // clear the graphics layer
06989             for (unsigned int i=0;i < (80*400);i++) {
06990                 mem_writeb(0xA8000+i,0);        // B
06991                 mem_writeb(0xB0000+i,0);        // G
06992                 mem_writeb(0xB8000+i,0);        // R
06993                 mem_writeb(0xE0000+i,0);        // E
06994             }
06995 
06996             IO_Write(0x6A,0x00);    // switch back to 8-color mode
06997         }
06998         else {
06999             // restore 80x25 text mode
07000             reg_eax = 3;
07001             CALLBACK_RunRealInt(0x10);
07002         }
07003 
07004         return CBRET_NONE;
07005     }
07006     CALLBACK_HandlerObject cb_bios_boot;
07007     CALLBACK_HandlerObject cb_bios_bootfail;
07008     CALLBACK_HandlerObject cb_pc98_rombasic;
07009     static Bitu cb_pc98_entry__func(void) {
07010         /* the purpose of this function is to say "N88 ROM BASIC NOT FOUND" */
07011         int x,y;
07012 
07013         x = y = 0;
07014 
07015         /* PC-98 MS-DOS boot sector may RETF back to the BIOS, and this is where execution ends up */
07016         BIOS_Int10RightJustifiedPrint(x,y,"N88 ROM BASIC NOT IMPLEMENTED");
07017 
07018         return CBRET_NONE;
07019     }
07020     static Bitu cb_bios_bootfail__func(void) {
07021         int x,y;
07022 
07023         x = y = 0;
07024 
07025         /* PC-98 MS-DOS boot sector may RETF back to the BIOS, and this is where execution ends up */
07026         BIOS_Int10RightJustifiedPrint(x,y,"Guest OS failed to boot, returned failure");
07027 
07028         /* and then after this call, there is a JMP $ to loop endlessly */
07029         return CBRET_NONE;
07030     }
07031     static Bitu cb_bios_boot__func(void) {
07032         /* Reset/power-on overrides the user's A20 gate preferences.
07033          * It's time to revert back to what the user wants. */
07034         void A20Gate_TakeUserSetting(Section *sec);
07035         void MEM_A20_Enable(bool enabled);
07036         A20Gate_TakeUserSetting(NULL);
07037         MEM_A20_Enable(false);
07038 
07039         if (cpu.pmode) E_Exit("BIOS error: BOOT function called while in protected/vm86 mode");
07040         DispatchVMEvent(VM_EVENT_BIOS_BOOT);
07041 
07042         // TODO: If instructed to, follow the INT 19h boot pattern, perhaps follow the BIOS Boot Specification, etc.
07043 
07044         // TODO: If instructed to boot a guest OS...
07045 
07046         /* wipe out the stack so it's not there to interfere with the system */
07047         reg_esp = 0;
07048         reg_eip = 0;
07049         CPU_SetSegGeneral(cs, 0x60);
07050         CPU_SetSegGeneral(ss, 0x60);
07051 
07052         for (Bitu i=0;i < 0x400;i++) mem_writeb(0x7C00+i,0);
07053 
07054         // Begin booting the DOSBox shell. NOTE: VM_Boot_DOSBox_Kernel will change CS:IP instruction pointer!
07055         if (!VM_Boot_DOSBox_Kernel()) E_Exit("BIOS error: BOOT function failed to boot DOSBox kernel");
07056         return CBRET_NONE;
07057     }
07058 public:
07059     void write_FFFF_signature() {
07060         /* write the signature at 0xF000:0xFFF0 */
07061 
07062         // The farjump at the processor reset entry point (jumps to POST routine)
07063         phys_writeb(0xffff0,0xEA);                  // FARJMP
07064         phys_writew(0xffff1,RealOff(BIOS_DEFAULT_RESET_LOCATION));  // offset
07065         phys_writew(0xffff3,RealSeg(BIOS_DEFAULT_RESET_LOCATION));  // segment
07066 
07067         // write system BIOS date
07068         for(Bitu i = 0; i < strlen(bios_date_string); i++) phys_writeb(0xffff5+i,(Bit8u)bios_date_string[i]);
07069 
07070         /* model byte */
07071         if (machine==MCH_TANDY || machine==MCH_AMSTRAD) phys_writeb(0xffffe,0xff);  /* Tandy model */
07072         else if (machine==MCH_PCJR) phys_writeb(0xffffe,0xfd);  /* PCJr model */
07073         else if (machine==MCH_MCGA) phys_writeb(0xffffe,0xfa);  /* PC/2 model 30 model */
07074         else phys_writeb(0xffffe,0xfc); /* PC (FIXME: This is listed as model byte PS/2 model 60) */
07075 
07076         // signature
07077         phys_writeb(0xfffff,0x55);
07078     }
07079     BIOS(Section* configuration):Module_base(configuration){
07080         /* tandy DAC can be requested in tandy_sound.cpp by initializing this field */
07081         Bitu wo;
07082 
07083         isapnp_biosstruct_base = 0;
07084 
07085         { // TODO: Eventually, move this to BIOS POST or init phase
07086             Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
07087 
07088             bochs_port_e9 = section->Get_bool("bochs debug port e9");
07089 
07090             // TODO: motherboard init, especially when we get around to full Intel Triton/i440FX chipset emulation
07091             isa_memory_hole_512kb = section->Get_bool("isa memory hole at 512kb");
07092 
07093             // FIXME: Erm, well this couldv'e been named better. It refers to the amount of conventional memory
07094             //        made available to the operating system below 1MB, which is usually DOS.
07095             dos_conventional_limit = (unsigned int)section->Get_int("dos mem limit");
07096 
07097             // for PC-98: When accessing the floppy through INT 1Bh, when enabled, run through a waiting loop to make sure
07098             //     the timer count is not too high on exit (Ys II)
07099             enable_fdc_timer_hack = section->Get_bool("pc-98 int 1b fdc timer wait");
07100 
07101             {
07102                 std::string s = section->Get_string("unhandled irq handler");
07103 
07104                 if (s == "simple")
07105                     unhandled_irq_method = UNHANDLED_IRQ_SIMPLE;
07106                 else if (s == "cooperative_2nd")
07107                     unhandled_irq_method = UNHANDLED_IRQ_COOPERATIVE_2ND;
07108                 // pick default
07109                 else if (IS_PC98_ARCH)
07110                     unhandled_irq_method = UNHANDLED_IRQ_COOPERATIVE_2ND;
07111                 else
07112                     unhandled_irq_method = UNHANDLED_IRQ_SIMPLE;
07113             }
07114         }
07115 
07116         if (bochs_port_e9) {
07117             if (BOCHS_PORT_E9 == NULL) {
07118                 BOCHS_PORT_E9 = new IO_WriteHandleObject;
07119                 BOCHS_PORT_E9->Install(0xE9,bochs_port_e9_write,IO_MB);
07120             }
07121             LOG(LOG_MISC,LOG_DEBUG)("Bochs port E9h emulation is active");
07122         }
07123         else {
07124             if (BOCHS_PORT_E9 != NULL) {
07125                 delete BOCHS_PORT_E9;
07126                 BOCHS_PORT_E9 = NULL;
07127             }
07128         }
07129 
07130         /* pick locations */
07131         BIOS_DEFAULT_RESET_LOCATION = PhysToReal416(ROMBIOS_GetMemory(64/*several callbacks*/,"BIOS default reset location",/*align*/4));
07132         BIOS_DEFAULT_HANDLER_LOCATION = PhysToReal416(ROMBIOS_GetMemory(1/*IRET*/,"BIOS default handler location",/*align*/4));
07133         BIOS_DEFAULT_IRQ0_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x13/*see callback.cpp for IRQ0*/,"BIOS default IRQ0 location",/*align*/4));
07134         BIOS_DEFAULT_IRQ1_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x15/*see callback.cpp for IRQ1*/,"BIOS default IRQ1 location",/*align*/4));
07135         BIOS_DEFAULT_IRQ07_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(7/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ2-7 location",/*align*/4));
07136         BIOS_DEFAULT_IRQ815_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(9/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ8-15 location",/*align*/4));
07137 
07138         write_FFFF_signature();
07139 
07140         /* Setup all the interrupt handlers the bios controls */
07141 
07142         /* INT 8 Clock IRQ Handler */
07143         call_irq0=CALLBACK_Allocate();
07144         if (IS_PC98_ARCH)
07145             CALLBACK_Setup(call_irq0,INT8_PC98_Handler,CB_IRET,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock");
07146         else
07147             CALLBACK_Setup(call_irq0,INT8_Handler,CB_IRQ0,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock");
07148 
07149         /* INT 11 Get equipment list */
07150         callback[1].Install(&INT11_Handler,CB_IRET,"Int 11 Equipment");
07151 
07152         /* INT 12 Memory Size default at 640 kb */
07153         callback[2].Install(&INT12_Handler,CB_IRET,"Int 12 Memory");
07154 
07155         ulimit = 640;
07156         t_conv = MEM_TotalPages() << 2; /* convert 4096/byte pages -> 1024/byte KB units */
07157         if (allow_more_than_640kb) {
07158             if (machine == MCH_CGA)
07159                 ulimit = 736;       /* 640KB + 64KB + 32KB  0x00000-0xB7FFF */
07160             else if (machine == MCH_HERC || machine == MCH_MDA)
07161                 ulimit = 704;       /* 640KB + 64KB = 0x00000-0xAFFFF */
07162 
07163             if (t_conv > ulimit) t_conv = ulimit;
07164             if (t_conv > 640) { /* because the memory emulation has already set things up */
07165                 bool MEM_map_RAM_physmem(Bitu start,Bitu end);
07166                 MEM_map_RAM_physmem(0xA0000,(t_conv<<10)-1);
07167                 memset(GetMemBase()+(640<<10),0,(t_conv-640)<<10);
07168             }
07169         }
07170         else {
07171             if (t_conv > 640) t_conv = 640;
07172         }
07173 
07174         if (IS_TANDY_ARCH) {
07175             /* reduce reported memory size for the Tandy (32k graphics memory
07176                at the end of the conventional 640k) */
07177             if (machine==MCH_TANDY && t_conv > 624) t_conv = 624;
07178         }
07179 
07180         /* allow user to further limit the available memory below 1MB */
07181         if (dos_conventional_limit != 0 && t_conv > dos_conventional_limit)
07182             t_conv = dos_conventional_limit;
07183 
07184         // TODO: Allow dosbox.conf to specify an option to add an EBDA (Extended BIOS Data Area)
07185         //       at the top of the DOS conventional limit, which we then reduce further to hold
07186         //       it. Most BIOSes past 1992 or so allocate an EBDA.
07187 
07188         /* if requested to emulate an ISA memory hole at 512KB, further limit the memory */
07189         if (isa_memory_hole_512kb && t_conv > 512) t_conv = 512;
07190 
07191         /* and then unmap RAM between t_conv and ulimit */
07192         if (t_conv < ulimit) {
07193             Bitu start = (t_conv+3)/4;  /* start = 1KB to page round up */
07194             Bitu end = ulimit/4;        /* end = 1KB to page round down */
07195             if (start < end) MEM_ResetPageHandler_Unmapped(start,end-start);
07196         }
07197 
07198         /* INT 4B. Now we can safely signal error instead of printing "Invalid interrupt 4B"
07199          * whenever we install Windows 95. Note that Windows 95 would call INT 4Bh because
07200          * that is where the Virtual DMA API lies in relation to EMM386.EXE */
07201         int4b_callback.Install(&INT4B_Handler,CB_IRET,"INT 4B");
07202 
07203         /* INT 14 Serial Ports */
07204         callback[3].Install(&INT14_Handler,CB_IRET_STI,"Int 14 COM-port");
07205 
07206         /* INT 15 Misc Calls */
07207         callback[4].Install(&INT15_Handler,CB_IRET,"Int 15 Bios");
07208 
07209         /* INT 17 Printer Routines */
07210         callback[5].Install(&INT17_Handler,CB_IRET_STI,"Int 17 Printer");
07211 
07212         /* INT 1A TIME and some other functions */
07213         callback[6].Install(&INT1A_Handler,CB_IRET_STI,"Int 1a Time");
07214 
07215         /* INT 1C System Timer tick called from INT 8 */
07216         callback[7].Install(&INT1C_Handler,CB_IRET,"Int 1c Timer");
07217         
07218         /* IRQ 8 RTC Handler */
07219         callback[8].Install(&INT70_Handler,CB_IRET,"Int 70 RTC");
07220 
07221         /* Irq 9 rerouted to irq 2 */
07222         callback[9].Install(NULL,CB_IRQ9,"irq 9 bios");
07223 
07224         // INT 19h: Boot function
07225         callback[10].Install(&INT19_Handler,CB_IRET,"int 19");
07226 
07227         // INT 76h: IDE IRQ 14
07228         // This is just a dummy IRQ handler to prevent crashes when
07229         // IDE emulation fires the IRQ and OS's like Win95 expect
07230         // the BIOS to handle the interrupt.
07231         // FIXME: Shouldn't the IRQ send an ACK to the PIC as well?!?
07232         callback[11].Install(&IRQ14_Dummy,CB_IRET_EOI_PIC2,"irq 14 ide");
07233 
07234         // INT 77h: IDE IRQ 15
07235         // This is just a dummy IRQ handler to prevent crashes when
07236         // IDE emulation fires the IRQ and OS's like Win95 expect
07237         // the BIOS to handle the interrupt.
07238         // FIXME: Shouldn't the IRQ send an ACK to the PIC as well?!?
07239         callback[12].Install(&IRQ15_Dummy,CB_IRET_EOI_PIC2,"irq 15 ide");
07240 
07241         // INT 0Eh: IDE IRQ 6
07242         callback[13].Install(&IRQ15_Dummy,CB_IRET_EOI_PIC1,"irq 6 floppy");
07243 
07244         // INT 18h: Enter BASIC
07245         // Non-IBM BIOS would display "NO ROM BASIC" here
07246         callback[15].Install(&INT18_Handler,CB_IRET,"int 18");
07247 
07248         init_vm86_fake_io();
07249 
07250         /* Irq 2-7 */
07251         call_irq07default=CALLBACK_Allocate();
07252 
07253         /* Irq 8-15 */
07254         call_irq815default=CALLBACK_Allocate();
07255 
07256         /* BIOS boot stages */
07257         cb_bios_post.Install(&cb_bios_post__func,CB_RETF,"BIOS POST");
07258         cb_bios_scan_video_bios.Install(&cb_bios_scan_video_bios__func,CB_RETF,"BIOS Scan Video BIOS");
07259         cb_bios_adapter_rom_scan.Install(&cb_bios_adapter_rom_scan__func,CB_RETF,"BIOS Adapter ROM scan");
07260         cb_bios_startup_screen.Install(&cb_bios_startup_screen__func,CB_RETF,"BIOS Startup screen");
07261         cb_bios_boot.Install(&cb_bios_boot__func,CB_RETF,"BIOS BOOT");
07262         cb_bios_bootfail.Install(&cb_bios_bootfail__func,CB_RETF,"BIOS BOOT FAIL");
07263         cb_pc98_rombasic.Install(&cb_pc98_entry__func,CB_RETF,"N88 ROM BASIC");
07264 
07265         // Compatible POST routine location: jump to the callback
07266         {
07267             Bitu wo_fence;
07268 
07269             wo = Real2Phys(BIOS_DEFAULT_RESET_LOCATION);
07270             wo_fence = wo + 64;
07271 
07272             // POST
07273             phys_writeb(wo+0x00,(Bit8u)0xFE);                       //GRP 4
07274             phys_writeb(wo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07275             phys_writew(wo+0x02,(Bit16u)cb_bios_post.Get_callback());           //The immediate word
07276             wo += 4;
07277 
07278             // video bios scan
07279             phys_writeb(wo+0x00,(Bit8u)0xFE);                       //GRP 4
07280             phys_writeb(wo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07281             phys_writew(wo+0x02,(Bit16u)cb_bios_scan_video_bios.Get_callback());        //The immediate word
07282             wo += 4;
07283 
07284             // adapter ROM scan
07285             phys_writeb(wo+0x00,(Bit8u)0xFE);                       //GRP 4
07286             phys_writeb(wo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07287             phys_writew(wo+0x02,(Bit16u)cb_bios_adapter_rom_scan.Get_callback());       //The immediate word
07288             wo += 4;
07289 
07290             // startup screen
07291             phys_writeb(wo+0x00,(Bit8u)0xFE);                       //GRP 4
07292             phys_writeb(wo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07293             phys_writew(wo+0x02,(Bit16u)cb_bios_startup_screen.Get_callback());     //The immediate word
07294             wo += 4;
07295 
07296             // user boot hook
07297             if (bios_user_boot_hook != 0) {
07298                 phys_writeb(wo+0x00,0x9C);                          //PUSHF
07299                 phys_writeb(wo+0x01,0x9A);                          //CALL FAR
07300                 phys_writew(wo+0x02,0x0000);                        //seg:0
07301                 phys_writew(wo+0x04,bios_user_boot_hook>>4);
07302                 wo += 6;
07303             }
07304 
07305             // boot
07306             BIOS_boot_code_offset = wo;
07307             phys_writeb(wo+0x00,(Bit8u)0xFE);                       //GRP 4
07308             phys_writeb(wo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07309             phys_writew(wo+0x02,(Bit16u)cb_bios_boot.Get_callback());           //The immediate word
07310             wo += 4;
07311 
07312             // boot fail
07313             BIOS_bootfail_code_offset = wo;
07314             phys_writeb(wo+0x00,(Bit8u)0xFE);                       //GRP 4
07315             phys_writeb(wo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07316             phys_writew(wo+0x02,(Bit16u)cb_bios_bootfail.Get_callback());           //The immediate word
07317             wo += 4;
07318 
07319             /* fence */
07320             phys_writeb(wo++,0xEB);                             // JMP $-2
07321             phys_writeb(wo++,0xFE);
07322 
07323             if (wo > wo_fence) E_Exit("BIOS boot callback overrun");
07324 
07325             if (IS_PC98_ARCH) {
07326                 /* Boot disks that run N88 basic, stopgap */
07327                 PhysPt bo = 0xE8002; // E800:0002
07328 
07329                 phys_writeb(bo+0x00,(Bit8u)0xFE);                       //GRP 4
07330                 phys_writeb(bo+0x01,(Bit8u)0x38);                       //Extra Callback instruction
07331                 phys_writew(bo+0x02,(Bit16u)cb_pc98_rombasic.Get_callback());           //The immediate word
07332 
07333                 phys_writeb(bo+0x04,0xEB);                             // JMP $-2
07334                 phys_writeb(bo+0x05,0xFE);
07335             }
07336         }
07337     }
07338     ~BIOS(){
07339         /* snap the CPU back to real mode. this code thinks in terms of 16-bit real mode
07340          * and if allowed to do it's thing in a 32-bit guest OS like WinNT, will trigger
07341          * a page fault. */
07342         CPU_Snap_Back_To_Real_Mode();
07343 
07344         if (BOCHS_PORT_E9) {
07345             delete BOCHS_PORT_E9;
07346             BOCHS_PORT_E9=NULL;
07347         }
07348         if (ISAPNP_PNP_ADDRESS_PORT) {
07349             delete ISAPNP_PNP_ADDRESS_PORT;
07350             ISAPNP_PNP_ADDRESS_PORT=NULL;
07351         }
07352         if (ISAPNP_PNP_DATA_PORT) {
07353             delete ISAPNP_PNP_DATA_PORT;
07354             ISAPNP_PNP_DATA_PORT=NULL;
07355         }
07356         if (ISAPNP_PNP_READ_PORT) {
07357             delete ISAPNP_PNP_READ_PORT;
07358             ISAPNP_PNP_READ_PORT=NULL;
07359         }
07360         if (isapnpigdevice) {
07361             /* ISA PnP will auto-free it */
07362             isapnpigdevice=NULL;
07363         }
07364 
07365         if (dosbox_int_iocallout != IO_Callout_t_none) {
07366             IO_FreeCallout(dosbox_int_iocallout);
07367             dosbox_int_iocallout = IO_Callout_t_none;
07368         }
07369 
07370         /* abort DAC playing */
07371         if (tandy_sb.port) {
07372             IO_Write(tandy_sb.port+0xcu,0xd3u);
07373             IO_Write(tandy_sb.port+0xcu,0xd0u);
07374         }
07375         real_writeb(0x40,0xd4,0x00);
07376         if (tandy_DAC_callback[0]) {
07377             Bit32u orig_vector=real_readd(0x40,0xd6);
07378             if (orig_vector==tandy_DAC_callback[0]->Get_RealPointer()) {
07379                 /* set IRQ vector to old value */
07380                 Bit8u tandy_irq = 7;
07381                 if (tandy_sb.port) tandy_irq = tandy_sb.irq;
07382                 else if (tandy_dac.port) tandy_irq = tandy_dac.irq;
07383                 Bit8u tandy_irq_vector = tandy_irq;
07384                 if (tandy_irq_vector<8) tandy_irq_vector += 8;
07385                 else tandy_irq_vector += (0x70-8);
07386 
07387                 RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6));
07388                 real_writed(0x40,0xd6,0x00000000);
07389             }
07390             delete tandy_DAC_callback[0];
07391             delete tandy_DAC_callback[1];
07392             tandy_DAC_callback[0]=NULL;
07393             tandy_DAC_callback[1]=NULL;
07394         }
07395 
07396         /* encourage the callback objects to uninstall HERE while we're in real mode, NOT during the
07397          * destructor stage where we're back in protected mode */
07398         for (unsigned int i=0;i < 20;i++) callback[i].Uninstall();
07399 
07400         /* assume these were allocated */
07401         CALLBACK_DeAllocate(call_irq0);
07402         CALLBACK_DeAllocate(call_irq07default);
07403         CALLBACK_DeAllocate(call_irq815default);
07404 
07405         /* done */
07406         CPU_Snap_Back_Restore();
07407     }
07408 };
07409 
07410 void BIOS_Enter_Boot_Phase(void) {
07411     /* direct CS:IP right to the instruction that leads to the boot process */
07412     /* note that since it's a callback instruction it doesn't really matter
07413      * what CS:IP is as long as it points to the instruction */
07414     reg_eip = BIOS_boot_code_offset & 0xFUL;
07415     CPU_SetSegGeneral(cs, BIOS_boot_code_offset >> 4UL);
07416 }
07417 
07418 void BIOS_SetCOMPort(Bitu port, Bit16u baseaddr) {
07419     switch(port) {
07420     case 0:
07421         mem_writew(BIOS_BASE_ADDRESS_COM1,baseaddr);
07422         mem_writeb(BIOS_COM1_TIMEOUT, 10); // FIXME: Right??
07423         break;
07424     case 1:
07425         mem_writew(BIOS_BASE_ADDRESS_COM2,baseaddr);
07426         mem_writeb(BIOS_COM2_TIMEOUT, 10); // FIXME: Right??
07427         break;
07428     case 2:
07429         mem_writew(BIOS_BASE_ADDRESS_COM3,baseaddr);
07430         mem_writeb(BIOS_COM3_TIMEOUT, 10); // FIXME: Right??
07431         break;
07432     case 3:
07433         mem_writew(BIOS_BASE_ADDRESS_COM4,baseaddr);
07434         mem_writeb(BIOS_COM4_TIMEOUT, 10); // FIXME: Right??
07435         break;
07436     }
07437 }
07438 
07439 void BIOS_SetLPTPort(Bitu port, Bit16u baseaddr) {
07440     switch(port) {
07441     case 0:
07442         mem_writew(BIOS_ADDRESS_LPT1,baseaddr);
07443         mem_writeb(BIOS_LPT1_TIMEOUT, 10);
07444         break;
07445     case 1:
07446         mem_writew(BIOS_ADDRESS_LPT2,baseaddr);
07447         mem_writeb(BIOS_LPT2_TIMEOUT, 10);
07448         break;
07449     case 2:
07450         mem_writew(BIOS_ADDRESS_LPT3,baseaddr);
07451         mem_writeb(BIOS_LPT3_TIMEOUT, 10);
07452         break;
07453     }
07454 }
07455 
07456 void BIOS_PnP_ComPortRegister(Bitu port,Bitu irq) {
07457     /* add to PnP BIOS */
07458     if (ISAPNPBIOS) {
07459         unsigned char tmp[256];
07460         unsigned int i;
07461 
07462         /* register serial ports */
07463         const unsigned char h1[9] = {
07464             ISAPNP_SYSDEV_HEADER(
07465                 ISAPNP_ID('P','N','P',0x0,0x5,0x0,0x1), /* PNP0501 16550A-compatible COM port */
07466                 ISAPNP_TYPE(0x07,0x00,0x02),        /* type: RS-232 communcations device, 16550-compatible */
07467                 0x0001 | 0x0002)
07468         };
07469 
07470         i = 0;
07471         memcpy(tmp+i,h1,9); i += 9;         /* can't disable, can't configure */
07472         /*----------allocated--------*/
07473         tmp[i+0] = (8 << 3) | 7;            /* IO resource */
07474         tmp[i+1] = 0x01;                /* 16-bit decode */
07475         host_writew(tmp+i+2,port);          /* min */
07476         host_writew(tmp+i+4,port);          /* max */
07477         tmp[i+6] = 0x10;                /* align */
07478         tmp[i+7] = 0x08;                /* length */
07479         i += 7+1;
07480 
07481         if (irq > 0) {
07482             tmp[i+0] = (4 << 3) | 3;            /* IRQ resource */
07483             host_writew(tmp+i+1,1 << irq);
07484             tmp[i+3] = 0x09;                /* HTL=1 LTL=1 */
07485             i += 3+1;
07486         }
07487 
07488         tmp[i+0] = 0x79;                /* END TAG */
07489         tmp[i+1] = 0x00;
07490         i += 2;
07491         /*-------------possible-----------*/
07492         tmp[i+0] = 0x79;                /* END TAG */
07493         tmp[i+1] = 0x00;
07494         i += 2;
07495         /*-------------compatible---------*/
07496         tmp[i+0] = 0x79;                /* END TAG */
07497         tmp[i+1] = 0x00;
07498         i += 2;
07499 
07500         if (!ISAPNP_RegisterSysDev(tmp,i)) {
07501             //LOG_MSG("ISAPNP register failed\n");
07502         }
07503     }
07504 }
07505 
07506 static BIOS* test = NULL;
07507 
07508 void BIOS_Destroy(Section* /*sec*/){
07509     ROMBIOS_DumpMemory();
07510     ISA_PNP_FreeAllDevs();
07511     if (test != NULL) {
07512         delete test;
07513         test = NULL;
07514     }
07515 }
07516 
07517 void BIOS_OnPowerOn(Section* sec) {
07518     (void)sec;//UNUSED
07519     if (test) delete test;
07520     test = new BIOS(control->GetSection("joystick"));
07521 }
07522 
07523 void swapInNextDisk(bool pressed);
07524 void swapInNextCD(bool pressed);
07525 
07526 void INT10_OnResetComplete();
07527 void CALLBACK_DeAllocate(Bitu in);
07528 
07529 void MOUSE_Unsetup_DOS(void);
07530 void MOUSE_Unsetup_BIOS(void);
07531 
07532 void BIOS_OnResetComplete(Section *x) {
07533     (void)x;//UNUSED
07534     INT10_OnResetComplete();
07535 
07536     if (IS_PC98_ARCH) {
07537         void PC98_BIOS_Bank_Switch_Reset(void);
07538         PC98_BIOS_Bank_Switch_Reset();
07539     }
07540 
07541     if (biosConfigSeg != 0u) {
07542         ROMBIOS_FreeMemory((Bitu)(biosConfigSeg << 4u)); /* remember it was alloc'd paragraph aligned, then saved >> 4 */
07543         biosConfigSeg = 0u;
07544     }
07545 
07546     call_pnp_rp = 0;
07547     if (call_pnp_r != ~0UL) {
07548         CALLBACK_DeAllocate(call_pnp_r);
07549         call_pnp_r = ~0UL;
07550     }
07551 
07552     call_pnp_pp = 0;
07553     if (call_pnp_p != ~0UL) {
07554         CALLBACK_DeAllocate(call_pnp_p);
07555         call_pnp_p = ~0UL;
07556     }
07557 
07558     MOUSE_Unsetup_DOS();
07559     MOUSE_Unsetup_BIOS();
07560 
07561     ISA_PNP_FreeAllSysNodeDevs();
07562 }
07563 
07564 void BIOS_Init() {
07565     LOG(LOG_MISC,LOG_DEBUG)("Initializing BIOS");
07566 
07567     /* make sure the array is zeroed */
07568     ISAPNP_SysDevNodeCount = 0;
07569     ISAPNP_SysDevNodeLargest = 0;
07570     for (int i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) ISAPNP_SysDevNodes[i] = NULL;
07571 
07572     /* make sure CD swap and floppy swap mapper events are available */
07573     MAPPER_AddHandler(swapInNextDisk,MK_d,MMODHOST|MMOD1,"swapimg","SwapFloppy"); /* Originally "Swap Image" but this version does not swap CDs */
07574     MAPPER_AddHandler(swapInNextCD,MK_c,MMODHOST|MMOD1,"swapcd","SwapCD"); /* Variant of "Swap Image" for CDs */
07575 
07576     /* NTS: VM_EVENT_BIOS_INIT this callback must be first. */
07577     AddExitFunction(AddExitFunctionFuncPair(BIOS_Destroy),false);
07578     AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(BIOS_OnPowerOn));
07579     AddVMEventFunction(VM_EVENT_RESET_END,AddVMEventFunctionFuncPair(BIOS_OnResetComplete));
07580 }
07581 
07582 void write_ID_version_string() {
07583     Bitu str_id_at,str_ver_at;
07584     size_t str_id_len,str_ver_len;
07585 
07586     /* NTS: We can't move the version and ID strings, it causes programs like MSD.EXE to lose
07587      *      track of the "IBM compatible blahblahblah" string. Which means that apparently
07588      *      programs looking for this information have the address hardcoded ALTHOUGH
07589      *      experiments show you can move the version string around so long as it's
07590      *      +1 from a paragraph boundary */
07591     /* TODO: *DO* allow dynamic relocation however if the dosbox.conf indicates that the user
07592      *       is not interested in IBM BIOS compatability. Also, it would be really cool if
07593      *       dosbox.conf could override these strings and the user could enter custom BIOS
07594      *       version and ID strings. Heh heh heh.. :) */
07595     str_id_at = 0xFE00E;
07596     str_ver_at = 0xFE061;
07597     str_id_len = strlen(bios_type_string)+1;
07598     str_ver_len = strlen(bios_version_string)+1;
07599     if (!IS_PC98_ARCH) {
07600         /* need to mark these strings off-limits so dynamic allocation does not overwrite them */
07601         ROMBIOS_GetMemory((Bitu)str_id_len+1,"BIOS ID string",1,str_id_at);
07602         ROMBIOS_GetMemory((Bitu)str_ver_len+1,"BIOS version string",1,str_ver_at);
07603     }
07604     if (str_id_at != 0) {
07605         for (size_t i=0;i < str_id_len;i++) phys_writeb(str_id_at+(PhysPt)i,(Bit8u)bios_type_string[i]);
07606     }
07607     if (str_ver_at != 0) {
07608         for (size_t i=0;i < str_ver_len;i++) phys_writeb(str_ver_at+(PhysPt)i,(Bit8u)bios_version_string[i]);
07609     }
07610 }
07611 
07612 extern Bit8u int10_font_08[256 * 8];
07613 
07614 /* NTS: Do not use callbacks! This function is called before CALLBACK_Init() */
07615 void ROMBIOS_Init() {
07616     Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
07617     Bitu oi,i;
07618 
07619     // log
07620     LOG(LOG_MISC,LOG_DEBUG)("Initializing ROM BIOS");
07621 
07622     oi = (Bitu)section->Get_int("rom bios minimum size"); /* in KB */
07623     oi = (oi + 3u) & ~3u; /* round to 4KB page */
07624     if (oi > 128u) oi = 128u;
07625     if (oi == 0u) {
07626         if (IS_PC98_ARCH)
07627             oi = 96u; // BIOS standard range is E8000-FFFFF
07628         else
07629             oi = 64u;
07630     }
07631     if (oi < 8) oi = 8; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */
07632     rombios_minimum_size = (oi << 10); /* convert to minimum, using size coming downward from 1MB */
07633 
07634     oi = (Bitu)section->Get_int("rom bios allocation max"); /* in KB */
07635     oi = (oi + 3u) & ~3u; /* round to 4KB page */
07636     if (oi > 128u) oi = 128u;
07637     if (oi == 0u) {
07638         if (IS_PC98_ARCH)
07639             oi = 96u;
07640         else
07641             oi = 64u;
07642     }
07643     if (oi < 8u) oi = 8u; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */
07644     oi <<= 10u;
07645     if (oi < rombios_minimum_size) oi = rombios_minimum_size;
07646     rombios_minimum_location = 0x100000ul - oi; /* convert to minimum, using size coming downward from 1MB */
07647 
07648     LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range: 0x%05X-0xFFFFF",(int)rombios_minimum_location);
07649     LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range according to minimum size: 0x%05X-0xFFFFF",(int)(0x100000 - rombios_minimum_size));
07650 
07651     if (IS_PC98_ARCH && rombios_minimum_location > 0xE8000)
07652         LOG(LOG_BIOS,LOG_DEBUG)("Caution: Minimum ROM base higher than E8000 will prevent use of actual PC-98 BIOS image or N88 BASIC");
07653 
07654     if (!MEM_map_ROM_physmem(rombios_minimum_location,0xFFFFF)) E_Exit("Unable to map ROM region as ROM");
07655 
07656     /* and the BIOS alias at the top of memory (TODO: what about 486/Pentium emulation where the BIOS at the 4GB top is different
07657      * from the BIOS at the legacy 1MB boundary because of shadowing and/or decompressing from ROM at boot? */
07658     {
07659         uint64_t top = (uint64_t)1UL << (uint64_t)MEM_get_address_bits();
07660         if (top >= ((uint64_t)1UL << (uint64_t)21UL)) { /* 2MB or more */
07661             unsigned long alias_base,alias_end;
07662 
07663             alias_base = (unsigned long)top + (unsigned long)rombios_minimum_location - (unsigned long)0x100000UL;
07664             alias_end = (unsigned long)top - (unsigned long)1UL;
07665 
07666             LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS also mapping alias to 0x%08lx-0x%08lx",alias_base,alias_end);
07667             if (!MEM_map_ROM_alias_physmem(alias_base,alias_end)) {
07668                 void MEM_cut_RAM_up_to(Bitu addr);
07669 
07670                 /* it's possible if memory aliasing is set that memsize is too large to make room.
07671                  * let memory emulation know where the ROM BIOS starts so it can unmap the RAM pages,
07672                  * reduce the memory reported to the OS, and try again... */
07673                 LOG(LOG_BIOS,LOG_DEBUG)("No room for ROM BIOS alias, reducing reported memory and unmapping RAM pages to make room");
07674                 MEM_cut_RAM_up_to(alias_base);
07675 
07676                 if (!MEM_map_ROM_alias_physmem(alias_base,alias_end))
07677                     E_Exit("Unable to map ROM region as ROM alias");
07678             }
07679         }
07680     }
07681 
07682     /* set up allocation */
07683     rombios_alloc.name = "ROM BIOS";
07684     rombios_alloc.topDownAlloc = true;
07685     rombios_alloc.initSetRange(rombios_minimum_location,0xFFFF0 - 1);
07686 
07687     write_ID_version_string();
07688 
07689     /* some structures when enabled are fixed no matter what */
07690     if (rom_bios_8x8_cga_font && !IS_PC98_ARCH) {
07691         /* line 139, int10_memory.cpp: the 8x8 font at 0xF000:FA6E, first 128 chars.
07692          * allocate this NOW before other things get in the way */
07693         if (ROMBIOS_GetMemory(128*8,"BIOS 8x8 font (first 128 chars)",1,0xFFA6E) == 0) {
07694             LOG_MSG("WARNING: Was not able to mark off 0xFFA6E off-limits for 8x8 font");
07695         }
07696     }
07697 
07698     /* PC-98 BIOS vectors appear to reside at segment 0xFD80. This is so common some games
07699      * use it (through INT 1Dh) to detect whether they are running on PC-98 or not (issue #927).
07700      *
07701      * Note that INT 1Dh is one of the few BIOS interrupts not intercepted by PC-98 MS-DOS */
07702     if (IS_PC98_ARCH) {
07703         if (ROMBIOS_GetMemory(128,"PC-98 INT vector stub segment 0xFD80",1,0xFD800) == 0) {
07704             LOG_MSG("WARNING: Was not able to mark off 0xFD800 off-limits for PC-98 int vector stubs");
07705         }
07706     }
07707 
07708     /* PC-98 BIOSes have a LIO interface at segment F990 with graphic subroutines for Microsoft BASIC */
07709     if (IS_PC98_ARCH) {
07710         if (ROMBIOS_GetMemory(256,"PC-98 LIO graphic ROM BIOS library",1,0xF9900) == 0) {
07711             LOG_MSG("WARNING: Was not able to mark off 0xF9900 off-limits for PC-98 LIO graphics library");
07712         }
07713     }
07714 
07715     /* install the font */
07716     if (rom_bios_8x8_cga_font) {
07717         for (i=0;i<128*8;i++) {
07718             phys_writeb(PhysMake(0xf000,0xfa6e)+i,int10_font_08[i]);
07719         }
07720     }
07721 
07722     /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute after reset.
07723      * we allow this for both hacker curiosity and automated CPU testing. */
07724     {
07725         std::string path = section->Get_string("call binary on reset");
07726         struct stat st;
07727 
07728         if (!path.empty() && stat(path.c_str(),&st) == 0 && S_ISREG(st.st_mode) && st.st_size <= (128u*1024u)) {
07729             Bitu base = ROMBIOS_GetMemory((Bitu)st.st_size,"User reset vector binary",16u/*page align*/,0u);
07730 
07731             if (base != 0) {
07732                 FILE *fp = fopen(path.c_str(),"rb");
07733 
07734                 if (fp != NULL) {
07735                     /* NTS: Make sure memory base != NULL, and that it fits within 1MB.
07736                      *      memory code allocates a minimum 1MB of memory even if
07737                      *      guest memory is less than 1MB because ROM BIOS emulation
07738                      *      depends on it. */
07739                     assert(GetMemBase() != NULL);
07740                     assert((base+(Bitu)st.st_size) <= 0x100000ul);
07741                     fread(GetMemBase()+base,(size_t)st.st_size,1u,fp);
07742                     fclose(fp);
07743 
07744                     LOG_MSG("User reset vector binary '%s' loaded at 0x%lx",path.c_str(),(unsigned long)base);
07745                     bios_user_reset_vector_blob = base;
07746                 }
07747                 else {
07748                     LOG_MSG("WARNING: Unable to open file to load user reset vector binary '%s' into ROM BIOS memory",path.c_str());
07749                 }
07750             }
07751             else {
07752                 LOG_MSG("WARNING: Unable to load user reset vector binary '%s' into ROM BIOS memory",path.c_str());
07753             }
07754         }
07755     }
07756 
07757     /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute just before boot.
07758      * we allow this for both hacker curiosity and automated CPU testing. */
07759     {
07760         std::string path = section->Get_string("call binary on boot");
07761         struct stat st;
07762 
07763         if (!path.empty() && stat(path.c_str(),&st) == 0 && S_ISREG(st.st_mode) && st.st_size <= (128u*1024u)) {
07764             Bitu base = ROMBIOS_GetMemory((Bitu)st.st_size,"User boot hook binary",16u/*page align*/,0u);
07765 
07766             if (base != 0) {
07767                 FILE *fp = fopen(path.c_str(),"rb");
07768 
07769                 if (fp != NULL) {
07770                     /* NTS: Make sure memory base != NULL, and that it fits within 1MB.
07771                      *      memory code allocates a minimum 1MB of memory even if
07772                      *      guest memory is less than 1MB because ROM BIOS emulation
07773                      *      depends on it. */
07774                     assert(GetMemBase() != NULL);
07775                     assert((base+(Bitu)st.st_size) <= 0x100000ul);
07776                     fread(GetMemBase()+base,(size_t)st.st_size,1u,fp);
07777                     fclose(fp);
07778 
07779                     LOG_MSG("User boot hook binary '%s' loaded at 0x%lx",path.c_str(),(unsigned long)base);
07780                     bios_user_boot_hook = base;
07781                 }
07782                 else {
07783                     LOG_MSG("WARNING: Unable to open file to load user boot hook binary '%s' into ROM BIOS memory",path.c_str());
07784                 }
07785             }
07786             else {
07787                 LOG_MSG("WARNING: Unable to load user boot hook binary '%s' into ROM BIOS memory",path.c_str());
07788             }
07789         }
07790     }
07791 }
07792 
07794 void UpdateKeyWithLed(int nVirtKey, int flagAct, int flagLed);
07795 
07796 void BIOS_SynchronizeNumLock()
07797 {
07798 #if defined(WIN32)
07799         UpdateKeyWithLed(VK_NUMLOCK, BIOS_KEYBOARD_FLAGS1_NUMLOCK_ACTIVE, BIOS_KEYBOARD_LEDS_NUM_LOCK);
07800 #endif
07801 }
07802 
07803 void BIOS_SynchronizeCapsLock()
07804 {
07805 #if defined(WIN32)
07806         UpdateKeyWithLed(VK_CAPITAL, BIOS_KEYBOARD_FLAGS1_CAPS_LOCK_ACTIVE, BIOS_KEYBOARD_LEDS_CAPS_LOCK);
07807 #endif
07808 }
07809 
07810 void BIOS_SynchronizeScrollLock()
07811 {
07812 #if defined(WIN32)
07813         UpdateKeyWithLed(VK_SCROLL, BIOS_KEYBOARD_FLAGS1_SCROLL_LOCK_ACTIVE, BIOS_KEYBOARD_LEDS_SCROLL_LOCK);
07814 #endif
07815 }
07816 
07817 void UpdateKeyWithLed(int nVirtKey, int flagAct, int flagLed)
07818 {
07819 #if defined(WIN32)
07820 
07821         const auto state = GetKeyState(nVirtKey);
07822 
07823         const auto flags1 = BIOS_KEYBOARD_FLAGS1;
07824         const auto flags2 = BIOS_KEYBOARD_LEDS;
07825 
07826         auto flag1 = mem_readb(flags1);
07827         auto flag2 = mem_readb(flags2);
07828 
07829         if (state & 1)
07830         {
07831                 flag1 |= flagAct;
07832                 flag2 |= flagLed;
07833         }
07834         else
07835         {
07836                 flag1 &= ~flagAct;
07837                 flag2 &= ~flagLed;
07838         }
07839 
07840         mem_writeb(flags1, flag1);
07841         mem_writeb(flags2, flag2);
07842 
07843 #else
07844 
07845     (void)nVirtKey;
07846     (void)flagAct;
07847     (void)flagLed;
07848 
07849 #endif
07850 }