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