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