DOSBox-X
|
00001 00002 #include "dosbox.h" 00003 #include "setup.h" 00004 #include "video.h" 00005 #include "pic.h" 00006 #include "vga.h" 00007 #include "inout.h" 00008 #include "programs.h" 00009 #include "support.h" 00010 #include "setup.h" 00011 #include "timer.h" 00012 #include "mem.h" 00013 #include "util_units.h" 00014 #include "control.h" 00015 #include "pc98_cg.h" 00016 #include "pc98_dac.h" 00017 #include "pc98_gdc.h" 00018 #include "pc98_gdc_const.h" 00019 #include "joystick.h" 00020 #include "regs.h" 00021 #include "mixer.h" 00022 #include "callback.h" 00023 00024 #include <string.h> 00025 #include <stdlib.h> 00026 #include <string> 00027 #include <stdio.h> 00028 00029 using namespace std; 00030 00031 #if defined(_MSC_VER) 00032 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ 00033 #endif 00034 00035 #include <map> 00036 00037 #include "np2glue.h" 00038 00039 MixerChannel *pc98_mixer = NULL; 00040 00041 NP2CFG pccore; 00042 00043 extern unsigned char pc98_mem_msw_m[8]; 00044 bool pc98_soundbios_rom_load = true; 00045 bool pc98_soundbios_enabled = false; 00046 00047 extern "C" unsigned char *CGetMemBase() { 00048 return MemBase; 00049 } 00050 00051 extern "C" void sound_sync(void) { 00052 if (pc98_mixer) pc98_mixer->FillUp(); 00053 } 00054 00055 extern "C" void _TRACEOUT(const char *fmt,...) { 00056 va_list va; 00057 char tmp[512]; 00058 00059 va_start(va,fmt); 00060 00061 vsnprintf(tmp,sizeof(tmp),fmt,va); 00062 LOG_MSG("PC98FM TRACEOUT: %s",tmp); 00063 00064 va_end(va); 00065 } 00066 00067 void getbiospath(OEMCHAR *path, const OEMCHAR *fname, int maxlen) { 00068 LOG_MSG("PC98FM getbiospath fname='%s'",fname); 00069 snprintf(path,(size_t)maxlen,"%s",fname); 00070 } 00071 00072 enum { 00073 JOY_UP_BIT = (1u << 0u), 00074 JOY_DOWN_BIT = (1u << 1u), 00075 JOY_LEFT_BIT = (1u << 2u), 00076 JOY_RIGHT_BIT = (1u << 3u), 00077 JOY_RAPIDBTN1_BIT = (1u << 4u), 00078 JOY_RAPIDBTN2_BIT = (1u << 5u), 00079 JOY_BTN1_BIT = (1u << 6u), 00080 JOY_BTN2_BIT = (1u << 7u) 00081 }; 00082 00083 00084 REG8 joymng_getstat(void) { 00085 unsigned char r = 0xFF; 00086 00087 if (JOYSTICK_IsEnabled(0)) { 00088 if (JOYSTICK_GetButton(0,0)) 00089 r &= ~JOY_BTN1_BIT; 00090 if (JOYSTICK_GetButton(0,1)) 00091 r &= ~JOY_BTN2_BIT; 00092 00093 float x = JOYSTICK_GetMove_X(0); 00094 float y = JOYSTICK_GetMove_Y(0); 00095 00096 if (x >= 0.5) 00097 r &= ~JOY_RIGHT_BIT; 00098 else if (x <= -0.5) 00099 r &= ~JOY_LEFT_BIT; 00100 00101 if (y >= 0.5) 00102 r &= ~JOY_DOWN_BIT; 00103 else if (y <= -0.5) 00104 r &= ~JOY_UP_BIT; 00105 } 00106 00107 return r; 00108 } 00109 00110 REG8 keystat_getjoy(void) { 00111 return 0xFF; 00112 } 00113 00114 struct CBUS4PORT { 00115 IOINP inp; 00116 IOOUT out; 00117 00118 CBUS4PORT() : inp(NULL), out(NULL) { } 00119 }; 00120 00121 static std::map<UINT,CBUS4PORT> cbuscore_map; 00122 00123 void pc98_fm86_write(Bitu port,Bitu val,Bitu iolen) { 00124 (void)iolen;//UNUSED 00125 const auto &cbusm = cbuscore_map[port]; 00126 const auto &func = cbusm.out; 00127 if (func) func(port,val); 00128 } 00129 00130 Bitu pc98_fm86_read(Bitu port,Bitu iolen) { 00131 (void)iolen;//UNUSED 00132 const auto &cbusm = cbuscore_map[port]; 00133 const auto &func = cbusm.inp; 00134 if (func) return func(port); 00135 return ~0ul; 00136 } 00137 00138 // four I/O ports, 2 ports apart 00139 void cbuscore_attachsndex(UINT port, const IOOUT *out, const IOINP *inp) { 00140 LOG_MSG("cbuscore_attachsndex(port=0x%x)",port); 00141 00142 for (unsigned int i=0;i < 4;i++) { 00143 auto &cbusm = cbuscore_map[port+(i*2)]; 00144 00145 IO_RegisterReadHandler(port+(i*2),pc98_fm86_read,IO_MB); 00146 cbusm.inp = inp[i]; 00147 00148 IO_RegisterWriteHandler(port+(i*2),pc98_fm86_write,IO_MB); 00149 cbusm.out = out[i]; 00150 } 00151 } 00152 00153 void pic_setirq(REG8 irq) { 00154 PIC_ActivateIRQ(irq); 00155 } 00156 00157 void pic_resetirq(REG8 irq) { 00158 PIC_DeActivateIRQ(irq); 00159 } 00160 00161 #include "sound.h" 00162 #include "fmboard.h" 00163 00164 // wrapper for fmtimer events 00165 void fmport_a(NEVENTITEM item); 00166 void fmport_b(NEVENTITEM item); 00167 00168 static void fmport_a_pic_event(Bitu val) { 00169 (void)val;//UNUSED 00170 fmport_a(NULL); 00171 } 00172 00173 static void fmport_b_pic_event(Bitu val) { 00174 (void)val;//UNUSED 00175 fmport_b(NULL); 00176 } 00177 00178 // save state support 00179 void *fmport_a_pic_event_PIC_Event = (void*)((uintptr_t)fmport_a_pic_event); 00180 void *fmport_b_pic_event_PIC_Event = (void*)((uintptr_t)fmport_b_pic_event); 00181 00182 extern "C" void pc98_fm_dosbox_fmtimer_setevent(unsigned int n,double dt) { 00183 PIC_EventHandler func = n ? fmport_b_pic_event : fmport_a_pic_event; 00184 00185 PIC_RemoveEvents(func); 00186 PIC_AddEvent(func,dt); 00187 } 00188 00189 extern "C" void pc98_fm_dosbox_fmtimer_clearevent(unsigned int n) { 00190 PIC_RemoveEvents(n ? fmport_b_pic_event : fmport_a_pic_event); 00191 } 00192 00193 // mixer callback 00194 00195 static void pc98_mix_CallBack(Bitu len) { 00196 unsigned int s = len; 00197 00198 if (s > (sizeof(MixTemp)/sizeof(Bit32s)/2)) 00199 s = (sizeof(MixTemp)/sizeof(Bit32s)/2); 00200 00201 memset(MixTemp,0,s * sizeof(Bit32s) * 2); 00202 00203 opngen_getpcm(NULL, (SINT32*)MixTemp, s); 00204 tms3631_getpcm(&tms3631, (SINT32*)MixTemp, s); 00205 00206 for (unsigned int i=0;i < 3;i++) 00207 psggen_getpcm(&__psg[i], (SINT32*)MixTemp, s); 00208 00209 // NTS: _RHYTHM is a struct with the same initial layout as PCMMIX 00210 pcmmix_getpcm((PCMMIX)(&rhythm), (SINT32*)MixTemp, s); 00211 #if defined(SUPPORT_PX) 00212 pcmmix_getpcm((PCMMIX)(&rhythm2), (SINT32*)MixTemp, s); 00213 pcmmix_getpcm((PCMMIX)(&rhythm3), (SINT32*)MixTemp, s); 00214 #endif // defined(SUPPORT_PX) 00215 00216 adpcm_getpcm(&adpcm, (SINT32*)MixTemp, s); 00217 #if defined(SUPPORT_PX) 00218 adpcm_getpcm(&adpcm2, (SINT32*)MixTemp, s); 00219 adpcm_getpcm(&adpcm3, (SINT32*)MixTemp, s); 00220 #endif // defined(SUPPORT_PX) 00221 00222 pcm86gen_getpcm(NULL, (SINT32*)MixTemp, s); 00223 00224 pc98_mixer->AddSamples_s32(s, (Bit32s*)MixTemp); 00225 } 00226 00227 static bool pc98fm_init = false; 00228 00229 extern "C" { 00230 UINT8 fmtimer_irq2index(const UINT8 irq); 00231 UINT8 fmtimer_index2irq(const UINT8 index); 00232 void fmboard_on_reset(); 00233 void rhythm_deinitialize(void); 00234 } 00235 00236 UINT8 board86_encodeirqidx(const unsigned char idx) { 00237 /* see board86.c to understand what this is about */ 00238 return ((idx & 1) ? 0x08 : 0x00) + 00239 ((idx & 2) ? 0x04 : 0x00); 00240 } 00241 00242 UINT8 board26k_encodeirqidx(const unsigned char idx) { 00243 /* see board26k.c to understand what this is about */ 00244 return (idx << 6); 00245 } 00246 00247 void PC98_FM_Destroy(Section *sec) { 00248 (void)sec;//UNUSED 00249 if (pc98fm_init) { 00250 rhythm_deinitialize(); 00251 } 00252 00253 if (pc98_mixer) { 00254 MIXER_DelChannel(pc98_mixer); 00255 pc98_mixer = NULL; 00256 } 00257 } 00258 00259 void pc98_set_msw4_soundbios(void) 00260 { 00261 /* Set MSW4 bit 3 for sound BIOS. */ 00262 if(pc98_soundbios_enabled) 00263 pc98_mem_msw_m[3/*MSW4*/] |= 0x8; 00264 else 00265 pc98_mem_msw_m[3/*MSW4*/] &= ~((unsigned char)0x8); 00266 } 00267 00268 static CALLBACK_HandlerObject soundbios_callback; 00269 00270 // for more information see chapter 13 of [https://ia801305.us.archive.org/8/items/PC9800TechnicalDataBookBIOS1992/PC-9800TechnicalDataBook_BIOS_1992_text.pdf] 00271 static Bitu SOUNDROM_INTD2_PC98_Handler(void) { 00272 const char *call_name = "?"; 00273 00274 switch (reg_ah) { 00275 case 0x00: // INITIALIZE 00276 call_name = "INITIALIZE"; 00277 goto unknown; 00278 case 0x01: // PLAY 00279 call_name = "PLAY"; 00280 goto unknown; 00281 case 0x02: // CLEAR 00282 call_name = "CLEAR"; 00283 goto unknown; 00284 case 0x10: // READ REG 00285 call_name = "READ REG"; 00286 goto unknown; 00287 case 0x11: // WRITE REG 00288 call_name = "WRITE REG"; 00289 goto unknown; 00290 case 0x12: // SET TOUCH 00291 call_name = "SET TOUCH"; 00292 goto unknown; 00293 case 0x13: // NOTE 00294 call_name = "NOTE"; 00295 goto unknown; 00296 case 0x14: // SET LENGTH 00297 call_name = "SET LENGTH"; 00298 goto unknown; 00299 case 0x15: // SET TEMPO 00300 call_name = "SET TEMPO"; 00301 goto unknown; 00302 case 0x16: // SET PARA BLOCK 00303 call_name = "SET PARA BLOCK"; 00304 goto unknown; 00305 case 0x17: // READ PARA 00306 call_name = "READ PARA"; 00307 goto unknown; 00308 case 0x18: // WRITE PARA 00309 call_name = "WRITE PARA"; 00310 goto unknown; 00311 case 0x19: // ALL STOP 00312 call_name = "ALL STOP"; 00313 goto unknown; 00314 case 0x1A: // CONT PLAY 00315 call_name = "CONT PLAY"; 00316 goto unknown; 00317 case 0x1B: // MODU ON 00318 call_name = "MODU ON"; 00319 goto unknown; 00320 case 0x1C: // MODU OFF 00321 call_name = "MODU OFF"; 00322 goto unknown; 00323 case 0x1D: // SET INT COND 00324 call_name = "SET INT COND"; 00325 goto unknown; 00326 case 0x1E: // HOLD STATE 00327 call_name = "HOLD STATE"; 00328 goto unknown; 00329 case 0x1F: // SET VOLUME 00330 call_name = "SET VOLUME"; 00331 goto unknown; 00332 default: 00333 unknown: 00334 LOG_MSG("PC-98 SOUND BIOS (INT D2h) call '%s' with AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", 00335 call_name, 00336 reg_ax, 00337 reg_bx, 00338 reg_cx, 00339 reg_dx, 00340 reg_si, 00341 reg_di, 00342 SegValue(ds), 00343 SegValue(es)); 00344 break; 00345 } 00346 00347 // guessing, from Yksoft1's patch 00348 reg_ah = 0; 00349 00350 return CBRET_NONE; 00351 } 00352 00353 bool LoadSoundBIOS(void) { 00354 FILE *fp; 00355 00356 if (!pc98_soundbios_rom_load) return false; 00357 00358 fp = fopen("SOUND.ROM","rb"); 00359 if (!fp) fp = fopen("sound.rom","rb"); 00360 if (!fp) return false; 00361 00362 if (fread(MemBase+0xCC000,0x4000,1,fp) != 1) { 00363 LOG_MSG("PC-98 SOUND.ROM failed to read 16k"); 00364 fclose(fp); 00365 return false; 00366 } 00367 00368 LOG_MSG("PC-98 SOUND.ROM loaded into memory"); 00369 fclose(fp); 00370 return true; 00371 } 00372 00373 bool PC98_FM_SoundBios_Enabled(void) { 00374 return pc98_soundbios_enabled; 00375 } 00376 00377 void PC98_FM_OnEnterPC98(Section *sec) { 00378 (void)sec;//UNUSED 00379 Section_prop * pc98_section=static_cast<Section_prop *>(control->GetSection("pc98")); 00380 assert(pc98_section != NULL); 00381 bool was_pc98fm_init = pc98fm_init; 00382 00383 if (!pc98fm_init) { 00384 unsigned char fmirqidx; 00385 unsigned int baseio; 00386 std::string board; 00387 int irq; 00388 00389 soundbios_callback.Uninstall(); 00390 00391 board = pc98_section->Get_string("pc-98 fm board"); 00392 if (board == "off" || board == "false") { 00393 /* Don't enable Sound BIOS if sound board itself is disabled. */ 00394 pc98_soundbios_enabled = false; 00395 pc98_set_msw4_soundbios(); 00396 return; 00397 } 00398 00399 irq = pc98_section->Get_int("pc-98 fm board irq"); 00400 baseio = (unsigned int)pc98_section->Get_hex("pc-98 fm board io port"); 00401 00402 pc98_soundbios_enabled = pc98_section->Get_bool("pc-98 sound bios"); 00403 pc98_soundbios_rom_load = pc98_section->Get_bool("pc-98 load sound bios rom file"); 00404 pc98_set_msw4_soundbios(); 00405 00406 if (pc98_soundbios_enabled) { 00407 /* TODO: Load SOUND.ROM to CC000h - CFFFFh when Sound BIOS is enabled? 00408 * Or simulate Sound BIOS calls ourselves? */ 00409 if (LoadSoundBIOS()) { 00410 /* good! */ 00411 } 00412 else { 00413 soundbios_callback.Install(&SOUNDROM_INTD2_PC98_Handler,CB_IRET,"Sound ROM INT D2h"); 00414 00415 /* fake INT D2h sound BIOS entry point at CEE0:0000. 00416 * Unlike the LIO interface there's only one interrupt (if Neko Project II code is correct) */ 00417 Bit32u ofs = 0xCEE0u << 4u; 00418 Bit32u callback_addr = soundbios_callback.Get_RealPointer(); 00419 00420 phys_writed(ofs+0,0x0001); // number of entries 00421 phys_writew(ofs+4,0xD2); // INT D2h entry point 00422 phys_writew(ofs+6,0x08); 00423 phys_writeb(ofs+8,0xEA); // JMP FAR <callback> 00424 phys_writed(ofs+9,callback_addr); 00425 } 00426 } 00427 00428 /* Manual testing shows PC-98 games like it when the board is on IRQ 12 */ 00429 if (irq == 0) irq = 12; 00430 00431 fmirqidx = fmtimer_irq2index(irq); 00432 00433 pc98fm_init = true; 00434 00435 unsigned int rate = 44100; 00436 00437 memset(&pccore,0,sizeof(pccore)); 00438 pccore.samplingrate = 44100; 00439 pccore.baseclock = PIT_TICK_RATE; 00440 pccore.multiple = 1; 00441 for (unsigned int i=0;i < 6;i++) pccore.vol14[i] = 0xFF; 00442 pccore.vol_fm = 128; 00443 pccore.vol_ssg = 128; 00444 pccore.vol_adpcm = 128; 00445 pccore.vol_pcm = 128; 00446 pccore.vol_rhythm = 128; 00447 00448 // fddmtrsnd_initialize(rate); 00449 // beep_initialize(rate); 00450 // beep_setvol(3); 00451 tms3631_initialize(rate); 00452 tms3631_setvol(pccore.vol14); 00453 opngen_initialize(rate); 00454 opngen_setvol(pccore.vol_fm); 00455 psggen_initialize(rate); 00456 psggen_setvol(pccore.vol_ssg); 00457 rhythm_initialize(rate); 00458 rhythm_setvol(pccore.vol_rhythm); 00459 adpcm_initialize(rate); 00460 adpcm_setvol(pccore.vol_adpcm); 00461 pcm86gen_initialize(rate); 00462 pcm86gen_setvol(pccore.vol_pcm); 00463 00464 if (board == "board86c" || board == "auto") { 00465 if (baseio == 0 || baseio == 0x188) { /* default */ 00466 pccore.snd86opt |= 0x01; 00467 baseio = 0x188; 00468 } 00469 else { 00470 baseio = 0x288; 00471 } 00472 00473 pccore.snd86opt += board86_encodeirqidx(fmirqidx); 00474 00475 LOG_MSG("PC-98 FM board is PC-9801-86c at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx)); 00476 fmboard_reset(&np2cfg, 0x14); 00477 } 00478 else if (board == "board86") { 00479 if (baseio == 0 || baseio == 0x188) { /* default */ 00480 pccore.snd86opt |= 0x01; 00481 baseio = 0x188; 00482 } 00483 else { 00484 baseio = 0x288; 00485 } 00486 00487 pccore.snd86opt += board86_encodeirqidx(fmirqidx); 00488 00489 LOG_MSG("PC-98 FM board is PC-9801-86 at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx)); 00490 fmboard_reset(&np2cfg, 0x04); 00491 } 00492 else if (board == "board26k") { 00493 if (baseio == 0x188) { 00494 pccore.snd26opt |= 0x10; 00495 baseio = 0x188; 00496 } 00497 else { /* default */ 00498 baseio = 0x088; 00499 } 00500 00501 pccore.snd26opt += board26k_encodeirqidx(fmirqidx); 00502 00503 LOG_MSG("PC-98 FM board is PC-9801-26k at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx)); 00504 fmboard_reset(&np2cfg, 0x02); 00505 } 00506 else if (board == "board14") { 00507 /* Apparently board14 is always IRQ 12, port 88h */ 00508 LOG_MSG("PC-98 FM board is PC-9801-14 at baseio=0x%x irq=%d",0x88,12); 00509 LOG_MSG("WARNING: This is not yet implemented!"); // board14 emulation requires emulation of a PIT timer (8253) on the board itself 00510 fmboard_reset(&np2cfg, 0x01); 00511 } 00512 else { 00513 if (baseio == 0 || baseio == 0x188) { /* default */ 00514 pccore.snd86opt |= 0x01; 00515 baseio = 0x188; 00516 } 00517 else { 00518 baseio = 0x288; 00519 } 00520 00521 pccore.snd86opt += board86_encodeirqidx(fmirqidx); 00522 00523 LOG_MSG("PC-98 FM board is PC-9801-86c at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx)); 00524 fmboard_reset(&np2cfg, 0x14); // board86c, a good default 00525 } 00526 00527 fmboard_bind(); 00528 fmboard_extenable(true); 00529 00530 // WARNING: Some parts of the borrowed code assume 44100, 22050, or 11025 and 00531 // will misrender if given any other sample rate (especially the OPNA synth). 00532 00533 pc98_mixer = MIXER_AddChannel(pc98_mix_CallBack, rate, "PC-98"); 00534 pc98_mixer->Enable(true); 00535 } 00536 00537 if (was_pc98fm_init) { 00538 fmboard_on_reset(); 00539 fmboard_bind(); // FIXME: Re-binds I/O ports as well 00540 fmboard_extenable(true); 00541 } 00542 } 00543