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