DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pc98_fm.cpp
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_enabled = false;
00045 
00046 extern "C" unsigned char *CGetMemBase() {
00047     return MemBase;
00048 }
00049 
00050 extern "C" void sound_sync(void) {
00051     if (pc98_mixer) pc98_mixer->FillUp();
00052 }
00053 
00054 extern "C" void _TRACEOUT(const char *fmt,...) {
00055     va_list va;
00056     char tmp[512];
00057 
00058     va_start(va,fmt);
00059 
00060     vsnprintf(tmp,sizeof(tmp),fmt,va);
00061     LOG_MSG("PC98FM TRACEOUT: %s",tmp);
00062 
00063     va_end(va);
00064 }
00065 
00066 void getbiospath(OEMCHAR *path, const OEMCHAR *fname, int maxlen) {
00067     LOG_MSG("PC98FM getbiospath fname='%s'",fname);
00068     snprintf(path,(size_t)maxlen,"%s",fname);
00069 }
00070 
00071 enum {
00072         JOY_UP_BIT                  = (1u << 0u),
00073         JOY_DOWN_BIT            = (1u << 1u),
00074         JOY_LEFT_BIT            = (1u << 2u),
00075         JOY_RIGHT_BIT           = (1u << 3u),
00076         JOY_RAPIDBTN1_BIT       = (1u << 4u),
00077         JOY_RAPIDBTN2_BIT       = (1u << 5u),
00078         JOY_BTN1_BIT            = (1u << 6u),
00079         JOY_BTN2_BIT            = (1u << 7u)
00080 };
00081 
00082 
00083 REG8 joymng_getstat(void) {
00084     unsigned char r = 0xFF;
00085 
00086     if (JOYSTICK_IsEnabled(0)) {
00087         if (JOYSTICK_GetButton(0,0))
00088             r &= ~JOY_BTN1_BIT;
00089         if (JOYSTICK_GetButton(0,1))
00090             r &= ~JOY_BTN2_BIT;
00091 
00092         float x = JOYSTICK_GetMove_X(0);
00093         float y = JOYSTICK_GetMove_Y(0);
00094 
00095         if (x >= 0.5)
00096             r &= ~JOY_RIGHT_BIT;
00097         else if (x <= -0.5)
00098             r &= ~JOY_LEFT_BIT;
00099 
00100         if (y >= 0.5)
00101             r &= ~JOY_DOWN_BIT;
00102         else if (y <= -0.5)
00103             r &= ~JOY_UP_BIT;
00104     }
00105 
00106     return r;
00107 }
00108 
00109 REG8 keystat_getjoy(void) {
00110     return 0xFF;
00111 }
00112 
00113 struct CBUS4PORT {
00114     IOINP       inp;
00115     IOOUT       out;
00116 
00117     CBUS4PORT() : inp(NULL), out(NULL) { }
00118 };
00119 
00120 static std::map<UINT,CBUS4PORT> cbuscore_map;
00121 
00122 void pc98_fm86_write(Bitu port,Bitu val,Bitu iolen) {
00123     (void)iolen;//UNUSED
00124     auto &cbusm = cbuscore_map[port];
00125     auto &func = cbusm.out;
00126     if (func) func(port,val);
00127 }
00128 
00129 Bitu pc98_fm86_read(Bitu port,Bitu iolen) {
00130     (void)iolen;//UNUSED
00131     auto &cbusm = cbuscore_map[port];
00132     auto &func = cbusm.inp;
00133     if (func) return func(port);
00134     return ~0ul;
00135 }
00136 
00137 // four I/O ports, 2 ports apart
00138 void cbuscore_attachsndex(UINT port, const IOOUT *out, const IOINP *inp) {
00139     LOG_MSG("cbuscore_attachsndex(port=0x%x)",port);
00140 
00141     for (unsigned int i=0;i < 4;i++) {
00142         auto &cbusm = cbuscore_map[port+(i*2)];
00143 
00144         IO_RegisterReadHandler(port+(i*2),pc98_fm86_read,IO_MB);
00145         cbusm.inp = inp[i];
00146 
00147         IO_RegisterWriteHandler(port+(i*2),pc98_fm86_write,IO_MB);
00148         cbusm.out = out[i];
00149     }
00150 }
00151 
00152 void pic_setirq(REG8 irq) {
00153     PIC_ActivateIRQ(irq);
00154 }
00155 
00156 void pic_resetirq(REG8 irq) {
00157     PIC_DeActivateIRQ(irq);
00158 }
00159 
00160 #include "sound.h"
00161 #include "fmboard.h"
00162 
00163 // wrapper for fmtimer events
00164 void fmport_a(NEVENTITEM item);
00165 void fmport_b(NEVENTITEM item);
00166 
00167 static void fmport_a_pic_event(Bitu val) {
00168     (void)val;//UNUSED
00169     fmport_a(NULL);
00170 }
00171 
00172 static void fmport_b_pic_event(Bitu val) {
00173     (void)val;//UNUSED
00174     fmport_b(NULL);
00175 }
00176 
00177 extern "C" void pc98_fm_dosbox_fmtimer_setevent(unsigned int n,double dt) {
00178     PIC_EventHandler func = n ? fmport_b_pic_event : fmport_a_pic_event;
00179 
00180     PIC_RemoveEvents(func);
00181     PIC_AddEvent(func,dt);
00182 }
00183 
00184 extern "C" void pc98_fm_dosbox_fmtimer_clearevent(unsigned int n) {
00185     PIC_RemoveEvents(n ? fmport_b_pic_event : fmport_a_pic_event);
00186 }
00187 
00188 // mixer callback
00189 
00190 static void pc98_mix_CallBack(Bitu len) {
00191     unsigned int s = len;
00192 
00193     if (s > (sizeof(MixTemp)/sizeof(Bit32s)/2))
00194         s = (sizeof(MixTemp)/sizeof(Bit32s)/2);
00195 
00196     memset(MixTemp,0,s * sizeof(Bit32s) * 2);
00197 
00198     opngen_getpcm(NULL, (SINT32*)MixTemp, s);
00199     tms3631_getpcm(&tms3631, (SINT32*)MixTemp, s);
00200 
00201     for (unsigned int i=0;i < 3;i++)
00202         psggen_getpcm(&__psg[i], (SINT32*)MixTemp, s);
00203  
00204     // NTS: _RHYTHM is a struct with the same initial layout as PCMMIX
00205     pcmmix_getpcm((PCMMIX)(&rhythm), (SINT32*)MixTemp, s);
00206 #if defined(SUPPORT_PX)
00207     pcmmix_getpcm((PCMMIX)(&rhythm2), (SINT32*)MixTemp, s);
00208     pcmmix_getpcm((PCMMIX)(&rhythm3), (SINT32*)MixTemp, s);
00209 #endif  // defined(SUPPORT_PX)
00210 
00211     adpcm_getpcm(&adpcm, (SINT32*)MixTemp, s);
00212 #if defined(SUPPORT_PX)
00213     adpcm_getpcm(&adpcm2, (SINT32*)MixTemp, s);
00214     adpcm_getpcm(&adpcm3, (SINT32*)MixTemp, s);
00215 #endif  // defined(SUPPORT_PX)
00216 
00217     pcm86gen_getpcm(NULL, (SINT32*)MixTemp, s);
00218 
00219     pc98_mixer->AddSamples_s32(s, (Bit32s*)MixTemp);
00220 }
00221 
00222 static bool pc98fm_init = false;
00223 
00224 extern "C" {
00225 UINT8 fmtimer_irq2index(const UINT8 irq);
00226 UINT8 fmtimer_index2irq(const UINT8 index);
00227 void fmboard_on_reset();
00228 void rhythm_deinitialize(void);
00229 }
00230 
00231 UINT8 board86_encodeirqidx(const unsigned char idx) {
00232     /* see board86.c to understand what this is about */
00233     return  ((idx & 1) ? 0x08 : 0x00) +
00234             ((idx & 2) ? 0x04 : 0x00);
00235 }
00236 
00237 UINT8 board26k_encodeirqidx(const unsigned char idx) {
00238     /* see board26k.c to understand what this is about */
00239     return  (idx << 6);
00240 }
00241 
00242 void PC98_FM_Destroy(Section *sec) {
00243     (void)sec;//UNUSED
00244     if (pc98fm_init) {
00245         rhythm_deinitialize();
00246     }
00247 
00248     if (pc98_mixer) {
00249         MIXER_DelChannel(pc98_mixer);
00250         pc98_mixer = NULL;
00251     }
00252 }
00253 
00254 void pc98_set_msw4_soundbios(void)
00255 {
00256     /* Set MSW4 bit 3 for sound BIOS. */
00257     if(pc98_soundbios_enabled)
00258         pc98_mem_msw_m[3/*MSW4*/] |= 0x8;
00259     else
00260         pc98_mem_msw_m[3/*MSW4*/] &= ~((unsigned char)0x8);
00261 }
00262 
00263 static CALLBACK_HandlerObject soundbios_callback;
00264 
00265 static Bitu SOUNDROM_INTD2_PC98_Handler(void) {
00266     LOG_MSG("PC-98 SOUND BIOS (INT D2h) call with AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X",
00267         reg_ax,
00268         reg_bx,
00269         reg_cx,
00270         reg_dx,
00271         reg_si,
00272         reg_di,
00273         SegValue(ds),
00274         SegValue(es));
00275 
00276     // guessing, from Yksoft1's patch
00277     reg_ah = 0;
00278 
00279     return CBRET_NONE;
00280 }
00281 
00282 bool PC98_FM_SoundBios_Enabled(void) {
00283     return pc98_soundbios_enabled;
00284 }
00285 
00286 void PC98_FM_OnEnterPC98(Section *sec) {
00287     (void)sec;//UNUSED
00288     Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00289     bool was_pc98fm_init = pc98fm_init;
00290 
00291     if (!pc98fm_init) {
00292         unsigned char fmirqidx;
00293         unsigned int baseio;
00294         std::string board;
00295         int irq;
00296 
00297         soundbios_callback.Uninstall();
00298 
00299         board = section->Get_string("pc-98 fm board");
00300         if (board == "off" || board == "false") {
00301             /* Don't enable Sound BIOS if sound board itself is disabled. */
00302             pc98_soundbios_enabled = false;
00303             pc98_set_msw4_soundbios();
00304             return;             
00305         }
00306 
00307         irq = section->Get_int("pc-98 fm board irq");
00308         baseio = (unsigned int)section->Get_hex("pc-98 fm board io port");
00309 
00310         pc98_soundbios_enabled = section->Get_bool("pc-98 sound bios");
00311         pc98_set_msw4_soundbios();
00312 
00313         if (pc98_soundbios_enabled) {
00314             /* TODO: Load SOUND.ROM to CC000h - CFFFFh when Sound BIOS is enabled? 
00315              * Or simulate Sound BIOS calls ourselves? */
00316             if (false/*TODO: Loaded SOUND.ROM*/) {
00317                 /* TODO */
00318             }
00319             else {
00320                 soundbios_callback.Install(&SOUNDROM_INTD2_PC98_Handler,CB_IRET,"Sound ROM INT D2h");
00321 
00322                 /* fake INT D2h sound BIOS entry point at CEE0:0000.
00323                  * Unlike the LIO interface there's only one interrupt (if Neko Project II code is correct) */
00324                 Bit32u ofs = 0xCEE0u << 4u;
00325                 Bit32u callback_addr = soundbios_callback.Get_RealPointer();
00326 
00327                 phys_writed(ofs+0,0x0001);      // number of entries
00328                 phys_writew(ofs+4,0xD2);        // INT D2h entry point
00329                 phys_writew(ofs+6,0x08);
00330                 phys_writeb(ofs+8,0xEA);        // JMP FAR <callback>
00331                 phys_writed(ofs+9,callback_addr);
00332             }
00333         }
00334 
00335         /* Manual testing shows PC-98 games like it when the board is on IRQ 12 */
00336         if (irq == 0) irq = 12;
00337 
00338         fmirqidx = fmtimer_irq2index(irq);
00339 
00340         pc98fm_init = true;
00341 
00342         unsigned int rate = 44100;
00343 
00344         memset(&pccore,0,sizeof(pccore));
00345         pccore.samplingrate = 44100;
00346                 pccore.baseclock = PIT_TICK_RATE;
00347                 pccore.multiple = 1;
00348         for (unsigned int i=0;i < 6;i++) pccore.vol14[i] = 0xFF;
00349         pccore.vol_fm = 128;
00350         pccore.vol_ssg = 128;
00351         pccore.vol_adpcm = 128;
00352         pccore.vol_pcm = 128;
00353         pccore.vol_rhythm = 128;
00354 
00355         //      fddmtrsnd_initialize(rate);
00356         //      beep_initialize(rate);
00357         //      beep_setvol(3);
00358         tms3631_initialize(rate);
00359         tms3631_setvol(pccore.vol14);
00360         opngen_initialize(rate);
00361         opngen_setvol(pccore.vol_fm);
00362         psggen_initialize(rate);
00363         psggen_setvol(pccore.vol_ssg);
00364         rhythm_initialize(rate);
00365         rhythm_setvol(pccore.vol_rhythm);
00366         adpcm_initialize(rate);
00367         adpcm_setvol(pccore.vol_adpcm);
00368         pcm86gen_initialize(rate);
00369         pcm86gen_setvol(pccore.vol_pcm);
00370 
00371         if (board == "board86c" || board == "auto") {
00372             if (baseio == 0 || baseio == 0x188) { /* default */
00373                 pccore.snd86opt |= 0x01;
00374                 baseio = 0x188;
00375             }
00376             else {
00377                 baseio = 0x288;
00378             }
00379 
00380             pccore.snd86opt += board86_encodeirqidx(fmirqidx);
00381 
00382             LOG_MSG("PC-98 FM board is PC-9801-86c at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx));
00383             fmboard_reset(&np2cfg, 0x14);
00384         }
00385         else if (board == "board86") {
00386             if (baseio == 0 || baseio == 0x188) { /* default */
00387                 pccore.snd86opt |= 0x01;
00388                 baseio = 0x188;
00389             }
00390             else {
00391                 baseio = 0x288;
00392             }
00393 
00394             pccore.snd86opt += board86_encodeirqidx(fmirqidx);
00395 
00396             LOG_MSG("PC-98 FM board is PC-9801-86 at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx));
00397             fmboard_reset(&np2cfg, 0x04);
00398         }
00399         else if (board == "board26k") {
00400             if (baseio == 0x188) {
00401                 pccore.snd26opt |= 0x10;
00402                 baseio = 0x188;
00403             }
00404             else { /* default */
00405                 baseio = 0x088;
00406             }
00407 
00408             pccore.snd26opt += board26k_encodeirqidx(fmirqidx);
00409 
00410             LOG_MSG("PC-98 FM board is PC-9801-26k at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx));
00411             fmboard_reset(&np2cfg, 0x02);
00412         }
00413         else {
00414             if (baseio == 0 || baseio == 0x188) { /* default */
00415                 pccore.snd86opt |= 0x01;
00416                 baseio = 0x188;
00417             }
00418             else {
00419                 baseio = 0x288;
00420             }
00421 
00422             pccore.snd86opt += board86_encodeirqidx(fmirqidx);
00423 
00424             LOG_MSG("PC-98 FM board is PC-9801-86c at baseio=0x%x irq=%d",baseio,fmtimer_index2irq(fmirqidx));
00425             fmboard_reset(&np2cfg, 0x14);   // board86c, a good default
00426         }
00427 
00428         fmboard_bind();
00429         fmboard_extenable(true);
00430 
00431         // WARNING: Some parts of the borrowed code assume 44100, 22050, or 11025 and
00432         //          will misrender if given any other sample rate (especially the OPNA synth).
00433 
00434         pc98_mixer = MIXER_AddChannel(pc98_mix_CallBack, rate, "PC-98");
00435         pc98_mixer->Enable(true);
00436     }
00437 
00438     if (was_pc98fm_init) {
00439         fmboard_on_reset();
00440         fmboard_bind(); // FIXME: Re-binds I/O ports as well
00441         fmboard_extenable(true);
00442     }
00443 }
00444