DOSBox-X
|
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 §or) { 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, ®_ah)) { 05568 // wait for TX buffer empty 05569 if (INT14_Wait(port+5u, 0x20u, timeout, ®_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, ®_ah)) { 05593 // wait for character to arrive 05594 if (INT14_Wait(port+5, 0x01, timeout, ®_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 }