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