From 09e546591a3a3e1a8c042e6c7dafce1cf5af19f7 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Fri, 27 Nov 2015 02:02:41 -0800 Subject: [PATCH] Updated Game_Music_Emu to latest VGMPlay branch --- Frameworks/GME/GME.xcodeproj/project.pbxproj | 979 +-- Frameworks/GME/gme/Ay_Apu.cpp | 824 +-- Frameworks/GME/gme/Ay_Apu.h | 246 +- Frameworks/GME/gme/Ay_Core.cpp | 380 +- Frameworks/GME/gme/Ay_Core.h | 162 +- Frameworks/GME/gme/Ay_Cpu.cpp | 118 +- Frameworks/GME/gme/Ay_Emu.h | 120 +- Frameworks/GME/gme/C140_Emu.cpp | 77 - Frameworks/GME/gme/C140_Emu.h | 36 - Frameworks/GME/gme/Chip_Resampler.h | 147 - Frameworks/GME/gme/Classic_Emu.cpp | 248 +- Frameworks/GME/gme/Classic_Emu.h | 158 +- Frameworks/GME/gme/Data_Reader.cpp | 630 +- Frameworks/GME/gme/Data_Reader.h | 302 +- Frameworks/GME/gme/Downsampler.cpp | 148 +- Frameworks/GME/gme/Downsampler.h | 50 +- Frameworks/GME/gme/Dual_Resampler.h | 122 +- Frameworks/GME/gme/Effects_Buffer.cpp | 14 +- Frameworks/GME/gme/Effects_Buffer.h | 298 +- Frameworks/GME/gme/Fir_Resampler.cpp | 246 +- Frameworks/GME/gme/Fir_Resampler.h | 202 +- Frameworks/GME/gme/Gb_Apu.cpp | 814 +-- Frameworks/GME/gme/Gb_Apu.h | 386 +- Frameworks/GME/gme/Gb_Cpu.cpp | 102 +- Frameworks/GME/gme/Gb_Cpu.h | 164 +- Frameworks/GME/gme/Gb_Cpu_run.h | 2366 ++++---- Frameworks/GME/gme/Gb_Oscs.cpp | 1424 ++--- Frameworks/GME/gme/Gb_Oscs.h | 376 +- Frameworks/GME/gme/Gbs_Core.cpp | 416 +- Frameworks/GME/gme/Gbs_Core.h | 218 +- Frameworks/GME/gme/Gbs_Cpu.cpp | 268 +- Frameworks/GME/gme/Gbs_Emu.cpp | 332 +- Frameworks/GME/gme/Gbs_Emu.h | 126 +- Frameworks/GME/gme/Gme_Loader.cpp | 172 +- Frameworks/GME/gme/Gme_Loader.h | 184 +- Frameworks/GME/gme/Hes_Apu.cpp | 722 +-- Frameworks/GME/gme/Hes_Apu.h | 174 +- Frameworks/GME/gme/Hes_Apu_Adpcm.h | 188 +- Frameworks/GME/gme/Hes_Core.cpp | 816 +-- Frameworks/GME/gme/Hes_Core.h | 240 +- Frameworks/GME/gme/Hes_Cpu.cpp | 246 +- Frameworks/GME/gme/Hes_Cpu.h | 278 +- Frameworks/GME/gme/Hes_Cpu_run.h | 2684 ++++----- Frameworks/GME/gme/Hes_Emu.h | 84 +- Frameworks/GME/gme/K051649_Emu.cpp | 73 - Frameworks/GME/gme/K051649_Emu.h | 33 - Frameworks/GME/gme/K053260_Emu.cpp | 77 - Frameworks/GME/gme/K053260_Emu.h | 36 - Frameworks/GME/gme/K054539_Emu.cpp | 79 - Frameworks/GME/gme/K054539_Emu.h | 36 - Frameworks/GME/gme/Kss_Core.cpp | 428 +- Frameworks/GME/gme/Kss_Core.h | 200 +- Frameworks/GME/gme/Kss_Emu.cpp | 984 +-- Frameworks/GME/gme/Kss_Emu.h | 158 +- Frameworks/GME/gme/Kss_Scc_Apu.cpp | 248 +- Frameworks/GME/gme/Kss_Scc_Apu.h | 222 +- Frameworks/GME/gme/Multi_Buffer.h | 438 +- Frameworks/GME/gme/Music_Emu.cpp | 2 +- Frameworks/GME/gme/Nes_Apu.cpp | 788 +-- Frameworks/GME/gme/Nes_Apu.h | 368 +- Frameworks/GME/gme/Nes_Cpu.cpp | 124 +- Frameworks/GME/gme/Nes_Cpu.h | 262 +- Frameworks/GME/gme/Nes_Fds_Apu.cpp | 560 +- Frameworks/GME/gme/Nes_Fds_Apu.h | 278 +- Frameworks/GME/gme/Nes_Fme7_Apu.cpp | 242 +- Frameworks/GME/gme/Nes_Fme7_Apu.h | 262 +- Frameworks/GME/gme/Nes_Mmc5_Apu.h | 140 +- Frameworks/GME/gme/Nes_Namco_Apu.h | 204 +- Frameworks/GME/gme/Nes_Oscs.cpp | 1156 ++-- Frameworks/GME/gme/Nes_Oscs.h | 294 +- Frameworks/GME/gme/Nes_Vrc6_Apu.cpp | 432 +- Frameworks/GME/gme/Nes_Vrc6_Apu.h | 190 +- Frameworks/GME/gme/Nes_Vrc7_Apu.cpp | 64 +- Frameworks/GME/gme/Nes_Vrc7_Apu.h | 162 +- Frameworks/GME/gme/Nsf_Core.cpp | 604 +- Frameworks/GME/gme/Nsf_Core.h | 136 +- Frameworks/GME/gme/Nsf_Cpu.cpp | 232 +- Frameworks/GME/gme/Nsf_Emu.cpp | 16 +- Frameworks/GME/gme/Nsf_Emu.h | 110 +- Frameworks/GME/gme/Nsf_Impl.cpp | 656 +- Frameworks/GME/gme/Nsf_Impl.h | 388 +- Frameworks/GME/gme/Nsfe_Emu.h | 148 +- Frameworks/GME/gme/Okim6258_Emu.cpp | 71 - Frameworks/GME/gme/Okim6258_Emu.h | 32 - Frameworks/GME/gme/Okim6295_Emu.cpp | 77 - Frameworks/GME/gme/Okim6295_Emu.h | 36 - Frameworks/GME/gme/Opl_Apu.cpp | 63 +- Frameworks/GME/gme/Pwm_Emu.cpp | 66 - Frameworks/GME/gme/Pwm_Emu.h | 33 - Frameworks/GME/gme/Qsound_Apu.cpp | 83 - Frameworks/GME/gme/Qsound_Apu.h | 36 - Frameworks/GME/gme/Resampler.cpp | 158 +- Frameworks/GME/gme/Resampler.h | 220 +- Frameworks/GME/gme/Rf5C164_Emu.cpp | 82 - Frameworks/GME/gme/Rf5C164_Emu.h | 39 - Frameworks/GME/gme/Rf5C68_Emu.cpp | 82 - Frameworks/GME/gme/Rf5C68_Emu.h | 39 - Frameworks/GME/gme/Sap_Apu.cpp | 678 +-- Frameworks/GME/gme/Sap_Apu.h | 206 +- Frameworks/GME/gme/Sap_Core.cpp | 384 +- Frameworks/GME/gme/Sap_Core.h | 182 +- Frameworks/GME/gme/Sap_Cpu.cpp | 192 +- Frameworks/GME/gme/Sap_Emu.h | 106 +- Frameworks/GME/gme/SegaPcm_Emu.cpp | 77 - Frameworks/GME/gme/SegaPcm_Emu.h | 36 - Frameworks/GME/gme/Sms_Apu.cpp | 742 +-- Frameworks/GME/gme/Sms_Apu.h | 256 +- Frameworks/GME/gme/Spc_Emu.h | 186 +- Frameworks/GME/gme/Spc_Filter.cpp | 214 +- Frameworks/GME/gme/Spc_Filter.h | 114 +- Frameworks/GME/gme/Upsampler.cpp | 146 +- Frameworks/GME/gme/Upsampler.h | 50 +- Frameworks/GME/gme/Vgm_Core.cpp | 2491 +------- Frameworks/GME/gme/Vgm_Core.h | 411 +- Frameworks/GME/gme/Vgm_Emu.cpp | 1084 ++-- Frameworks/GME/gme/Vgm_Emu.h | 130 +- Frameworks/GME/gme/Ym2151_Emu.cpp | 75 - Frameworks/GME/gme/Ym2151_Emu.h | 33 - Frameworks/GME/gme/Ym2203_Emu.cpp | 155 - Frameworks/GME/gme/Ym2203_Emu.h | 45 - Frameworks/GME/gme/Ym2413_Emu.cpp | 34 +- Frameworks/GME/gme/Ym2413_Emu.h | 2 - Frameworks/GME/gme/Ym2608_Emu.cpp | 167 - Frameworks/GME/gme/Ym2608_Emu.h | 49 - Frameworks/GME/gme/Ym2610b_Emu.cpp | 173 - Frameworks/GME/gme/Ym2610b_Emu.h | 50 - Frameworks/GME/gme/Ym2612_Emu.cpp | 15 +- Frameworks/GME/gme/Ym2612_Emu.h | 88 +- Frameworks/GME/gme/Ym2612_Emu_Gens.cpp | 2602 ++++---- Frameworks/GME/gme/Ym2612_Emu_MAME.cpp | 16 +- Frameworks/GME/gme/Ym3812_Emu.cpp | 66 - Frameworks/GME/gme/Ym3812_Emu.h | 35 - Frameworks/GME/gme/Ymf262_Emu.cpp | 93 - Frameworks/GME/gme/Ymf262_Emu.h | 36 - Frameworks/GME/gme/Ymz280b_Emu.cpp | 78 - Frameworks/GME/gme/Ymz280b_Emu.h | 36 - Frameworks/GME/gme/Z80_Cpu.cpp | 164 +- Frameworks/GME/gme/adlib.h | 154 - Frameworks/GME/gme/blargg_common.cpp | 266 + Frameworks/GME/gme/blargg_common.h | 31 +- Frameworks/GME/gme/blargg_source.h | 2 + Frameworks/GME/gme/c140.h | 58 - Frameworks/GME/gme/dac_control.h | 44 - Frameworks/GME/gme/dbopl.cpp | 1526 ----- Frameworks/GME/gme/dbopl.h | 292 - Frameworks/GME/gme/divfix.h | 18 - Frameworks/GME/gme/emuconfig.h | 78 - Frameworks/GME/gme/i_fmpac.h | 38 - Frameworks/GME/gme/i_fmunit.h | 38 - Frameworks/GME/gme/i_vrc7.h | 38 - Frameworks/GME/gme/k051649.c | 298 - Frameworks/GME/gme/k051649.h | 45 - Frameworks/GME/gme/kmsnddev.h | 31 - Frameworks/GME/gme/mathdefs.h | 15 - Frameworks/GME/gme/nestypes.h | 39 - Frameworks/GME/gme/qmix.c | 462 -- Frameworks/GME/gme/qmix.h | 29 - Frameworks/GME/gme/s_deltat.c | 281 - Frameworks/GME/gme/s_deltat.h | 23 - Frameworks/GME/gme/s_logtbl.c | 88 - Frameworks/GME/gme/s_logtbl.h | 43 - Frameworks/GME/gme/s_opl.c | 1244 ---- Frameworks/GME/gme/s_opl.h | 26 - Frameworks/GME/gme/s_opltbl.c | 136 - Frameworks/GME/gme/s_opltbl.h | 38 - Frameworks/GME/gme/ym2413.h | 50 - Frameworks/GME/vgmplay/.gitignore | 3 + Frameworks/GME/vgmplay/ChipMapper.c | 170 + Frameworks/GME/vgmplay/ChipMapper.h | 1 + Frameworks/GME/vgmplay/Makefile | 202 + Frameworks/GME/vgmplay/PortTalk_IOCTL.h | 37 + Frameworks/GME/vgmplay/SourceReadme.txt | 31 + Frameworks/GME/vgmplay/VGMFile.h | 147 + Frameworks/GME/vgmplay/VGMPlay.c | 5150 ++++++++++++++++ Frameworks/GME/vgmplay/VGMPlay.dsp | 1096 ++++ Frameworks/GME/vgmplay/VGMPlay.dsw | 29 + Frameworks/GME/vgmplay/VGMPlay.h | 295 + Frameworks/GME/vgmplay/VGMPlay.ini | 344 ++ Frameworks/GME/vgmplay/VGMPlay.txt | 159 + Frameworks/GME/vgmplay/VGMPlayUI.c | 2672 ++++++++ Frameworks/GME/vgmplay/VGMPlay_AddFmts.c | 1155 ++++ Frameworks/GME/vgmplay/VGMPlay_Intf.h | 75 + Frameworks/GME/vgmplay/VGMPlay_Updates.txt | 130 + .../GME/vgmplay/XMasFiles/SWJ-SQRC01_1C.h | 403 ++ .../SWJ-SQRC01_1C_trimmed_optimized.vgz | Bin 0 -> 6408 bytes Frameworks/GME/vgmplay/XMasFiles/WEWISH.CMF | Bin 0 -> 8773 bytes Frameworks/GME/vgmplay/XMasFiles/XMasBonus.h | 2255 +++++++ Frameworks/GME/vgmplay/XMasFiles/clyde1_1.dro | Bin 0 -> 7667 bytes .../GME/vgmplay/XMasFiles/lem_xmas_001_jb.dro | Bin 0 -> 12544 bytes .../vgmplay/XMasFiles/lemmings_012_tim7.vgm | Bin 0 -> 13638 bytes Frameworks/GME/vgmplay/XMasFiles/rudolph.dro | Bin 0 -> 5201 bytes Frameworks/GME/vgmplay/chips/2151intf.c | 195 + Frameworks/GME/vgmplay/chips/2151intf.h | 33 + Frameworks/GME/vgmplay/chips/2203intf.c | 462 ++ Frameworks/GME/vgmplay/chips/2203intf.h | 43 + Frameworks/GME/vgmplay/chips/2413intf.c | 313 + Frameworks/GME/vgmplay/chips/2413intf.h | 22 + Frameworks/GME/vgmplay/chips/2413tone.h | 20 + Frameworks/GME/vgmplay/chips/2608intf.c | 486 ++ Frameworks/GME/vgmplay/chips/2608intf.h | 54 + Frameworks/GME/vgmplay/chips/2610intf.c | 492 ++ Frameworks/GME/vgmplay/chips/2610intf.h | 59 + Frameworks/GME/vgmplay/chips/2612intf.c | 412 ++ Frameworks/GME/vgmplay/chips/2612intf.h | 72 + Frameworks/GME/vgmplay/chips/262intf.c | 321 + Frameworks/GME/vgmplay/chips/262intf.h | 41 + Frameworks/GME/vgmplay/chips/281btone.h | 20 + Frameworks/GME/vgmplay/chips/3526intf.c | 225 + Frameworks/GME/vgmplay/chips/3526intf.h | 33 + Frameworks/GME/vgmplay/chips/3812intf.c | 330 + Frameworks/GME/vgmplay/chips/3812intf.h | 34 + Frameworks/GME/vgmplay/chips/8950intf.c | 275 + Frameworks/GME/vgmplay/chips/8950intf.h | 40 + Frameworks/GME/vgmplay/chips/ChipIncl.h | 44 + Frameworks/GME/vgmplay/chips/Ootake_PSG.c | 1055 ++++ Frameworks/GME/vgmplay/chips/Ootake_PSG.h | 114 + Frameworks/GME/vgmplay/chips/adlibemu.h | 20 + Frameworks/GME/vgmplay/chips/adlibemu_opl2.c | 5 + Frameworks/GME/vgmplay/chips/adlibemu_opl3.c | 5 + Frameworks/GME/vgmplay/chips/ay8910.c | 1354 +++++ Frameworks/GME/vgmplay/chips/ay8910.h | 143 + Frameworks/GME/vgmplay/chips/ay_intf.c | 160 + Frameworks/GME/vgmplay/chips/ay_intf.h | 11 + Frameworks/GME/{gme => vgmplay/chips}/c140.c | 1258 ++-- Frameworks/GME/vgmplay/chips/c140.h | 36 + Frameworks/GME/vgmplay/chips/c352.c | 733 +++ Frameworks/GME/vgmplay/chips/c352.h | 26 + Frameworks/GME/vgmplay/chips/c6280.c | 452 ++ Frameworks/GME/vgmplay/chips/c6280.h | 24 + Frameworks/GME/vgmplay/chips/c6280intf.c | 167 + Frameworks/GME/vgmplay/chips/c6280intf.h | 11 + .../GME/{gme => vgmplay/chips}/dac_control.c | 839 +-- Frameworks/GME/vgmplay/chips/dac_control.h | 16 + Frameworks/GME/vgmplay/chips/emu2149.c | 524 ++ Frameworks/GME/vgmplay/chips/emu2149.h | 102 + Frameworks/GME/vgmplay/chips/emu2413.c | 2127 +++++++ Frameworks/GME/vgmplay/chips/emu2413.h | 151 + .../GME/vgmplay/chips/emu2413_NESpatches.txt | 1 + Frameworks/GME/vgmplay/chips/emutypes.h | 31 + Frameworks/GME/vgmplay/chips/es5503.c | 624 ++ Frameworks/GME/vgmplay/chips/es5503.h | 34 + Frameworks/GME/vgmplay/chips/es5506.c | 2429 ++++++++ Frameworks/GME/vgmplay/chips/es5506.h | 65 + Frameworks/GME/{gme => vgmplay/chips}/fm.c | 229 +- Frameworks/GME/{gme => vgmplay/chips}/fm.h | 110 +- .../GME/{gme => vgmplay/chips}/fm2612.c | 228 +- .../{gme/fmopl.cpp => vgmplay/chips/fmopl.c} | 5363 +++++++++-------- Frameworks/GME/{gme => vgmplay/chips}/fmopl.h | 238 +- Frameworks/GME/vgmplay/chips/gb.c | 920 +++ Frameworks/GME/vgmplay/chips/gb.h | 13 + Frameworks/GME/vgmplay/chips/iremga20.c | 367 ++ Frameworks/GME/vgmplay/chips/iremga20.h | 30 + Frameworks/GME/vgmplay/chips/k051649.c | 426 ++ Frameworks/GME/vgmplay/chips/k051649.h | 39 + .../GME/{gme => vgmplay/chips}/k053260.c | 108 +- .../GME/{gme => vgmplay/chips}/k053260.h | 26 +- .../GME/{gme => vgmplay/chips}/k054539.c | 522 +- .../GME/{gme => vgmplay/chips}/k054539.h | 29 +- .../GME/{gme => vgmplay/chips}/mamedef.h | 23 +- Frameworks/GME/vgmplay/chips/multipcm.c | 868 +++ Frameworks/GME/vgmplay/chips/multipcm.h | 22 + Frameworks/GME/vgmplay/chips/nes_apu.c | 941 +++ Frameworks/GME/vgmplay/chips/nes_apu.h | 63 + Frameworks/GME/vgmplay/chips/nes_defs.h | 219 + Frameworks/GME/vgmplay/chips/nes_intf.c | 339 ++ Frameworks/GME/vgmplay/chips/nes_intf.h | 13 + Frameworks/GME/vgmplay/chips/np_nes_apu.c | 511 ++ Frameworks/GME/vgmplay/chips/np_nes_apu.h | 12 + Frameworks/GME/vgmplay/chips/np_nes_dmc.c | 871 +++ Frameworks/GME/vgmplay/chips/np_nes_dmc.h | 20 + Frameworks/GME/vgmplay/chips/np_nes_fds.c | 547 ++ Frameworks/GME/vgmplay/chips/np_nes_fds.h | 16 + .../GME/{gme => vgmplay/chips}/okim6258.c | 254 +- .../GME/{gme => vgmplay/chips}/okim6258.h | 33 +- .../GME/{gme => vgmplay/chips}/okim6295.c | 190 +- .../GME/{gme => vgmplay/chips}/okim6295.h | 25 +- Frameworks/GME/vgmplay/chips/opl.c | 2027 +++++++ Frameworks/GME/vgmplay/chips/opl.h | 218 + Frameworks/GME/vgmplay/chips/panning.c | 82 + Frameworks/GME/vgmplay/chips/panning.h | 13 + Frameworks/GME/vgmplay/chips/pokey.c | 1490 +++++ Frameworks/GME/vgmplay/chips/pokey.h | 99 + Frameworks/GME/{gme => vgmplay/chips}/pwm.c | 868 +-- Frameworks/GME/{gme => vgmplay/chips}/pwm.h | 132 +- Frameworks/GME/vgmplay/chips/qsound.c | 483 ++ Frameworks/GME/vgmplay/chips/qsound.h | 29 + .../GME/{gme => vgmplay/chips}/rf5c68.c | 922 +-- .../GME/{gme => vgmplay/chips}/rf5c68.h | 66 +- Frameworks/GME/vgmplay/chips/saa1099.c | 592 ++ Frameworks/GME/vgmplay/chips/saa1099.h | 23 + .../GME/{gme => vgmplay/chips}/scd_pcm.c | 1008 ++-- .../GME/{gme => vgmplay/chips}/scd_pcm.h | 104 +- Frameworks/GME/vgmplay/chips/scsp.c | 1692 ++++++ Frameworks/GME/vgmplay/chips/scsp.h | 47 + Frameworks/GME/vgmplay/chips/scspdsp.c | 356 ++ Frameworks/GME/vgmplay/chips/scspdsp.h | 40 + Frameworks/GME/vgmplay/chips/scsplfo.c | 165 + .../GME/{gme => vgmplay/chips}/segapcm.c | 793 +-- .../GME/{gme => vgmplay/chips}/segapcm.h | 86 +- Frameworks/GME/vgmplay/chips/sn76489.c | 404 ++ Frameworks/GME/vgmplay/chips/sn76489.h | 108 + Frameworks/GME/vgmplay/chips/sn76496.c | 883 +++ Frameworks/GME/vgmplay/chips/sn76496.h | 35 + Frameworks/GME/vgmplay/chips/sn764intf.c | 183 + Frameworks/GME/vgmplay/chips/sn764intf.h | 21 + Frameworks/GME/vgmplay/chips/upd7759.c | 936 +++ Frameworks/GME/vgmplay/chips/upd7759.h | 42 + Frameworks/GME/vgmplay/chips/vrc7tone.h | 20 + Frameworks/GME/vgmplay/chips/vsu.c | 636 ++ Frameworks/GME/vgmplay/chips/vsu.h | 15 + Frameworks/GME/vgmplay/chips/ws_audio.c | 474 ++ Frameworks/GME/vgmplay/chips/ws_audio.h | 16 + Frameworks/GME/vgmplay/chips/ws_initialIo.h | 271 + Frameworks/GME/vgmplay/chips/x1_010.c | 399 ++ Frameworks/GME/vgmplay/chips/x1_010.h | 38 + .../GME/{gme => vgmplay/chips}/ym2151.c | 4548 ++++++++------ .../GME/{gme => vgmplay/chips}/ym2151.h | 176 +- .../GME/{gme => vgmplay/chips}/ym2413.c | 4363 +++++++------- Frameworks/GME/vgmplay/chips/ym2413.h | 43 + Frameworks/GME/vgmplay/chips/ym2612.c | 2551 ++++++++ Frameworks/GME/vgmplay/chips/ym2612.h | 190 + .../ymdeltat.cpp => vgmplay/chips/ymdeltat.c} | 1335 ++-- .../GME/{gme => vgmplay/chips}/ymdeltat.h | 171 +- Frameworks/GME/vgmplay/chips/ymf262.c | 2747 +++++++++ Frameworks/GME/vgmplay/chips/ymf262.h | 49 + Frameworks/GME/vgmplay/chips/ymf271.c | 1982 ++++++ Frameworks/GME/vgmplay/chips/ymf271.h | 30 + Frameworks/GME/vgmplay/chips/ymf278b.c | 1220 ++++ Frameworks/GME/vgmplay/chips/ymf278b.h | 30 + .../GME/{gme => vgmplay/chips}/ymz280b.c | 179 +- .../GME/{gme => vgmplay/chips}/ymz280b.h | 26 +- Frameworks/GME/vgmplay/licenses/GPL.txt | 340 ++ Frameworks/GME/vgmplay/licenses/List.txt | 11 + .../GME/vgmplay/licenses/mame_license.txt | 35 + Frameworks/GME/vgmplay/pt_ioctl.c | 251 + Frameworks/GME/vgmplay/resampler.c | 355 ++ Frameworks/GME/vgmplay/resampler.h | 73 + Frameworks/GME/vgmplay/stdbool.h | 16 + Frameworks/GME/vgmplay/vgm-player | 366 ++ Frameworks/GME/vgmplay/vgm2pcm.c | 87 + Frameworks/GME/vgmplay/vgmplay.1 | 148 + Frameworks/GME/vgmplay/vgmspec171.txt | 847 +++ 342 files changed, 90075 insertions(+), 39784 deletions(-) delete mode 100644 Frameworks/GME/gme/C140_Emu.cpp delete mode 100644 Frameworks/GME/gme/C140_Emu.h delete mode 100644 Frameworks/GME/gme/Chip_Resampler.h delete mode 100644 Frameworks/GME/gme/K051649_Emu.cpp delete mode 100644 Frameworks/GME/gme/K051649_Emu.h delete mode 100644 Frameworks/GME/gme/K053260_Emu.cpp delete mode 100644 Frameworks/GME/gme/K053260_Emu.h delete mode 100644 Frameworks/GME/gme/K054539_Emu.cpp delete mode 100644 Frameworks/GME/gme/K054539_Emu.h delete mode 100644 Frameworks/GME/gme/Okim6258_Emu.cpp delete mode 100644 Frameworks/GME/gme/Okim6258_Emu.h delete mode 100644 Frameworks/GME/gme/Okim6295_Emu.cpp delete mode 100644 Frameworks/GME/gme/Okim6295_Emu.h delete mode 100644 Frameworks/GME/gme/Pwm_Emu.cpp delete mode 100644 Frameworks/GME/gme/Pwm_Emu.h delete mode 100644 Frameworks/GME/gme/Qsound_Apu.cpp delete mode 100644 Frameworks/GME/gme/Qsound_Apu.h delete mode 100644 Frameworks/GME/gme/Rf5C164_Emu.cpp delete mode 100644 Frameworks/GME/gme/Rf5C164_Emu.h delete mode 100644 Frameworks/GME/gme/Rf5C68_Emu.cpp delete mode 100644 Frameworks/GME/gme/Rf5C68_Emu.h delete mode 100644 Frameworks/GME/gme/SegaPcm_Emu.cpp delete mode 100644 Frameworks/GME/gme/SegaPcm_Emu.h delete mode 100644 Frameworks/GME/gme/Ym2151_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ym2151_Emu.h delete mode 100644 Frameworks/GME/gme/Ym2203_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ym2203_Emu.h delete mode 100644 Frameworks/GME/gme/Ym2608_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ym2608_Emu.h delete mode 100644 Frameworks/GME/gme/Ym2610b_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ym2610b_Emu.h delete mode 100644 Frameworks/GME/gme/Ym3812_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ym3812_Emu.h delete mode 100644 Frameworks/GME/gme/Ymf262_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ymf262_Emu.h delete mode 100644 Frameworks/GME/gme/Ymz280b_Emu.cpp delete mode 100644 Frameworks/GME/gme/Ymz280b_Emu.h delete mode 100644 Frameworks/GME/gme/adlib.h delete mode 100644 Frameworks/GME/gme/c140.h delete mode 100644 Frameworks/GME/gme/dac_control.h delete mode 100644 Frameworks/GME/gme/dbopl.cpp delete mode 100644 Frameworks/GME/gme/dbopl.h delete mode 100644 Frameworks/GME/gme/divfix.h delete mode 100644 Frameworks/GME/gme/emuconfig.h delete mode 100644 Frameworks/GME/gme/i_fmpac.h delete mode 100644 Frameworks/GME/gme/i_fmunit.h delete mode 100644 Frameworks/GME/gme/i_vrc7.h delete mode 100644 Frameworks/GME/gme/k051649.c delete mode 100644 Frameworks/GME/gme/k051649.h delete mode 100644 Frameworks/GME/gme/kmsnddev.h delete mode 100644 Frameworks/GME/gme/mathdefs.h delete mode 100644 Frameworks/GME/gme/nestypes.h delete mode 100644 Frameworks/GME/gme/qmix.c delete mode 100644 Frameworks/GME/gme/qmix.h delete mode 100644 Frameworks/GME/gme/s_deltat.c delete mode 100644 Frameworks/GME/gme/s_deltat.h delete mode 100644 Frameworks/GME/gme/s_logtbl.c delete mode 100644 Frameworks/GME/gme/s_logtbl.h delete mode 100644 Frameworks/GME/gme/s_opl.c delete mode 100644 Frameworks/GME/gme/s_opl.h delete mode 100644 Frameworks/GME/gme/s_opltbl.c delete mode 100644 Frameworks/GME/gme/s_opltbl.h delete mode 100644 Frameworks/GME/gme/ym2413.h create mode 100644 Frameworks/GME/vgmplay/.gitignore create mode 100644 Frameworks/GME/vgmplay/ChipMapper.c create mode 100644 Frameworks/GME/vgmplay/ChipMapper.h create mode 100644 Frameworks/GME/vgmplay/Makefile create mode 100644 Frameworks/GME/vgmplay/PortTalk_IOCTL.h create mode 100644 Frameworks/GME/vgmplay/SourceReadme.txt create mode 100644 Frameworks/GME/vgmplay/VGMFile.h create mode 100644 Frameworks/GME/vgmplay/VGMPlay.c create mode 100644 Frameworks/GME/vgmplay/VGMPlay.dsp create mode 100644 Frameworks/GME/vgmplay/VGMPlay.dsw create mode 100644 Frameworks/GME/vgmplay/VGMPlay.h create mode 100644 Frameworks/GME/vgmplay/VGMPlay.ini create mode 100644 Frameworks/GME/vgmplay/VGMPlay.txt create mode 100644 Frameworks/GME/vgmplay/VGMPlayUI.c create mode 100644 Frameworks/GME/vgmplay/VGMPlay_AddFmts.c create mode 100644 Frameworks/GME/vgmplay/VGMPlay_Intf.h create mode 100644 Frameworks/GME/vgmplay/VGMPlay_Updates.txt create mode 100644 Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C.h create mode 100644 Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C_trimmed_optimized.vgz create mode 100644 Frameworks/GME/vgmplay/XMasFiles/WEWISH.CMF create mode 100644 Frameworks/GME/vgmplay/XMasFiles/XMasBonus.h create mode 100644 Frameworks/GME/vgmplay/XMasFiles/clyde1_1.dro create mode 100644 Frameworks/GME/vgmplay/XMasFiles/lem_xmas_001_jb.dro create mode 100644 Frameworks/GME/vgmplay/XMasFiles/lemmings_012_tim7.vgm create mode 100644 Frameworks/GME/vgmplay/XMasFiles/rudolph.dro create mode 100644 Frameworks/GME/vgmplay/chips/2151intf.c create mode 100644 Frameworks/GME/vgmplay/chips/2151intf.h create mode 100644 Frameworks/GME/vgmplay/chips/2203intf.c create mode 100644 Frameworks/GME/vgmplay/chips/2203intf.h create mode 100644 Frameworks/GME/vgmplay/chips/2413intf.c create mode 100644 Frameworks/GME/vgmplay/chips/2413intf.h create mode 100644 Frameworks/GME/vgmplay/chips/2413tone.h create mode 100644 Frameworks/GME/vgmplay/chips/2608intf.c create mode 100644 Frameworks/GME/vgmplay/chips/2608intf.h create mode 100644 Frameworks/GME/vgmplay/chips/2610intf.c create mode 100644 Frameworks/GME/vgmplay/chips/2610intf.h create mode 100644 Frameworks/GME/vgmplay/chips/2612intf.c create mode 100644 Frameworks/GME/vgmplay/chips/2612intf.h create mode 100644 Frameworks/GME/vgmplay/chips/262intf.c create mode 100644 Frameworks/GME/vgmplay/chips/262intf.h create mode 100644 Frameworks/GME/vgmplay/chips/281btone.h create mode 100644 Frameworks/GME/vgmplay/chips/3526intf.c create mode 100644 Frameworks/GME/vgmplay/chips/3526intf.h create mode 100644 Frameworks/GME/vgmplay/chips/3812intf.c create mode 100644 Frameworks/GME/vgmplay/chips/3812intf.h create mode 100644 Frameworks/GME/vgmplay/chips/8950intf.c create mode 100644 Frameworks/GME/vgmplay/chips/8950intf.h create mode 100644 Frameworks/GME/vgmplay/chips/ChipIncl.h create mode 100644 Frameworks/GME/vgmplay/chips/Ootake_PSG.c create mode 100644 Frameworks/GME/vgmplay/chips/Ootake_PSG.h create mode 100644 Frameworks/GME/vgmplay/chips/adlibemu.h create mode 100644 Frameworks/GME/vgmplay/chips/adlibemu_opl2.c create mode 100644 Frameworks/GME/vgmplay/chips/adlibemu_opl3.c create mode 100644 Frameworks/GME/vgmplay/chips/ay8910.c create mode 100644 Frameworks/GME/vgmplay/chips/ay8910.h create mode 100644 Frameworks/GME/vgmplay/chips/ay_intf.c create mode 100644 Frameworks/GME/vgmplay/chips/ay_intf.h rename Frameworks/GME/{gme => vgmplay/chips}/c140.c (89%) create mode 100644 Frameworks/GME/vgmplay/chips/c140.h create mode 100644 Frameworks/GME/vgmplay/chips/c352.c create mode 100644 Frameworks/GME/vgmplay/chips/c352.h create mode 100644 Frameworks/GME/vgmplay/chips/c6280.c create mode 100644 Frameworks/GME/vgmplay/chips/c6280.h create mode 100644 Frameworks/GME/vgmplay/chips/c6280intf.c create mode 100644 Frameworks/GME/vgmplay/chips/c6280intf.h rename Frameworks/GME/{gme => vgmplay/chips}/dac_control.c (53%) create mode 100644 Frameworks/GME/vgmplay/chips/dac_control.h create mode 100644 Frameworks/GME/vgmplay/chips/emu2149.c create mode 100644 Frameworks/GME/vgmplay/chips/emu2149.h create mode 100644 Frameworks/GME/vgmplay/chips/emu2413.c create mode 100644 Frameworks/GME/vgmplay/chips/emu2413.h create mode 100644 Frameworks/GME/vgmplay/chips/emu2413_NESpatches.txt create mode 100644 Frameworks/GME/vgmplay/chips/emutypes.h create mode 100644 Frameworks/GME/vgmplay/chips/es5503.c create mode 100644 Frameworks/GME/vgmplay/chips/es5503.h create mode 100644 Frameworks/GME/vgmplay/chips/es5506.c create mode 100644 Frameworks/GME/vgmplay/chips/es5506.h rename Frameworks/GME/{gme => vgmplay/chips}/fm.c (95%) rename Frameworks/GME/{gme => vgmplay/chips}/fm.h (60%) rename Frameworks/GME/{gme => vgmplay/chips}/fm2612.c (93%) rename Frameworks/GME/{gme/fmopl.cpp => vgmplay/chips/fmopl.c} (85%) rename Frameworks/GME/{gme => vgmplay/chips}/fmopl.h (66%) create mode 100644 Frameworks/GME/vgmplay/chips/gb.c create mode 100644 Frameworks/GME/vgmplay/chips/gb.h create mode 100644 Frameworks/GME/vgmplay/chips/iremga20.c create mode 100644 Frameworks/GME/vgmplay/chips/iremga20.h create mode 100644 Frameworks/GME/vgmplay/chips/k051649.c create mode 100644 Frameworks/GME/vgmplay/chips/k051649.h rename Frameworks/GME/{gme => vgmplay/chips}/k053260.c (75%) rename Frameworks/GME/{gme => vgmplay/chips}/k053260.h (52%) rename Frameworks/GME/{gme => vgmplay/chips}/k054539.c (56%) rename Frameworks/GME/{gme => vgmplay/chips}/k054539.h (69%) rename Frameworks/GME/{gme => vgmplay/chips}/mamedef.h (78%) create mode 100644 Frameworks/GME/vgmplay/chips/multipcm.c create mode 100644 Frameworks/GME/vgmplay/chips/multipcm.h create mode 100644 Frameworks/GME/vgmplay/chips/nes_apu.c create mode 100644 Frameworks/GME/vgmplay/chips/nes_apu.h create mode 100644 Frameworks/GME/vgmplay/chips/nes_defs.h create mode 100644 Frameworks/GME/vgmplay/chips/nes_intf.c create mode 100644 Frameworks/GME/vgmplay/chips/nes_intf.h create mode 100644 Frameworks/GME/vgmplay/chips/np_nes_apu.c create mode 100644 Frameworks/GME/vgmplay/chips/np_nes_apu.h create mode 100644 Frameworks/GME/vgmplay/chips/np_nes_dmc.c create mode 100644 Frameworks/GME/vgmplay/chips/np_nes_dmc.h create mode 100644 Frameworks/GME/vgmplay/chips/np_nes_fds.c create mode 100644 Frameworks/GME/vgmplay/chips/np_nes_fds.h rename Frameworks/GME/{gme => vgmplay/chips}/okim6258.c (67%) rename Frameworks/GME/{gme => vgmplay/chips}/okim6258.h (52%) rename Frameworks/GME/{gme => vgmplay/chips}/okim6295.c (77%) rename Frameworks/GME/{gme => vgmplay/chips}/okim6295.h (67%) create mode 100644 Frameworks/GME/vgmplay/chips/opl.c create mode 100644 Frameworks/GME/vgmplay/chips/opl.h create mode 100644 Frameworks/GME/vgmplay/chips/panning.c create mode 100644 Frameworks/GME/vgmplay/chips/panning.h create mode 100644 Frameworks/GME/vgmplay/chips/pokey.c create mode 100644 Frameworks/GME/vgmplay/chips/pokey.h rename Frameworks/GME/{gme => vgmplay/chips}/pwm.c (91%) rename Frameworks/GME/{gme => vgmplay/chips}/pwm.h (93%) create mode 100644 Frameworks/GME/vgmplay/chips/qsound.c create mode 100644 Frameworks/GME/vgmplay/chips/qsound.h rename Frameworks/GME/{gme => vgmplay/chips}/rf5c68.c (80%) rename Frameworks/GME/{gme => vgmplay/chips}/rf5c68.h (84%) create mode 100644 Frameworks/GME/vgmplay/chips/saa1099.c create mode 100644 Frameworks/GME/vgmplay/chips/saa1099.h rename Frameworks/GME/{gme => vgmplay/chips}/scd_pcm.c (82%) rename Frameworks/GME/{gme => vgmplay/chips}/scd_pcm.h (51%) create mode 100644 Frameworks/GME/vgmplay/chips/scsp.c create mode 100644 Frameworks/GME/vgmplay/chips/scsp.h create mode 100644 Frameworks/GME/vgmplay/chips/scspdsp.c create mode 100644 Frameworks/GME/vgmplay/chips/scspdsp.h create mode 100644 Frameworks/GME/vgmplay/chips/scsplfo.c rename Frameworks/GME/{gme => vgmplay/chips}/segapcm.c (76%) rename Frameworks/GME/{gme => vgmplay/chips}/segapcm.h (82%) create mode 100644 Frameworks/GME/vgmplay/chips/sn76489.c create mode 100644 Frameworks/GME/vgmplay/chips/sn76489.h create mode 100644 Frameworks/GME/vgmplay/chips/sn76496.c create mode 100644 Frameworks/GME/vgmplay/chips/sn76496.h create mode 100644 Frameworks/GME/vgmplay/chips/sn764intf.c create mode 100644 Frameworks/GME/vgmplay/chips/sn764intf.h create mode 100644 Frameworks/GME/vgmplay/chips/upd7759.c create mode 100644 Frameworks/GME/vgmplay/chips/upd7759.h create mode 100644 Frameworks/GME/vgmplay/chips/vrc7tone.h create mode 100644 Frameworks/GME/vgmplay/chips/vsu.c create mode 100644 Frameworks/GME/vgmplay/chips/vsu.h create mode 100644 Frameworks/GME/vgmplay/chips/ws_audio.c create mode 100644 Frameworks/GME/vgmplay/chips/ws_audio.h create mode 100644 Frameworks/GME/vgmplay/chips/ws_initialIo.h create mode 100644 Frameworks/GME/vgmplay/chips/x1_010.c create mode 100644 Frameworks/GME/vgmplay/chips/x1_010.h rename Frameworks/GME/{gme => vgmplay/chips}/ym2151.c (72%) rename Frameworks/GME/{gme => vgmplay/chips}/ym2151.h (81%) rename Frameworks/GME/{gme => vgmplay/chips}/ym2413.c (73%) create mode 100644 Frameworks/GME/vgmplay/chips/ym2413.h create mode 100644 Frameworks/GME/vgmplay/chips/ym2612.c create mode 100644 Frameworks/GME/vgmplay/chips/ym2612.h rename Frameworks/GME/{gme/ymdeltat.cpp => vgmplay/chips/ymdeltat.c} (94%) rename Frameworks/GME/{gme => vgmplay/chips}/ymdeltat.h (90%) create mode 100644 Frameworks/GME/vgmplay/chips/ymf262.c create mode 100644 Frameworks/GME/vgmplay/chips/ymf262.h create mode 100644 Frameworks/GME/vgmplay/chips/ymf271.c create mode 100644 Frameworks/GME/vgmplay/chips/ymf271.h create mode 100644 Frameworks/GME/vgmplay/chips/ymf278b.c create mode 100644 Frameworks/GME/vgmplay/chips/ymf278b.h rename Frameworks/GME/{gme => vgmplay/chips}/ymz280b.c (88%) rename Frameworks/GME/{gme => vgmplay/chips}/ymz280b.h (61%) create mode 100644 Frameworks/GME/vgmplay/licenses/GPL.txt create mode 100644 Frameworks/GME/vgmplay/licenses/List.txt create mode 100644 Frameworks/GME/vgmplay/licenses/mame_license.txt create mode 100644 Frameworks/GME/vgmplay/pt_ioctl.c create mode 100644 Frameworks/GME/vgmplay/resampler.c create mode 100644 Frameworks/GME/vgmplay/resampler.h create mode 100644 Frameworks/GME/vgmplay/stdbool.h create mode 100644 Frameworks/GME/vgmplay/vgm-player create mode 100644 Frameworks/GME/vgmplay/vgm2pcm.c create mode 100644 Frameworks/GME/vgmplay/vgmplay.1 create mode 100644 Frameworks/GME/vgmplay/vgmspec171.txt diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index b1e962265..4aab1d7d6 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -90,7 +90,6 @@ 17C8F2560CBED286008D969D /* Ym2413_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1ED0CBED286008D969D /* Ym2413_Emu.h */; }; 17C8F2570CBED286008D969D /* Ym2612_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EE0CBED286008D969D /* Ym2612_Emu.cpp */; }; 17C8F2580CBED286008D969D /* Ym2612_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1EF0CBED286008D969D /* Ym2612_Emu.h */; }; - 8370B72F17F615FE001A4D7A /* adlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B68C17F615FD001A4D7A /* adlib.h */; }; 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */; }; 8370B73117F615FE001A4D7A /* Ay_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B68E17F615FD001A4D7A /* Ay_Core.h */; }; 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B68F17F615FD001A4D7A /* blargg_common.cpp */; }; @@ -100,24 +99,8 @@ 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */; }; 8370B73717F615FE001A4D7A /* Bml_Parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */; }; 8370B73817F615FE001A4D7A /* Bml_Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69517F615FD001A4D7A /* Bml_Parser.h */; }; - 8370B73917F615FE001A4D7A /* C140_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69617F615FD001A4D7A /* C140_Emu.cpp */; }; - 8370B73A17F615FE001A4D7A /* C140_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69717F615FD001A4D7A /* C140_Emu.h */; }; - 8370B73B17F615FE001A4D7A /* c140.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69817F615FD001A4D7A /* c140.c */; }; - 8370B73C17F615FE001A4D7A /* c140.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69917F615FD001A4D7A /* c140.h */; }; - 8370B73D17F615FE001A4D7A /* Chip_Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */; }; - 8370B73E17F615FE001A4D7A /* dac_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69B17F615FD001A4D7A /* dac_control.c */; }; - 8370B73F17F615FE001A4D7A /* dac_control.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69C17F615FD001A4D7A /* dac_control.h */; }; - 8370B74017F615FE001A4D7A /* dbopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69D17F615FD001A4D7A /* dbopl.cpp */; }; - 8370B74117F615FE001A4D7A /* dbopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69E17F615FD001A4D7A /* dbopl.h */; }; - 8370B74217F615FE001A4D7A /* divfix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69F17F615FD001A4D7A /* divfix.h */; }; 8370B74317F615FE001A4D7A /* Downsampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A017F615FD001A4D7A /* Downsampler.cpp */; }; 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A117F615FD001A4D7A /* Downsampler.h */; }; - 8370B74517F615FE001A4D7A /* emuconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A217F615FD001A4D7A /* emuconfig.h */; }; - 8370B74617F615FE001A4D7A /* fm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A317F615FD001A4D7A /* fm.c */; }; - 8370B74717F615FE001A4D7A /* fm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A417F615FD001A4D7A /* fm.h */; }; - 8370B74817F615FE001A4D7A /* fm2612.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A517F615FD001A4D7A /* fm2612.c */; }; - 8370B74917F615FE001A4D7A /* fmopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A617F615FD001A4D7A /* fmopl.cpp */; }; - 8370B74A17F615FE001A4D7A /* fmopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A717F615FD001A4D7A /* fmopl.h */; }; 8370B74B17F615FE001A4D7A /* Gb_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */; }; 8370B74C17F615FE001A4D7A /* Gbs_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */; }; 8370B74D17F615FE001A4D7A /* Gbs_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */; }; @@ -129,82 +112,27 @@ 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */; }; 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B317F615FD001A4D7A /* Hes_Core.h */; }; 8370B75717F615FE001A4D7A /* Hes_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */; }; - 8370B75817F615FE001A4D7A /* i_fmpac.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B517F615FD001A4D7A /* i_fmpac.h */; }; - 8370B75917F615FE001A4D7A /* i_fmunit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B617F615FD001A4D7A /* i_fmunit.h */; }; - 8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B717F615FD001A4D7A /* i_vrc7.h */; }; - 8370B75B17F615FE001A4D7A /* K051649_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */; }; - 8370B75C17F615FE001A4D7A /* K051649_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B917F615FD001A4D7A /* K051649_Emu.h */; }; - 8370B75D17F615FE001A4D7A /* k051649.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BA17F615FD001A4D7A /* k051649.c */; }; - 8370B75E17F615FE001A4D7A /* k051649.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BB17F615FD001A4D7A /* k051649.h */; }; - 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */; }; - 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */; }; - 8370B76117F615FE001A4D7A /* k053260.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BE17F615FD001A4D7A /* k053260.c */; }; - 8370B76217F615FE001A4D7A /* k053260.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BF17F615FD001A4D7A /* k053260.h */; }; - 8370B76317F615FE001A4D7A /* K054539_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */; }; - 8370B76417F615FE001A4D7A /* K054539_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C117F615FD001A4D7A /* K054539_Emu.h */; }; - 8370B76517F615FE001A4D7A /* k054539.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C217F615FD001A4D7A /* k054539.c */; }; - 8370B76617F615FE001A4D7A /* k054539.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C317F615FD001A4D7A /* k054539.h */; }; - 8370B76717F615FE001A4D7A /* kmsnddev.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C417F615FD001A4D7A /* kmsnddev.h */; }; 8370B76817F615FE001A4D7A /* Kss_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */; }; 8370B76917F615FE001A4D7A /* Kss_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C617F615FD001A4D7A /* Kss_Core.h */; }; - 8370B76A17F615FE001A4D7A /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C717F615FD001A4D7A /* mamedef.h */; }; - 8370B76B17F615FE001A4D7A /* mathdefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C817F615FD001A4D7A /* mathdefs.h */; }; 8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */; }; 8370B76D17F615FE001A4D7A /* Nes_Fds_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */; }; 8370B76E17F615FE001A4D7A /* Nes_Fds_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */; }; 8370B76F17F615FE001A4D7A /* Nes_Mmc5_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */; }; 8370B77017F615FE001A4D7A /* Nes_Vrc7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */; }; 8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */; }; - 8370B77217F615FE001A4D7A /* nestypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CF17F615FD001A4D7A /* nestypes.h */; }; 8370B77317F615FE001A4D7A /* Nsf_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */; }; 8370B77417F615FE001A4D7A /* Nsf_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D117F615FD001A4D7A /* Nsf_Core.h */; }; 8370B77517F615FE001A4D7A /* Nsf_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */; }; 8370B77617F615FE001A4D7A /* Nsf_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */; }; 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */; }; - 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */; }; - 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */; }; - 8370B77A17F615FE001A4D7A /* okim6258.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D717F615FD001A4D7A /* okim6258.c */; }; - 8370B77B17F615FE001A4D7A /* okim6258.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D817F615FD001A4D7A /* okim6258.h */; }; - 8370B77C17F615FE001A4D7A /* Okim6295_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */; }; - 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */; }; - 8370B77E17F615FE001A4D7A /* okim6295.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DB17F615FD001A4D7A /* okim6295.c */; }; - 8370B77F17F615FE001A4D7A /* okim6295.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DC17F615FD001A4D7A /* okim6295.h */; }; 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */; }; 8370B78117F615FE001A4D7A /* Opl_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */; }; - 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */; }; - 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */; }; - 8370B78417F615FE001A4D7A /* pwm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E117F615FE001A4D7A /* pwm.c */; }; - 8370B78517F615FE001A4D7A /* pwm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E217F615FE001A4D7A /* pwm.h */; }; - 8370B78617F615FE001A4D7A /* qmix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E317F615FE001A4D7A /* qmix.c */; }; - 8370B78717F615FE001A4D7A /* qmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E417F615FE001A4D7A /* qmix.h */; }; - 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */; }; - 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */; }; 8370B78A17F615FE001A4D7A /* Resampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E717F615FE001A4D7A /* Resampler.cpp */; }; 8370B78B17F615FE001A4D7A /* Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E817F615FE001A4D7A /* Resampler.h */; }; - 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */; }; - 8370B78D17F615FE001A4D7A /* Rf5C68_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */; }; - 8370B78E17F615FE001A4D7A /* rf5c68.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6EB17F615FE001A4D7A /* rf5c68.c */; }; - 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EC17F615FE001A4D7A /* rf5c68.h */; }; - 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */; }; - 8370B79117F615FE001A4D7A /* Rf5C164_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */; }; 8370B79217F615FE001A4D7A /* Rom_Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */; }; 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F017F615FE001A4D7A /* Rom_Data.h */; }; - 8370B79417F615FE001A4D7A /* s_deltat.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F117F615FE001A4D7A /* s_deltat.c */; }; - 8370B79517F615FE001A4D7A /* s_deltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F217F615FE001A4D7A /* s_deltat.h */; }; - 8370B79617F615FE001A4D7A /* s_logtbl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F317F615FE001A4D7A /* s_logtbl.c */; }; - 8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F417F615FE001A4D7A /* s_logtbl.h */; }; - 8370B79817F615FE001A4D7A /* s_opl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F517F615FE001A4D7A /* s_opl.c */; }; - 8370B79917F615FE001A4D7A /* s_opl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F617F615FE001A4D7A /* s_opl.h */; }; - 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F717F615FE001A4D7A /* s_opltbl.c */; }; - 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F817F615FE001A4D7A /* s_opltbl.h */; }; 8370B79C17F615FE001A4D7A /* Sap_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */; }; 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FA17F615FE001A4D7A /* Sap_Core.h */; }; - 8370B79E17F615FE001A4D7A /* scd_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FB17F615FE001A4D7A /* scd_pcm.c */; }; - 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FC17F615FE001A4D7A /* scd_pcm.h */; }; - 8370B7A017F615FE001A4D7A /* SegaPcm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */; }; - 8370B7A117F615FE001A4D7A /* SegaPcm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */; }; - 8370B7A217F615FE001A4D7A /* segapcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FF17F615FE001A4D7A /* segapcm.c */; }; - 8370B7A317F615FE001A4D7A /* segapcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70017F615FE001A4D7A /* segapcm.h */; }; 8370B7A417F615FE001A4D7A /* Sgc_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */; }; 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70217F615FE001A4D7A /* Sgc_Core.h */; }; 8370B7A617F615FE001A4D7A /* Sgc_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */; }; @@ -224,31 +152,154 @@ 8370B7B417F615FE001A4D7A /* Upsampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71117F615FE001A4D7A /* Upsampler.h */; }; 8370B7B517F615FE001A4D7A /* Vgm_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */; }; 8370B7B617F615FE001A4D7A /* Vgm_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71317F615FE001A4D7A /* Vgm_Core.h */; }; - 8370B7B717F615FE001A4D7A /* Ym2151_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */; }; - 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */; }; - 8370B7B917F615FE001A4D7A /* ym2151.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71617F615FE001A4D7A /* ym2151.c */; }; - 8370B7BA17F615FE001A4D7A /* ym2151.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71717F615FE001A4D7A /* ym2151.h */; }; - 8370B7BB17F615FE001A4D7A /* Ym2203_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */; }; - 8370B7BC17F615FE001A4D7A /* Ym2203_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */; }; - 8370B7BD17F615FE001A4D7A /* ym2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71A17F615FE001A4D7A /* ym2413.c */; }; - 8370B7BE17F615FE001A4D7A /* ym2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71B17F615FE001A4D7A /* ym2413.h */; }; - 8370B7BF17F615FE001A4D7A /* Ym2608_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */; }; - 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */; }; - 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */; }; - 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */; }; - 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */; }; - 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */; }; - 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72417F615FE001A4D7A /* ymdeltat.cpp */; }; - 8370B7C817F615FE001A4D7A /* ymdeltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72517F615FE001A4D7A /* ymdeltat.h */; }; - 8370B7C917F615FE001A4D7A /* Ymf262_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */; }; - 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */; }; - 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */; }; - 8370B7CC17F615FE001A4D7A /* Ymz280b_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */; }; - 8370B7CD17F615FE001A4D7A /* ymz280b.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72A17F615FE001A4D7A /* ymz280b.c */; }; - 8370B7CE17F615FE001A4D7A /* ymz280b.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72B17F615FE001A4D7A /* ymz280b.h */; }; 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */; }; 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */; }; 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */; }; + 83BB5E411C0842A600734457 /* 2151intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DAD1C0842A600734457 /* 2151intf.c */; }; + 83BB5E421C0842A600734457 /* 2151intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DAE1C0842A600734457 /* 2151intf.h */; }; + 83BB5E431C0842A600734457 /* 2203intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DAF1C0842A600734457 /* 2203intf.c */; }; + 83BB5E441C0842A600734457 /* 2203intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DB01C0842A600734457 /* 2203intf.h */; }; + 83BB5E451C0842A600734457 /* 2413intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DB11C0842A600734457 /* 2413intf.c */; }; + 83BB5E461C0842A600734457 /* 2413intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DB21C0842A600734457 /* 2413intf.h */; }; + 83BB5E471C0842A600734457 /* 2413tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DB31C0842A600734457 /* 2413tone.h */; }; + 83BB5E481C0842A600734457 /* 2608intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DB41C0842A600734457 /* 2608intf.c */; }; + 83BB5E491C0842A600734457 /* 2608intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DB51C0842A600734457 /* 2608intf.h */; }; + 83BB5E4A1C0842A600734457 /* 2610intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DB61C0842A600734457 /* 2610intf.c */; }; + 83BB5E4B1C0842A600734457 /* 2610intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DB71C0842A600734457 /* 2610intf.h */; }; + 83BB5E4C1C0842A600734457 /* 2612intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DB81C0842A600734457 /* 2612intf.c */; }; + 83BB5E4D1C0842A600734457 /* 2612intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DB91C0842A600734457 /* 2612intf.h */; }; + 83BB5E4E1C0842A600734457 /* 262intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DBA1C0842A600734457 /* 262intf.c */; }; + 83BB5E4F1C0842A600734457 /* 262intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DBB1C0842A600734457 /* 262intf.h */; }; + 83BB5E501C0842A600734457 /* 281btone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DBC1C0842A600734457 /* 281btone.h */; }; + 83BB5E511C0842A600734457 /* 3526intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DBD1C0842A600734457 /* 3526intf.c */; }; + 83BB5E521C0842A600734457 /* 3526intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DBE1C0842A600734457 /* 3526intf.h */; }; + 83BB5E531C0842A600734457 /* 3812intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DBF1C0842A600734457 /* 3812intf.c */; }; + 83BB5E541C0842A600734457 /* 3812intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DC01C0842A600734457 /* 3812intf.h */; }; + 83BB5E551C0842A600734457 /* 8950intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DC11C0842A600734457 /* 8950intf.c */; }; + 83BB5E561C0842A600734457 /* 8950intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DC21C0842A600734457 /* 8950intf.h */; }; + 83BB5E571C0842A600734457 /* adlibemu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DC31C0842A600734457 /* adlibemu.h */; }; + 83BB5E581C0842A600734457 /* adlibemu_opl2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DC41C0842A600734457 /* adlibemu_opl2.c */; }; + 83BB5E591C0842A600734457 /* adlibemu_opl3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DC51C0842A600734457 /* adlibemu_opl3.c */; }; + 83BB5E5A1C0842A600734457 /* ay8910.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DC61C0842A600734457 /* ay8910.c */; }; + 83BB5E5B1C0842A600734457 /* ay8910.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DC71C0842A600734457 /* ay8910.h */; }; + 83BB5E5C1C0842A600734457 /* ay_intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DC81C0842A600734457 /* ay_intf.c */; }; + 83BB5E5D1C0842A600734457 /* ay_intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DC91C0842A600734457 /* ay_intf.h */; }; + 83BB5E5E1C0842A600734457 /* c140.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DCA1C0842A600734457 /* c140.c */; }; + 83BB5E5F1C0842A600734457 /* c140.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DCB1C0842A600734457 /* c140.h */; }; + 83BB5E601C0842A600734457 /* c352.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DCC1C0842A600734457 /* c352.c */; }; + 83BB5E611C0842A600734457 /* c352.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DCD1C0842A600734457 /* c352.h */; }; + 83BB5E621C0842A600734457 /* c6280.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DCE1C0842A600734457 /* c6280.c */; }; + 83BB5E631C0842A600734457 /* c6280.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DCF1C0842A600734457 /* c6280.h */; }; + 83BB5E641C0842A600734457 /* c6280intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DD01C0842A600734457 /* c6280intf.c */; }; + 83BB5E651C0842A600734457 /* c6280intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DD11C0842A600734457 /* c6280intf.h */; }; + 83BB5E661C0842A600734457 /* ChipIncl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DD21C0842A600734457 /* ChipIncl.h */; }; + 83BB5E671C0842A600734457 /* dac_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DD31C0842A600734457 /* dac_control.c */; }; + 83BB5E681C0842A600734457 /* dac_control.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DD41C0842A600734457 /* dac_control.h */; }; + 83BB5E691C0842A600734457 /* emu2149.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DD51C0842A600734457 /* emu2149.c */; }; + 83BB5E6A1C0842A600734457 /* emu2149.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DD61C0842A600734457 /* emu2149.h */; }; + 83BB5E6B1C0842A600734457 /* emu2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DD71C0842A600734457 /* emu2413.c */; }; + 83BB5E6C1C0842A600734457 /* emu2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DD81C0842A600734457 /* emu2413.h */; }; + 83BB5E6D1C0842A600734457 /* emu2413_NESpatches.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83BB5DD91C0842A600734457 /* emu2413_NESpatches.txt */; }; + 83BB5E6E1C0842A600734457 /* emutypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DDA1C0842A600734457 /* emutypes.h */; }; + 83BB5E6F1C0842A600734457 /* es5503.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DDB1C0842A600734457 /* es5503.c */; }; + 83BB5E701C0842A600734457 /* es5503.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DDC1C0842A600734457 /* es5503.h */; }; + 83BB5E711C0842A600734457 /* es5506.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DDD1C0842A600734457 /* es5506.c */; }; + 83BB5E721C0842A600734457 /* es5506.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DDE1C0842A600734457 /* es5506.h */; }; + 83BB5E731C0842A600734457 /* fm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DDF1C0842A600734457 /* fm.c */; }; + 83BB5E741C0842A600734457 /* fm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DE01C0842A600734457 /* fm.h */; }; + 83BB5E751C0842A600734457 /* fm2612.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DE11C0842A600734457 /* fm2612.c */; }; + 83BB5E761C0842A600734457 /* fmopl.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DE21C0842A600734457 /* fmopl.c */; }; + 83BB5E771C0842A600734457 /* fmopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DE31C0842A600734457 /* fmopl.h */; }; + 83BB5E781C0842A600734457 /* gb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DE41C0842A600734457 /* gb.c */; }; + 83BB5E791C0842A600734457 /* gb.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DE51C0842A600734457 /* gb.h */; }; + 83BB5E7A1C0842A600734457 /* iremga20.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DE61C0842A600734457 /* iremga20.c */; }; + 83BB5E7B1C0842A600734457 /* iremga20.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DE71C0842A600734457 /* iremga20.h */; }; + 83BB5E7C1C0842A600734457 /* k051649.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DE81C0842A600734457 /* k051649.c */; }; + 83BB5E7D1C0842A600734457 /* k051649.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DE91C0842A600734457 /* k051649.h */; }; + 83BB5E7E1C0842A600734457 /* k053260.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DEA1C0842A600734457 /* k053260.c */; }; + 83BB5E7F1C0842A600734457 /* k053260.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DEB1C0842A600734457 /* k053260.h */; }; + 83BB5E801C0842A600734457 /* k054539.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DEC1C0842A600734457 /* k054539.c */; }; + 83BB5E811C0842A600734457 /* k054539.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DED1C0842A600734457 /* k054539.h */; }; + 83BB5E821C0842A600734457 /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DEE1C0842A600734457 /* mamedef.h */; }; + 83BB5E831C0842A600734457 /* multipcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DEF1C0842A600734457 /* multipcm.c */; }; + 83BB5E841C0842A600734457 /* multipcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DF01C0842A600734457 /* multipcm.h */; }; + 83BB5E851C0842A600734457 /* nes_apu.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DF11C0842A600734457 /* nes_apu.c */; }; + 83BB5E861C0842A600734457 /* nes_apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DF21C0842A600734457 /* nes_apu.h */; }; + 83BB5E871C0842A600734457 /* nes_defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DF31C0842A600734457 /* nes_defs.h */; }; + 83BB5E881C0842A600734457 /* nes_intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DF41C0842A600734457 /* nes_intf.c */; }; + 83BB5E891C0842A600734457 /* nes_intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DF51C0842A600734457 /* nes_intf.h */; }; + 83BB5E8A1C0842A600734457 /* np_nes_apu.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DF61C0842A600734457 /* np_nes_apu.c */; }; + 83BB5E8B1C0842A600734457 /* np_nes_apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DF71C0842A600734457 /* np_nes_apu.h */; }; + 83BB5E8C1C0842A600734457 /* np_nes_dmc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DF81C0842A600734457 /* np_nes_dmc.c */; }; + 83BB5E8D1C0842A600734457 /* np_nes_dmc.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DF91C0842A600734457 /* np_nes_dmc.h */; }; + 83BB5E8E1C0842A600734457 /* np_nes_fds.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DFA1C0842A600734457 /* np_nes_fds.c */; }; + 83BB5E8F1C0842A600734457 /* np_nes_fds.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DFB1C0842A600734457 /* np_nes_fds.h */; }; + 83BB5E901C0842A600734457 /* okim6258.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DFC1C0842A600734457 /* okim6258.c */; }; + 83BB5E911C0842A600734457 /* okim6258.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DFD1C0842A600734457 /* okim6258.h */; }; + 83BB5E921C0842A600734457 /* okim6295.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5DFE1C0842A600734457 /* okim6295.c */; }; + 83BB5E931C0842A600734457 /* okim6295.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5DFF1C0842A600734457 /* okim6295.h */; }; + 83BB5E941C0842A600734457 /* Ootake_PSG.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E001C0842A600734457 /* Ootake_PSG.c */; }; + 83BB5E951C0842A600734457 /* Ootake_PSG.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E011C0842A600734457 /* Ootake_PSG.h */; }; + 83BB5E981C0842A600734457 /* panning.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E041C0842A600734457 /* panning.c */; }; + 83BB5E991C0842A600734457 /* panning.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E051C0842A600734457 /* panning.h */; }; + 83BB5E9A1C0842A600734457 /* pokey.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E061C0842A600734457 /* pokey.c */; }; + 83BB5E9B1C0842A600734457 /* pokey.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E071C0842A600734457 /* pokey.h */; }; + 83BB5E9C1C0842A600734457 /* pwm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E081C0842A600734457 /* pwm.c */; }; + 83BB5E9D1C0842A600734457 /* pwm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E091C0842A600734457 /* pwm.h */; }; + 83BB5E9E1C0842A600734457 /* qsound.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E0A1C0842A600734457 /* qsound.c */; }; + 83BB5E9F1C0842A600734457 /* qsound.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E0B1C0842A600734457 /* qsound.h */; }; + 83BB5EA01C0842A600734457 /* rf5c68.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E0C1C0842A600734457 /* rf5c68.c */; }; + 83BB5EA11C0842A600734457 /* rf5c68.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E0D1C0842A600734457 /* rf5c68.h */; }; + 83BB5EA21C0842A600734457 /* saa1099.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E0E1C0842A600734457 /* saa1099.c */; }; + 83BB5EA31C0842A600734457 /* saa1099.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E0F1C0842A600734457 /* saa1099.h */; }; + 83BB5EA41C0842A600734457 /* scd_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E101C0842A600734457 /* scd_pcm.c */; }; + 83BB5EA51C0842A600734457 /* scd_pcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E111C0842A600734457 /* scd_pcm.h */; }; + 83BB5EA61C0842A600734457 /* scsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E121C0842A600734457 /* scsp.c */; }; + 83BB5EA71C0842A600734457 /* scsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E131C0842A600734457 /* scsp.h */; }; + 83BB5EA81C0842A600734457 /* scspdsp.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E141C0842A600734457 /* scspdsp.c */; }; + 83BB5EA91C0842A600734457 /* scspdsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E151C0842A600734457 /* scspdsp.h */; }; + 83BB5EAB1C0842A600734457 /* segapcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E171C0842A600734457 /* segapcm.c */; }; + 83BB5EAC1C0842A600734457 /* segapcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E181C0842A600734457 /* segapcm.h */; }; + 83BB5EAD1C0842A600734457 /* sn76489.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E191C0842A600734457 /* sn76489.c */; }; + 83BB5EAE1C0842A600734457 /* sn76489.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E1A1C0842A600734457 /* sn76489.h */; }; + 83BB5EAF1C0842A600734457 /* sn76496.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E1B1C0842A600734457 /* sn76496.c */; }; + 83BB5EB01C0842A600734457 /* sn76496.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E1C1C0842A600734457 /* sn76496.h */; }; + 83BB5EB11C0842A600734457 /* sn764intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E1D1C0842A600734457 /* sn764intf.c */; }; + 83BB5EB21C0842A600734457 /* sn764intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E1E1C0842A600734457 /* sn764intf.h */; }; + 83BB5EB31C0842A600734457 /* upd7759.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E1F1C0842A600734457 /* upd7759.c */; }; + 83BB5EB41C0842A600734457 /* upd7759.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E201C0842A600734457 /* upd7759.h */; }; + 83BB5EB51C0842A600734457 /* vrc7tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E211C0842A600734457 /* vrc7tone.h */; }; + 83BB5EB61C0842A600734457 /* vsu.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E221C0842A600734457 /* vsu.c */; }; + 83BB5EB71C0842A600734457 /* vsu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E231C0842A600734457 /* vsu.h */; }; + 83BB5EB81C0842A600734457 /* ws_audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E241C0842A600734457 /* ws_audio.c */; }; + 83BB5EB91C0842A600734457 /* ws_audio.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E251C0842A600734457 /* ws_audio.h */; }; + 83BB5EBA1C0842A600734457 /* ws_initialIo.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E261C0842A600734457 /* ws_initialIo.h */; }; + 83BB5EBB1C0842A600734457 /* x1_010.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E271C0842A600734457 /* x1_010.c */; }; + 83BB5EBC1C0842A600734457 /* x1_010.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E281C0842A600734457 /* x1_010.h */; }; + 83BB5EBD1C0842A600734457 /* ym2151.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E291C0842A600734457 /* ym2151.c */; }; + 83BB5EBE1C0842A600734457 /* ym2151.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E2A1C0842A600734457 /* ym2151.h */; }; + 83BB5EBF1C0842A600734457 /* ym2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E2B1C0842A600734457 /* ym2413.c */; }; + 83BB5EC01C0842A600734457 /* ym2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E2C1C0842A600734457 /* ym2413.h */; }; + 83BB5EC11C0842A600734457 /* ym2612.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E2D1C0842A600734457 /* ym2612.c */; }; + 83BB5EC21C0842A600734457 /* ym2612.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E2E1C0842A600734457 /* ym2612.h */; }; + 83BB5EC31C0842A600734457 /* ymdeltat.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E2F1C0842A600734457 /* ymdeltat.c */; }; + 83BB5EC41C0842A600734457 /* ymdeltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E301C0842A600734457 /* ymdeltat.h */; }; + 83BB5EC51C0842A600734457 /* ymf262.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E311C0842A600734457 /* ymf262.c */; }; + 83BB5EC61C0842A600734457 /* ymf262.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E321C0842A600734457 /* ymf262.h */; }; + 83BB5EC71C0842A600734457 /* ymf271.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E331C0842A600734457 /* ymf271.c */; }; + 83BB5EC81C0842A600734457 /* ymf271.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E341C0842A600734457 /* ymf271.h */; }; + 83BB5EC91C0842A600734457 /* ymf278b.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E351C0842A600734457 /* ymf278b.c */; }; + 83BB5ECA1C0842A600734457 /* ymf278b.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E361C0842A600734457 /* ymf278b.h */; }; + 83BB5ECB1C0842A600734457 /* ymz280b.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E371C0842A600734457 /* ymz280b.c */; }; + 83BB5ECC1C0842A600734457 /* ymz280b.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E381C0842A600734457 /* ymz280b.h */; }; + 83BB5ECD1C0842A600734457 /* VGMPlay.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E391C0842A600734457 /* VGMPlay.c */; }; + 83BB5ECE1C0842A600734457 /* ChipMapper.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E3A1C0842A600734457 /* ChipMapper.c */; }; + 83BB5ECF1C0842A600734457 /* ChipMapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E3B1C0842A600734457 /* ChipMapper.h */; }; + 83BB5ED01C0842A600734457 /* VGMFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E3C1C0842A600734457 /* VGMFile.h */; }; + 83BB5ED11C0842A600734457 /* VGMPlay_Intf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E3D1C0842A600734457 /* VGMPlay_Intf.h */; }; + 83BB5ED21C0842A600734457 /* VGMPlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E3E1C0842A600734457 /* VGMPlay.h */; }; + 83BB5ED31C0842A600734457 /* resampler.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BB5E3F1C0842A600734457 /* resampler.c */; }; + 83BB5ED41C0842A600734457 /* resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BB5E401C0842A600734457 /* resampler.h */; }; 83FC5D5E181B47FB00B917E5 /* dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D3B181B47FB00B917E5 /* dsp.cpp */; }; 83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D3C181B47FB00B917E5 /* dsp.hpp */; }; 83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D57181B47FB00B917E5 /* smp.cpp */; }; @@ -350,7 +401,6 @@ 17C8F1ED0CBED286008D969D /* Ym2413_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ym2413_Emu.h; path = gme/Ym2413_Emu.h; sourceTree = ""; }; 17C8F1EE0CBED286008D969D /* Ym2612_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu.cpp; path = gme/Ym2612_Emu.cpp; sourceTree = ""; }; 17C8F1EF0CBED286008D969D /* Ym2612_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ym2612_Emu.h; path = gme/Ym2612_Emu.h; sourceTree = ""; }; - 8370B68C17F615FD001A4D7A /* adlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adlib.h; path = gme/adlib.h; sourceTree = ""; }; 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Core.cpp; path = gme/Ay_Core.cpp; sourceTree = ""; }; 8370B68E17F615FD001A4D7A /* Ay_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ay_Core.h; path = gme/Ay_Core.h; sourceTree = ""; }; 8370B68F17F615FD001A4D7A /* blargg_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = blargg_common.cpp; path = gme/blargg_common.cpp; sourceTree = ""; }; @@ -360,24 +410,8 @@ 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Blip_Buffer_impl2.h; path = gme/Blip_Buffer_impl2.h; sourceTree = ""; }; 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Bml_Parser.cpp; path = gme/Bml_Parser.cpp; sourceTree = ""; }; 8370B69517F615FD001A4D7A /* Bml_Parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bml_Parser.h; path = gme/Bml_Parser.h; sourceTree = ""; }; - 8370B69617F615FD001A4D7A /* C140_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C140_Emu.cpp; path = gme/C140_Emu.cpp; sourceTree = ""; }; - 8370B69717F615FD001A4D7A /* C140_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = C140_Emu.h; path = gme/C140_Emu.h; sourceTree = ""; }; - 8370B69817F615FD001A4D7A /* c140.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = c140.c; path = gme/c140.c; sourceTree = ""; }; - 8370B69917F615FD001A4D7A /* c140.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = c140.h; path = gme/c140.h; sourceTree = ""; }; - 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Chip_Resampler.h; path = gme/Chip_Resampler.h; sourceTree = ""; }; - 8370B69B17F615FD001A4D7A /* dac_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dac_control.c; path = gme/dac_control.c; sourceTree = ""; }; - 8370B69C17F615FD001A4D7A /* dac_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dac_control.h; path = gme/dac_control.h; sourceTree = ""; }; - 8370B69D17F615FD001A4D7A /* dbopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dbopl.cpp; path = gme/dbopl.cpp; sourceTree = ""; }; - 8370B69E17F615FD001A4D7A /* dbopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dbopl.h; path = gme/dbopl.h; sourceTree = ""; }; - 8370B69F17F615FD001A4D7A /* divfix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = divfix.h; path = gme/divfix.h; sourceTree = ""; }; 8370B6A017F615FD001A4D7A /* Downsampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Downsampler.cpp; path = gme/Downsampler.cpp; sourceTree = ""; }; 8370B6A117F615FD001A4D7A /* Downsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Downsampler.h; path = gme/Downsampler.h; sourceTree = ""; }; - 8370B6A217F615FD001A4D7A /* emuconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = emuconfig.h; path = gme/emuconfig.h; sourceTree = ""; }; - 8370B6A317F615FD001A4D7A /* fm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fm.c; path = gme/fm.c; sourceTree = ""; }; - 8370B6A417F615FD001A4D7A /* fm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fm.h; path = gme/fm.h; sourceTree = ""; }; - 8370B6A517F615FD001A4D7A /* fm2612.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fm2612.c; path = gme/fm2612.c; sourceTree = ""; }; - 8370B6A617F615FD001A4D7A /* fmopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fmopl.cpp; path = gme/fmopl.cpp; sourceTree = ""; }; - 8370B6A717F615FD001A4D7A /* fmopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fmopl.h; path = gme/fmopl.h; sourceTree = ""; }; 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gb_Cpu_run.h; path = gme/Gb_Cpu_run.h; sourceTree = ""; }; 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gbs_Core.cpp; path = gme/Gbs_Core.cpp; sourceTree = ""; }; 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gbs_Core.h; path = gme/Gbs_Core.h; sourceTree = ""; }; @@ -389,82 +423,27 @@ 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Core.cpp; path = gme/Hes_Core.cpp; sourceTree = ""; }; 8370B6B317F615FD001A4D7A /* Hes_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Core.h; path = gme/Hes_Core.h; sourceTree = ""; }; 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Cpu_run.h; path = gme/Hes_Cpu_run.h; sourceTree = ""; }; - 8370B6B517F615FD001A4D7A /* i_fmpac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_fmpac.h; path = gme/i_fmpac.h; sourceTree = ""; }; - 8370B6B617F615FD001A4D7A /* i_fmunit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_fmunit.h; path = gme/i_fmunit.h; sourceTree = ""; }; - 8370B6B717F615FD001A4D7A /* i_vrc7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_vrc7.h; path = gme/i_vrc7.h; sourceTree = ""; }; - 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K051649_Emu.cpp; path = gme/K051649_Emu.cpp; sourceTree = ""; }; - 8370B6B917F615FD001A4D7A /* K051649_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K051649_Emu.h; path = gme/K051649_Emu.h; sourceTree = ""; }; - 8370B6BA17F615FD001A4D7A /* k051649.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k051649.c; path = gme/k051649.c; sourceTree = ""; }; - 8370B6BB17F615FD001A4D7A /* k051649.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k051649.h; path = gme/k051649.h; sourceTree = ""; }; - 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K053260_Emu.cpp; path = gme/K053260_Emu.cpp; sourceTree = ""; }; - 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K053260_Emu.h; path = gme/K053260_Emu.h; sourceTree = ""; }; - 8370B6BE17F615FD001A4D7A /* k053260.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k053260.c; path = gme/k053260.c; sourceTree = ""; }; - 8370B6BF17F615FD001A4D7A /* k053260.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k053260.h; path = gme/k053260.h; sourceTree = ""; }; - 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K054539_Emu.cpp; path = gme/K054539_Emu.cpp; sourceTree = ""; }; - 8370B6C117F615FD001A4D7A /* K054539_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K054539_Emu.h; path = gme/K054539_Emu.h; sourceTree = ""; }; - 8370B6C217F615FD001A4D7A /* k054539.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k054539.c; path = gme/k054539.c; sourceTree = ""; }; - 8370B6C317F615FD001A4D7A /* k054539.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k054539.h; path = gme/k054539.h; sourceTree = ""; }; - 8370B6C417F615FD001A4D7A /* kmsnddev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kmsnddev.h; path = gme/kmsnddev.h; sourceTree = ""; }; 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Core.cpp; path = gme/Kss_Core.cpp; sourceTree = ""; }; 8370B6C617F615FD001A4D7A /* Kss_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Kss_Core.h; path = gme/Kss_Core.h; sourceTree = ""; }; - 8370B6C717F615FD001A4D7A /* mamedef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mamedef.h; path = gme/mamedef.h; sourceTree = ""; }; - 8370B6C817F615FD001A4D7A /* mathdefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mathdefs.h; path = gme/mathdefs.h; sourceTree = ""; }; 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Cpu_run.h; path = gme/Nes_Cpu_run.h; sourceTree = ""; }; 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Fds_Apu.cpp; path = gme/Nes_Fds_Apu.cpp; sourceTree = ""; }; 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Fds_Apu.h; path = gme/Nes_Fds_Apu.h; sourceTree = ""; }; 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Mmc5_Apu.h; path = gme/Nes_Mmc5_Apu.h; sourceTree = ""; }; 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Vrc7_Apu.cpp; path = gme/Nes_Vrc7_Apu.cpp; sourceTree = ""; }; 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Vrc7_Apu.h; path = gme/Nes_Vrc7_Apu.h; sourceTree = ""; }; - 8370B6CF17F615FD001A4D7A /* nestypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nestypes.h; path = gme/nestypes.h; sourceTree = ""; }; 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Core.cpp; path = gme/Nsf_Core.cpp; sourceTree = ""; }; 8370B6D117F615FD001A4D7A /* Nsf_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nsf_Core.h; path = gme/Nsf_Core.h; sourceTree = ""; }; 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Cpu.cpp; path = gme/Nsf_Cpu.cpp; sourceTree = ""; }; 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Impl.cpp; path = gme/Nsf_Impl.cpp; sourceTree = ""; }; 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nsf_Impl.h; path = gme/Nsf_Impl.h; sourceTree = ""; }; - 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Okim6258_Emu.cpp; path = gme/Okim6258_Emu.cpp; sourceTree = ""; }; - 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Okim6258_Emu.h; path = gme/Okim6258_Emu.h; sourceTree = ""; }; - 8370B6D717F615FD001A4D7A /* okim6258.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = okim6258.c; path = gme/okim6258.c; sourceTree = ""; }; - 8370B6D817F615FD001A4D7A /* okim6258.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = okim6258.h; path = gme/okim6258.h; sourceTree = ""; }; - 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Okim6295_Emu.cpp; path = gme/Okim6295_Emu.cpp; sourceTree = ""; }; - 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Okim6295_Emu.h; path = gme/Okim6295_Emu.h; sourceTree = ""; }; - 8370B6DB17F615FD001A4D7A /* okim6295.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = okim6295.c; path = gme/okim6295.c; sourceTree = ""; }; - 8370B6DC17F615FD001A4D7A /* okim6295.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = okim6295.h; path = gme/okim6295.h; sourceTree = ""; }; 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Opl_Apu.cpp; path = gme/Opl_Apu.cpp; sourceTree = ""; }; 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Opl_Apu.h; path = gme/Opl_Apu.h; sourceTree = ""; }; - 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Pwm_Emu.cpp; path = gme/Pwm_Emu.cpp; sourceTree = ""; }; - 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Pwm_Emu.h; path = gme/Pwm_Emu.h; sourceTree = ""; }; - 8370B6E117F615FE001A4D7A /* pwm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pwm.c; path = gme/pwm.c; sourceTree = ""; }; - 8370B6E217F615FE001A4D7A /* pwm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pwm.h; path = gme/pwm.h; sourceTree = ""; }; - 8370B6E317F615FE001A4D7A /* qmix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qmix.c; path = gme/qmix.c; sourceTree = ""; }; - 8370B6E417F615FE001A4D7A /* qmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qmix.h; path = gme/qmix.h; sourceTree = ""; }; - 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Qsound_Apu.cpp; path = gme/Qsound_Apu.cpp; sourceTree = ""; }; - 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Qsound_Apu.h; path = gme/Qsound_Apu.h; sourceTree = ""; }; 8370B6E717F615FE001A4D7A /* Resampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resampler.cpp; path = gme/Resampler.cpp; sourceTree = ""; }; 8370B6E817F615FE001A4D7A /* Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Resampler.h; path = gme/Resampler.h; sourceTree = ""; }; - 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rf5C68_Emu.cpp; path = gme/Rf5C68_Emu.cpp; sourceTree = ""; }; - 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rf5C68_Emu.h; path = gme/Rf5C68_Emu.h; sourceTree = ""; }; - 8370B6EB17F615FE001A4D7A /* rf5c68.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rf5c68.c; path = gme/rf5c68.c; sourceTree = ""; }; - 8370B6EC17F615FE001A4D7A /* rf5c68.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rf5c68.h; path = gme/rf5c68.h; sourceTree = ""; }; - 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rf5C164_Emu.cpp; path = gme/Rf5C164_Emu.cpp; sourceTree = ""; }; - 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rf5C164_Emu.h; path = gme/Rf5C164_Emu.h; sourceTree = ""; }; 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rom_Data.cpp; path = gme/Rom_Data.cpp; sourceTree = ""; }; 8370B6F017F615FE001A4D7A /* Rom_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rom_Data.h; path = gme/Rom_Data.h; sourceTree = ""; }; - 8370B6F117F615FE001A4D7A /* s_deltat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_deltat.c; path = gme/s_deltat.c; sourceTree = ""; }; - 8370B6F217F615FE001A4D7A /* s_deltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_deltat.h; path = gme/s_deltat.h; sourceTree = ""; }; - 8370B6F317F615FE001A4D7A /* s_logtbl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_logtbl.c; path = gme/s_logtbl.c; sourceTree = ""; }; - 8370B6F417F615FE001A4D7A /* s_logtbl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_logtbl.h; path = gme/s_logtbl.h; sourceTree = ""; }; - 8370B6F517F615FE001A4D7A /* s_opl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_opl.c; path = gme/s_opl.c; sourceTree = ""; }; - 8370B6F617F615FE001A4D7A /* s_opl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_opl.h; path = gme/s_opl.h; sourceTree = ""; }; - 8370B6F717F615FE001A4D7A /* s_opltbl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_opltbl.c; path = gme/s_opltbl.c; sourceTree = ""; }; - 8370B6F817F615FE001A4D7A /* s_opltbl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_opltbl.h; path = gme/s_opltbl.h; sourceTree = ""; }; 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Core.cpp; path = gme/Sap_Core.cpp; sourceTree = ""; }; 8370B6FA17F615FE001A4D7A /* Sap_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sap_Core.h; path = gme/Sap_Core.h; sourceTree = ""; }; - 8370B6FB17F615FE001A4D7A /* scd_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scd_pcm.c; path = gme/scd_pcm.c; sourceTree = ""; }; - 8370B6FC17F615FE001A4D7A /* scd_pcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scd_pcm.h; path = gme/scd_pcm.h; sourceTree = ""; }; - 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SegaPcm_Emu.cpp; path = gme/SegaPcm_Emu.cpp; sourceTree = ""; }; - 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SegaPcm_Emu.h; path = gme/SegaPcm_Emu.h; sourceTree = ""; }; - 8370B6FF17F615FE001A4D7A /* segapcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = segapcm.c; path = gme/segapcm.c; sourceTree = ""; }; - 8370B70017F615FE001A4D7A /* segapcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = segapcm.h; path = gme/segapcm.h; sourceTree = ""; }; 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Core.cpp; path = gme/Sgc_Core.cpp; sourceTree = ""; }; 8370B70217F615FE001A4D7A /* Sgc_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Core.h; path = gme/Sgc_Core.h; sourceTree = ""; }; 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Cpu.cpp; path = gme/Sgc_Cpu.cpp; sourceTree = ""; }; @@ -484,33 +463,154 @@ 8370B71117F615FE001A4D7A /* Upsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Upsampler.h; path = gme/Upsampler.h; sourceTree = ""; }; 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Core.cpp; path = gme/Vgm_Core.cpp; sourceTree = ""; }; 8370B71317F615FE001A4D7A /* Vgm_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Vgm_Core.h; path = gme/Vgm_Core.h; sourceTree = ""; }; - 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2151_Emu.cpp; path = gme/Ym2151_Emu.cpp; sourceTree = ""; }; - 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2151_Emu.h; path = gme/Ym2151_Emu.h; sourceTree = ""; }; - 8370B71617F615FE001A4D7A /* ym2151.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ym2151.c; path = gme/ym2151.c; sourceTree = ""; }; - 8370B71717F615FE001A4D7A /* ym2151.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ym2151.h; path = gme/ym2151.h; sourceTree = ""; }; - 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2203_Emu.cpp; path = gme/Ym2203_Emu.cpp; sourceTree = ""; }; - 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2203_Emu.h; path = gme/Ym2203_Emu.h; sourceTree = ""; }; - 8370B71A17F615FE001A4D7A /* ym2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ym2413.c; path = gme/ym2413.c; sourceTree = ""; }; - 8370B71B17F615FE001A4D7A /* ym2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ym2413.h; path = gme/ym2413.h; sourceTree = ""; }; - 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2608_Emu.cpp; path = gme/Ym2608_Emu.cpp; sourceTree = ""; }; - 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2608_Emu.h; path = gme/Ym2608_Emu.h; sourceTree = ""; }; - 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2610b_Emu.cpp; path = gme/Ym2610b_Emu.cpp; sourceTree = ""; }; - 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2610b_Emu.h; path = gme/Ym2610b_Emu.h; sourceTree = ""; }; - 8370B72017F615FE001A4D7A /* Ym2612_Emu_Gens.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu_Gens.cpp; path = gme/Ym2612_Emu_Gens.cpp; sourceTree = ""; }; - 8370B72117F615FE001A4D7A /* Ym2612_Emu_MAME.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu_MAME.cpp; path = gme/Ym2612_Emu_MAME.cpp; sourceTree = ""; }; - 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym3812_Emu.cpp; path = gme/Ym3812_Emu.cpp; sourceTree = ""; }; - 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym3812_Emu.h; path = gme/Ym3812_Emu.h; sourceTree = ""; }; - 8370B72417F615FE001A4D7A /* ymdeltat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ymdeltat.cpp; path = gme/ymdeltat.cpp; sourceTree = ""; }; - 8370B72517F615FE001A4D7A /* ymdeltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ymdeltat.h; path = gme/ymdeltat.h; sourceTree = ""; }; - 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ymf262_Emu.cpp; path = gme/Ymf262_Emu.cpp; sourceTree = ""; }; - 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ymf262_Emu.h; path = gme/Ymf262_Emu.h; sourceTree = ""; }; - 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ymz280b_Emu.cpp; path = gme/Ymz280b_Emu.cpp; sourceTree = ""; }; - 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ymz280b_Emu.h; path = gme/Ymz280b_Emu.h; sourceTree = ""; }; - 8370B72A17F615FE001A4D7A /* ymz280b.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ymz280b.c; path = gme/ymz280b.c; sourceTree = ""; }; - 8370B72B17F615FE001A4D7A /* ymz280b.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ymz280b.h; path = gme/ymz280b.h; sourceTree = ""; }; 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu_run.h; path = gme/Z80_Cpu_run.h; sourceTree = ""; }; 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80_Cpu.cpp; path = gme/Z80_Cpu.cpp; sourceTree = ""; }; 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu.h; path = gme/Z80_Cpu.h; sourceTree = ""; }; + 83BB5DAD1C0842A600734457 /* 2151intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2151intf.c; sourceTree = ""; }; + 83BB5DAE1C0842A600734457 /* 2151intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2151intf.h; sourceTree = ""; }; + 83BB5DAF1C0842A600734457 /* 2203intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2203intf.c; sourceTree = ""; }; + 83BB5DB01C0842A600734457 /* 2203intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2203intf.h; sourceTree = ""; }; + 83BB5DB11C0842A600734457 /* 2413intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2413intf.c; sourceTree = ""; }; + 83BB5DB21C0842A600734457 /* 2413intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2413intf.h; sourceTree = ""; }; + 83BB5DB31C0842A600734457 /* 2413tone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2413tone.h; sourceTree = ""; }; + 83BB5DB41C0842A600734457 /* 2608intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2608intf.c; sourceTree = ""; }; + 83BB5DB51C0842A600734457 /* 2608intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2608intf.h; sourceTree = ""; }; + 83BB5DB61C0842A600734457 /* 2610intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2610intf.c; sourceTree = ""; }; + 83BB5DB71C0842A600734457 /* 2610intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2610intf.h; sourceTree = ""; }; + 83BB5DB81C0842A600734457 /* 2612intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 2612intf.c; sourceTree = ""; }; + 83BB5DB91C0842A600734457 /* 2612intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2612intf.h; sourceTree = ""; }; + 83BB5DBA1C0842A600734457 /* 262intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 262intf.c; sourceTree = ""; }; + 83BB5DBB1C0842A600734457 /* 262intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 262intf.h; sourceTree = ""; }; + 83BB5DBC1C0842A600734457 /* 281btone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 281btone.h; sourceTree = ""; }; + 83BB5DBD1C0842A600734457 /* 3526intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 3526intf.c; sourceTree = ""; }; + 83BB5DBE1C0842A600734457 /* 3526intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 3526intf.h; sourceTree = ""; }; + 83BB5DBF1C0842A600734457 /* 3812intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 3812intf.c; sourceTree = ""; }; + 83BB5DC01C0842A600734457 /* 3812intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 3812intf.h; sourceTree = ""; }; + 83BB5DC11C0842A600734457 /* 8950intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 8950intf.c; sourceTree = ""; }; + 83BB5DC21C0842A600734457 /* 8950intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 8950intf.h; sourceTree = ""; }; + 83BB5DC31C0842A600734457 /* adlibemu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adlibemu.h; sourceTree = ""; }; + 83BB5DC41C0842A600734457 /* adlibemu_opl2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adlibemu_opl2.c; sourceTree = ""; }; + 83BB5DC51C0842A600734457 /* adlibemu_opl3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adlibemu_opl3.c; sourceTree = ""; }; + 83BB5DC61C0842A600734457 /* ay8910.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ay8910.c; sourceTree = ""; }; + 83BB5DC71C0842A600734457 /* ay8910.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ay8910.h; sourceTree = ""; }; + 83BB5DC81C0842A600734457 /* ay_intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ay_intf.c; sourceTree = ""; }; + 83BB5DC91C0842A600734457 /* ay_intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ay_intf.h; sourceTree = ""; }; + 83BB5DCA1C0842A600734457 /* c140.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = c140.c; sourceTree = ""; }; + 83BB5DCB1C0842A600734457 /* c140.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c140.h; sourceTree = ""; }; + 83BB5DCC1C0842A600734457 /* c352.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = c352.c; sourceTree = ""; }; + 83BB5DCD1C0842A600734457 /* c352.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c352.h; sourceTree = ""; }; + 83BB5DCE1C0842A600734457 /* c6280.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = c6280.c; sourceTree = ""; }; + 83BB5DCF1C0842A600734457 /* c6280.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c6280.h; sourceTree = ""; }; + 83BB5DD01C0842A600734457 /* c6280intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = c6280intf.c; sourceTree = ""; }; + 83BB5DD11C0842A600734457 /* c6280intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c6280intf.h; sourceTree = ""; }; + 83BB5DD21C0842A600734457 /* ChipIncl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChipIncl.h; sourceTree = ""; }; + 83BB5DD31C0842A600734457 /* dac_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dac_control.c; sourceTree = ""; }; + 83BB5DD41C0842A600734457 /* dac_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dac_control.h; sourceTree = ""; }; + 83BB5DD51C0842A600734457 /* emu2149.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emu2149.c; sourceTree = ""; }; + 83BB5DD61C0842A600734457 /* emu2149.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emu2149.h; sourceTree = ""; }; + 83BB5DD71C0842A600734457 /* emu2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emu2413.c; sourceTree = ""; }; + 83BB5DD81C0842A600734457 /* emu2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emu2413.h; sourceTree = ""; }; + 83BB5DD91C0842A600734457 /* emu2413_NESpatches.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = emu2413_NESpatches.txt; sourceTree = ""; }; + 83BB5DDA1C0842A600734457 /* emutypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emutypes.h; sourceTree = ""; }; + 83BB5DDB1C0842A600734457 /* es5503.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = es5503.c; sourceTree = ""; }; + 83BB5DDC1C0842A600734457 /* es5503.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = es5503.h; sourceTree = ""; }; + 83BB5DDD1C0842A600734457 /* es5506.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = es5506.c; sourceTree = ""; }; + 83BB5DDE1C0842A600734457 /* es5506.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = es5506.h; sourceTree = ""; }; + 83BB5DDF1C0842A600734457 /* fm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fm.c; sourceTree = ""; }; + 83BB5DE01C0842A600734457 /* fm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fm.h; sourceTree = ""; }; + 83BB5DE11C0842A600734457 /* fm2612.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fm2612.c; sourceTree = ""; }; + 83BB5DE21C0842A600734457 /* fmopl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmopl.c; sourceTree = ""; }; + 83BB5DE31C0842A600734457 /* fmopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmopl.h; sourceTree = ""; }; + 83BB5DE41C0842A600734457 /* gb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gb.c; sourceTree = ""; }; + 83BB5DE51C0842A600734457 /* gb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gb.h; sourceTree = ""; }; + 83BB5DE61C0842A600734457 /* iremga20.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iremga20.c; sourceTree = ""; }; + 83BB5DE71C0842A600734457 /* iremga20.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iremga20.h; sourceTree = ""; }; + 83BB5DE81C0842A600734457 /* k051649.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k051649.c; sourceTree = ""; }; + 83BB5DE91C0842A600734457 /* k051649.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = k051649.h; sourceTree = ""; }; + 83BB5DEA1C0842A600734457 /* k053260.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k053260.c; sourceTree = ""; }; + 83BB5DEB1C0842A600734457 /* k053260.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = k053260.h; sourceTree = ""; }; + 83BB5DEC1C0842A600734457 /* k054539.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = k054539.c; sourceTree = ""; }; + 83BB5DED1C0842A600734457 /* k054539.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = k054539.h; sourceTree = ""; }; + 83BB5DEE1C0842A600734457 /* mamedef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mamedef.h; sourceTree = ""; }; + 83BB5DEF1C0842A600734457 /* multipcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = multipcm.c; sourceTree = ""; }; + 83BB5DF01C0842A600734457 /* multipcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = multipcm.h; sourceTree = ""; }; + 83BB5DF11C0842A600734457 /* nes_apu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nes_apu.c; sourceTree = ""; }; + 83BB5DF21C0842A600734457 /* nes_apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nes_apu.h; sourceTree = ""; }; + 83BB5DF31C0842A600734457 /* nes_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nes_defs.h; sourceTree = ""; }; + 83BB5DF41C0842A600734457 /* nes_intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nes_intf.c; sourceTree = ""; }; + 83BB5DF51C0842A600734457 /* nes_intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nes_intf.h; sourceTree = ""; }; + 83BB5DF61C0842A600734457 /* np_nes_apu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = np_nes_apu.c; sourceTree = ""; }; + 83BB5DF71C0842A600734457 /* np_nes_apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = np_nes_apu.h; sourceTree = ""; }; + 83BB5DF81C0842A600734457 /* np_nes_dmc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = np_nes_dmc.c; sourceTree = ""; }; + 83BB5DF91C0842A600734457 /* np_nes_dmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = np_nes_dmc.h; sourceTree = ""; }; + 83BB5DFA1C0842A600734457 /* np_nes_fds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = np_nes_fds.c; sourceTree = ""; }; + 83BB5DFB1C0842A600734457 /* np_nes_fds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = np_nes_fds.h; sourceTree = ""; }; + 83BB5DFC1C0842A600734457 /* okim6258.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = okim6258.c; sourceTree = ""; }; + 83BB5DFD1C0842A600734457 /* okim6258.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = okim6258.h; sourceTree = ""; }; + 83BB5DFE1C0842A600734457 /* okim6295.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = okim6295.c; sourceTree = ""; }; + 83BB5DFF1C0842A600734457 /* okim6295.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = okim6295.h; sourceTree = ""; }; + 83BB5E001C0842A600734457 /* Ootake_PSG.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Ootake_PSG.c; sourceTree = ""; }; + 83BB5E011C0842A600734457 /* Ootake_PSG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ootake_PSG.h; sourceTree = ""; }; + 83BB5E041C0842A600734457 /* panning.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = panning.c; sourceTree = ""; }; + 83BB5E051C0842A600734457 /* panning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = panning.h; sourceTree = ""; }; + 83BB5E061C0842A600734457 /* pokey.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pokey.c; sourceTree = ""; }; + 83BB5E071C0842A600734457 /* pokey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pokey.h; sourceTree = ""; }; + 83BB5E081C0842A600734457 /* pwm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pwm.c; sourceTree = ""; }; + 83BB5E091C0842A600734457 /* pwm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pwm.h; sourceTree = ""; }; + 83BB5E0A1C0842A600734457 /* qsound.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qsound.c; sourceTree = ""; }; + 83BB5E0B1C0842A600734457 /* qsound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qsound.h; sourceTree = ""; }; + 83BB5E0C1C0842A600734457 /* rf5c68.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rf5c68.c; sourceTree = ""; }; + 83BB5E0D1C0842A600734457 /* rf5c68.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rf5c68.h; sourceTree = ""; }; + 83BB5E0E1C0842A600734457 /* saa1099.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = saa1099.c; sourceTree = ""; }; + 83BB5E0F1C0842A600734457 /* saa1099.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = saa1099.h; sourceTree = ""; }; + 83BB5E101C0842A600734457 /* scd_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scd_pcm.c; sourceTree = ""; }; + 83BB5E111C0842A600734457 /* scd_pcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scd_pcm.h; sourceTree = ""; }; + 83BB5E121C0842A600734457 /* scsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scsp.c; sourceTree = ""; }; + 83BB5E131C0842A600734457 /* scsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scsp.h; sourceTree = ""; }; + 83BB5E141C0842A600734457 /* scspdsp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scspdsp.c; sourceTree = ""; }; + 83BB5E151C0842A600734457 /* scspdsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scspdsp.h; sourceTree = ""; }; + 83BB5E171C0842A600734457 /* segapcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = segapcm.c; sourceTree = ""; }; + 83BB5E181C0842A600734457 /* segapcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = segapcm.h; sourceTree = ""; }; + 83BB5E191C0842A600734457 /* sn76489.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sn76489.c; sourceTree = ""; }; + 83BB5E1A1C0842A600734457 /* sn76489.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sn76489.h; sourceTree = ""; }; + 83BB5E1B1C0842A600734457 /* sn76496.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sn76496.c; sourceTree = ""; }; + 83BB5E1C1C0842A600734457 /* sn76496.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sn76496.h; sourceTree = ""; }; + 83BB5E1D1C0842A600734457 /* sn764intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sn764intf.c; sourceTree = ""; }; + 83BB5E1E1C0842A600734457 /* sn764intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sn764intf.h; sourceTree = ""; }; + 83BB5E1F1C0842A600734457 /* upd7759.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = upd7759.c; sourceTree = ""; }; + 83BB5E201C0842A600734457 /* upd7759.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = upd7759.h; sourceTree = ""; }; + 83BB5E211C0842A600734457 /* vrc7tone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vrc7tone.h; sourceTree = ""; }; + 83BB5E221C0842A600734457 /* vsu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vsu.c; sourceTree = ""; }; + 83BB5E231C0842A600734457 /* vsu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vsu.h; sourceTree = ""; }; + 83BB5E241C0842A600734457 /* ws_audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ws_audio.c; sourceTree = ""; }; + 83BB5E251C0842A600734457 /* ws_audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ws_audio.h; sourceTree = ""; }; + 83BB5E261C0842A600734457 /* ws_initialIo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ws_initialIo.h; sourceTree = ""; }; + 83BB5E271C0842A600734457 /* x1_010.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x1_010.c; sourceTree = ""; }; + 83BB5E281C0842A600734457 /* x1_010.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x1_010.h; sourceTree = ""; }; + 83BB5E291C0842A600734457 /* ym2151.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ym2151.c; sourceTree = ""; }; + 83BB5E2A1C0842A600734457 /* ym2151.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ym2151.h; sourceTree = ""; }; + 83BB5E2B1C0842A600734457 /* ym2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ym2413.c; sourceTree = ""; }; + 83BB5E2C1C0842A600734457 /* ym2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ym2413.h; sourceTree = ""; }; + 83BB5E2D1C0842A600734457 /* ym2612.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ym2612.c; sourceTree = ""; }; + 83BB5E2E1C0842A600734457 /* ym2612.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ym2612.h; sourceTree = ""; }; + 83BB5E2F1C0842A600734457 /* ymdeltat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ymdeltat.c; sourceTree = ""; }; + 83BB5E301C0842A600734457 /* ymdeltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ymdeltat.h; sourceTree = ""; }; + 83BB5E311C0842A600734457 /* ymf262.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ymf262.c; sourceTree = ""; }; + 83BB5E321C0842A600734457 /* ymf262.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ymf262.h; sourceTree = ""; }; + 83BB5E331C0842A600734457 /* ymf271.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ymf271.c; sourceTree = ""; }; + 83BB5E341C0842A600734457 /* ymf271.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ymf271.h; sourceTree = ""; }; + 83BB5E351C0842A600734457 /* ymf278b.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ymf278b.c; sourceTree = ""; }; + 83BB5E361C0842A600734457 /* ymf278b.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ymf278b.h; sourceTree = ""; }; + 83BB5E371C0842A600734457 /* ymz280b.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ymz280b.c; sourceTree = ""; }; + 83BB5E381C0842A600734457 /* ymz280b.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ymz280b.h; sourceTree = ""; }; + 83BB5E391C0842A600734457 /* VGMPlay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = VGMPlay.c; path = vgmplay/VGMPlay.c; sourceTree = ""; }; + 83BB5E3A1C0842A600734457 /* ChipMapper.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ChipMapper.c; path = vgmplay/ChipMapper.c; sourceTree = ""; }; + 83BB5E3B1C0842A600734457 /* ChipMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ChipMapper.h; path = vgmplay/ChipMapper.h; sourceTree = ""; }; + 83BB5E3C1C0842A600734457 /* VGMFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VGMFile.h; path = vgmplay/VGMFile.h; sourceTree = ""; }; + 83BB5E3D1C0842A600734457 /* VGMPlay_Intf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VGMPlay_Intf.h; path = vgmplay/VGMPlay_Intf.h; sourceTree = ""; }; + 83BB5E3E1C0842A600734457 /* VGMPlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VGMPlay.h; path = vgmplay/VGMPlay.h; sourceTree = ""; }; + 83BB5E3F1C0842A600734457 /* resampler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = resampler.c; path = vgmplay/resampler.c; sourceTree = ""; }; + 83BB5E401C0842A600734457 /* resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = resampler.h; path = vgmplay/resampler.h; sourceTree = ""; }; 83FC5D3B181B47FB00B917E5 /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = ""; }; 83FC5D3C181B47FB00B917E5 /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = ""; }; 83FC5D57181B47FB00B917E5 /* smp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smp.cpp; sourceTree = ""; }; @@ -609,8 +709,8 @@ 17C8F1860CBED26C008D969D /* Source */ = { isa = PBXGroup; children = ( + 83BB5DAB1C08427700734457 /* vgmplay */, 83FC5D35181B47FB00B917E5 /* higan */, - 8370B68C17F615FD001A4D7A /* adlib.h */, 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */, 8370B68E17F615FD001A4D7A /* Ay_Core.h */, 8370B68F17F615FD001A4D7A /* blargg_common.cpp */, @@ -620,24 +720,8 @@ 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */, 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */, 8370B69517F615FD001A4D7A /* Bml_Parser.h */, - 8370B69617F615FD001A4D7A /* C140_Emu.cpp */, - 8370B69717F615FD001A4D7A /* C140_Emu.h */, - 8370B69817F615FD001A4D7A /* c140.c */, - 8370B69917F615FD001A4D7A /* c140.h */, - 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */, - 8370B69B17F615FD001A4D7A /* dac_control.c */, - 8370B69C17F615FD001A4D7A /* dac_control.h */, - 8370B69D17F615FD001A4D7A /* dbopl.cpp */, - 8370B69E17F615FD001A4D7A /* dbopl.h */, - 8370B69F17F615FD001A4D7A /* divfix.h */, 8370B6A017F615FD001A4D7A /* Downsampler.cpp */, 8370B6A117F615FD001A4D7A /* Downsampler.h */, - 8370B6A217F615FD001A4D7A /* emuconfig.h */, - 8370B6A317F615FD001A4D7A /* fm.c */, - 8370B6A417F615FD001A4D7A /* fm.h */, - 8370B6A517F615FD001A4D7A /* fm2612.c */, - 8370B6A617F615FD001A4D7A /* fmopl.cpp */, - 8370B6A717F615FD001A4D7A /* fmopl.h */, 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */, 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */, 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */, @@ -649,82 +733,27 @@ 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */, 8370B6B317F615FD001A4D7A /* Hes_Core.h */, 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */, - 8370B6B517F615FD001A4D7A /* i_fmpac.h */, - 8370B6B617F615FD001A4D7A /* i_fmunit.h */, - 8370B6B717F615FD001A4D7A /* i_vrc7.h */, - 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */, - 8370B6B917F615FD001A4D7A /* K051649_Emu.h */, - 8370B6BA17F615FD001A4D7A /* k051649.c */, - 8370B6BB17F615FD001A4D7A /* k051649.h */, - 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */, - 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */, - 8370B6BE17F615FD001A4D7A /* k053260.c */, - 8370B6BF17F615FD001A4D7A /* k053260.h */, - 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */, - 8370B6C117F615FD001A4D7A /* K054539_Emu.h */, - 8370B6C217F615FD001A4D7A /* k054539.c */, - 8370B6C317F615FD001A4D7A /* k054539.h */, - 8370B6C417F615FD001A4D7A /* kmsnddev.h */, 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */, 8370B6C617F615FD001A4D7A /* Kss_Core.h */, - 8370B6C717F615FD001A4D7A /* mamedef.h */, - 8370B6C817F615FD001A4D7A /* mathdefs.h */, 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */, 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */, 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */, 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */, 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */, 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */, - 8370B6CF17F615FD001A4D7A /* nestypes.h */, 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */, 8370B6D117F615FD001A4D7A /* Nsf_Core.h */, 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */, 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */, 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */, - 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */, - 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */, - 8370B6D717F615FD001A4D7A /* okim6258.c */, - 8370B6D817F615FD001A4D7A /* okim6258.h */, - 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */, - 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */, - 8370B6DB17F615FD001A4D7A /* okim6295.c */, - 8370B6DC17F615FD001A4D7A /* okim6295.h */, 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */, 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */, - 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */, - 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */, - 8370B6E117F615FE001A4D7A /* pwm.c */, - 8370B6E217F615FE001A4D7A /* pwm.h */, - 8370B6E317F615FE001A4D7A /* qmix.c */, - 8370B6E417F615FE001A4D7A /* qmix.h */, - 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */, - 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */, 8370B6E717F615FE001A4D7A /* Resampler.cpp */, 8370B6E817F615FE001A4D7A /* Resampler.h */, - 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */, - 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */, - 8370B6EB17F615FE001A4D7A /* rf5c68.c */, - 8370B6EC17F615FE001A4D7A /* rf5c68.h */, - 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */, - 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */, 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */, 8370B6F017F615FE001A4D7A /* Rom_Data.h */, - 8370B6F117F615FE001A4D7A /* s_deltat.c */, - 8370B6F217F615FE001A4D7A /* s_deltat.h */, - 8370B6F317F615FE001A4D7A /* s_logtbl.c */, - 8370B6F417F615FE001A4D7A /* s_logtbl.h */, - 8370B6F517F615FE001A4D7A /* s_opl.c */, - 8370B6F617F615FE001A4D7A /* s_opl.h */, - 8370B6F717F615FE001A4D7A /* s_opltbl.c */, - 8370B6F817F615FE001A4D7A /* s_opltbl.h */, 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */, 8370B6FA17F615FE001A4D7A /* Sap_Core.h */, - 8370B6FB17F615FE001A4D7A /* scd_pcm.c */, - 8370B6FC17F615FE001A4D7A /* scd_pcm.h */, - 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */, - 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */, - 8370B6FF17F615FE001A4D7A /* segapcm.c */, - 8370B70017F615FE001A4D7A /* segapcm.h */, 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */, 8370B70217F615FE001A4D7A /* Sgc_Core.h */, 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */, @@ -744,30 +773,6 @@ 8370B71117F615FE001A4D7A /* Upsampler.h */, 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */, 8370B71317F615FE001A4D7A /* Vgm_Core.h */, - 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */, - 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */, - 8370B71617F615FE001A4D7A /* ym2151.c */, - 8370B71717F615FE001A4D7A /* ym2151.h */, - 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */, - 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */, - 8370B71A17F615FE001A4D7A /* ym2413.c */, - 8370B71B17F615FE001A4D7A /* ym2413.h */, - 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */, - 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */, - 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */, - 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */, - 8370B72017F615FE001A4D7A /* Ym2612_Emu_Gens.cpp */, - 8370B72117F615FE001A4D7A /* Ym2612_Emu_MAME.cpp */, - 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */, - 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */, - 8370B72417F615FE001A4D7A /* ymdeltat.cpp */, - 8370B72517F615FE001A4D7A /* ymdeltat.h */, - 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */, - 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */, - 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */, - 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */, - 8370B72A17F615FE001A4D7A /* ymz280b.c */, - 8370B72B17F615FE001A4D7A /* ymz280b.h */, 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */, 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */, 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */, @@ -857,6 +862,167 @@ name = Source; sourceTree = ""; }; + 83BB5DAB1C08427700734457 /* vgmplay */ = { + isa = PBXGroup; + children = ( + 83BB5DAC1C0842A600734457 /* chips */, + 83BB5E391C0842A600734457 /* VGMPlay.c */, + 83BB5E3A1C0842A600734457 /* ChipMapper.c */, + 83BB5E3B1C0842A600734457 /* ChipMapper.h */, + 83BB5E3C1C0842A600734457 /* VGMFile.h */, + 83BB5E3D1C0842A600734457 /* VGMPlay_Intf.h */, + 83BB5E3E1C0842A600734457 /* VGMPlay.h */, + 83BB5E3F1C0842A600734457 /* resampler.c */, + 83BB5E401C0842A600734457 /* resampler.h */, + ); + name = vgmplay; + sourceTree = ""; + }; + 83BB5DAC1C0842A600734457 /* chips */ = { + isa = PBXGroup; + children = ( + 83BB5DAD1C0842A600734457 /* 2151intf.c */, + 83BB5DAE1C0842A600734457 /* 2151intf.h */, + 83BB5DAF1C0842A600734457 /* 2203intf.c */, + 83BB5DB01C0842A600734457 /* 2203intf.h */, + 83BB5DB11C0842A600734457 /* 2413intf.c */, + 83BB5DB21C0842A600734457 /* 2413intf.h */, + 83BB5DB31C0842A600734457 /* 2413tone.h */, + 83BB5DB41C0842A600734457 /* 2608intf.c */, + 83BB5DB51C0842A600734457 /* 2608intf.h */, + 83BB5DB61C0842A600734457 /* 2610intf.c */, + 83BB5DB71C0842A600734457 /* 2610intf.h */, + 83BB5DB81C0842A600734457 /* 2612intf.c */, + 83BB5DB91C0842A600734457 /* 2612intf.h */, + 83BB5DBA1C0842A600734457 /* 262intf.c */, + 83BB5DBB1C0842A600734457 /* 262intf.h */, + 83BB5DBC1C0842A600734457 /* 281btone.h */, + 83BB5DBD1C0842A600734457 /* 3526intf.c */, + 83BB5DBE1C0842A600734457 /* 3526intf.h */, + 83BB5DBF1C0842A600734457 /* 3812intf.c */, + 83BB5DC01C0842A600734457 /* 3812intf.h */, + 83BB5DC11C0842A600734457 /* 8950intf.c */, + 83BB5DC21C0842A600734457 /* 8950intf.h */, + 83BB5DC31C0842A600734457 /* adlibemu.h */, + 83BB5DC41C0842A600734457 /* adlibemu_opl2.c */, + 83BB5DC51C0842A600734457 /* adlibemu_opl3.c */, + 83BB5DC61C0842A600734457 /* ay8910.c */, + 83BB5DC71C0842A600734457 /* ay8910.h */, + 83BB5DC81C0842A600734457 /* ay_intf.c */, + 83BB5DC91C0842A600734457 /* ay_intf.h */, + 83BB5DCA1C0842A600734457 /* c140.c */, + 83BB5DCB1C0842A600734457 /* c140.h */, + 83BB5DCC1C0842A600734457 /* c352.c */, + 83BB5DCD1C0842A600734457 /* c352.h */, + 83BB5DCE1C0842A600734457 /* c6280.c */, + 83BB5DCF1C0842A600734457 /* c6280.h */, + 83BB5DD01C0842A600734457 /* c6280intf.c */, + 83BB5DD11C0842A600734457 /* c6280intf.h */, + 83BB5DD21C0842A600734457 /* ChipIncl.h */, + 83BB5DD31C0842A600734457 /* dac_control.c */, + 83BB5DD41C0842A600734457 /* dac_control.h */, + 83BB5DD51C0842A600734457 /* emu2149.c */, + 83BB5DD61C0842A600734457 /* emu2149.h */, + 83BB5DD71C0842A600734457 /* emu2413.c */, + 83BB5DD81C0842A600734457 /* emu2413.h */, + 83BB5DD91C0842A600734457 /* emu2413_NESpatches.txt */, + 83BB5DDA1C0842A600734457 /* emutypes.h */, + 83BB5DDB1C0842A600734457 /* es5503.c */, + 83BB5DDC1C0842A600734457 /* es5503.h */, + 83BB5DDD1C0842A600734457 /* es5506.c */, + 83BB5DDE1C0842A600734457 /* es5506.h */, + 83BB5DDF1C0842A600734457 /* fm.c */, + 83BB5DE01C0842A600734457 /* fm.h */, + 83BB5DE11C0842A600734457 /* fm2612.c */, + 83BB5DE21C0842A600734457 /* fmopl.c */, + 83BB5DE31C0842A600734457 /* fmopl.h */, + 83BB5DE41C0842A600734457 /* gb.c */, + 83BB5DE51C0842A600734457 /* gb.h */, + 83BB5DE61C0842A600734457 /* iremga20.c */, + 83BB5DE71C0842A600734457 /* iremga20.h */, + 83BB5DE81C0842A600734457 /* k051649.c */, + 83BB5DE91C0842A600734457 /* k051649.h */, + 83BB5DEA1C0842A600734457 /* k053260.c */, + 83BB5DEB1C0842A600734457 /* k053260.h */, + 83BB5DEC1C0842A600734457 /* k054539.c */, + 83BB5DED1C0842A600734457 /* k054539.h */, + 83BB5DEE1C0842A600734457 /* mamedef.h */, + 83BB5DEF1C0842A600734457 /* multipcm.c */, + 83BB5DF01C0842A600734457 /* multipcm.h */, + 83BB5DF11C0842A600734457 /* nes_apu.c */, + 83BB5DF21C0842A600734457 /* nes_apu.h */, + 83BB5DF31C0842A600734457 /* nes_defs.h */, + 83BB5DF41C0842A600734457 /* nes_intf.c */, + 83BB5DF51C0842A600734457 /* nes_intf.h */, + 83BB5DF61C0842A600734457 /* np_nes_apu.c */, + 83BB5DF71C0842A600734457 /* np_nes_apu.h */, + 83BB5DF81C0842A600734457 /* np_nes_dmc.c */, + 83BB5DF91C0842A600734457 /* np_nes_dmc.h */, + 83BB5DFA1C0842A600734457 /* np_nes_fds.c */, + 83BB5DFB1C0842A600734457 /* np_nes_fds.h */, + 83BB5DFC1C0842A600734457 /* okim6258.c */, + 83BB5DFD1C0842A600734457 /* okim6258.h */, + 83BB5DFE1C0842A600734457 /* okim6295.c */, + 83BB5DFF1C0842A600734457 /* okim6295.h */, + 83BB5E001C0842A600734457 /* Ootake_PSG.c */, + 83BB5E011C0842A600734457 /* Ootake_PSG.h */, + 83BB5E041C0842A600734457 /* panning.c */, + 83BB5E051C0842A600734457 /* panning.h */, + 83BB5E061C0842A600734457 /* pokey.c */, + 83BB5E071C0842A600734457 /* pokey.h */, + 83BB5E081C0842A600734457 /* pwm.c */, + 83BB5E091C0842A600734457 /* pwm.h */, + 83BB5E0A1C0842A600734457 /* qsound.c */, + 83BB5E0B1C0842A600734457 /* qsound.h */, + 83BB5E0C1C0842A600734457 /* rf5c68.c */, + 83BB5E0D1C0842A600734457 /* rf5c68.h */, + 83BB5E0E1C0842A600734457 /* saa1099.c */, + 83BB5E0F1C0842A600734457 /* saa1099.h */, + 83BB5E101C0842A600734457 /* scd_pcm.c */, + 83BB5E111C0842A600734457 /* scd_pcm.h */, + 83BB5E121C0842A600734457 /* scsp.c */, + 83BB5E131C0842A600734457 /* scsp.h */, + 83BB5E141C0842A600734457 /* scspdsp.c */, + 83BB5E151C0842A600734457 /* scspdsp.h */, + 83BB5E171C0842A600734457 /* segapcm.c */, + 83BB5E181C0842A600734457 /* segapcm.h */, + 83BB5E191C0842A600734457 /* sn76489.c */, + 83BB5E1A1C0842A600734457 /* sn76489.h */, + 83BB5E1B1C0842A600734457 /* sn76496.c */, + 83BB5E1C1C0842A600734457 /* sn76496.h */, + 83BB5E1D1C0842A600734457 /* sn764intf.c */, + 83BB5E1E1C0842A600734457 /* sn764intf.h */, + 83BB5E1F1C0842A600734457 /* upd7759.c */, + 83BB5E201C0842A600734457 /* upd7759.h */, + 83BB5E211C0842A600734457 /* vrc7tone.h */, + 83BB5E221C0842A600734457 /* vsu.c */, + 83BB5E231C0842A600734457 /* vsu.h */, + 83BB5E241C0842A600734457 /* ws_audio.c */, + 83BB5E251C0842A600734457 /* ws_audio.h */, + 83BB5E261C0842A600734457 /* ws_initialIo.h */, + 83BB5E271C0842A600734457 /* x1_010.c */, + 83BB5E281C0842A600734457 /* x1_010.h */, + 83BB5E291C0842A600734457 /* ym2151.c */, + 83BB5E2A1C0842A600734457 /* ym2151.h */, + 83BB5E2B1C0842A600734457 /* ym2413.c */, + 83BB5E2C1C0842A600734457 /* ym2413.h */, + 83BB5E2D1C0842A600734457 /* ym2612.c */, + 83BB5E2E1C0842A600734457 /* ym2612.h */, + 83BB5E2F1C0842A600734457 /* ymdeltat.c */, + 83BB5E301C0842A600734457 /* ymdeltat.h */, + 83BB5E311C0842A600734457 /* ymf262.c */, + 83BB5E321C0842A600734457 /* ymf262.h */, + 83BB5E331C0842A600734457 /* ymf271.c */, + 83BB5E341C0842A600734457 /* ymf271.h */, + 83BB5E351C0842A600734457 /* ymf278b.c */, + 83BB5E361C0842A600734457 /* ymf278b.h */, + 83BB5E371C0842A600734457 /* ymz280b.c */, + 83BB5E381C0842A600734457 /* ymz280b.h */, + ); + name = chips; + path = vgmplay/chips; + sourceTree = ""; + }; 83FC5D35181B47FB00B917E5 /* higan */ = { isa = PBXGroup; children = ( @@ -918,37 +1084,44 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 83BB5ECF1C0842A600734457 /* ChipMapper.h in Headers */, + 83BB5E861C0842A600734457 /* nes_apu.h in Headers */, 8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */, + 83BB5EBC1C0842A600734457 /* x1_010.h in Headers */, 8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */, - 8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */, + 83BB5EBE1C0842A600734457 /* ym2151.h in Headers */, 8370B7A817F615FE001A4D7A /* Sgc_Emu.h in Headers */, - 8370B74217F615FE001A4D7A /* divfix.h in Headers */, - 8370B76717F615FE001A4D7A /* kmsnddev.h in Headers */, - 8370B78D17F615FE001A4D7A /* Rf5C68_Emu.h in Headers */, - 8370B7A317F615FE001A4D7A /* segapcm.h in Headers */, - 8370B7CE17F615FE001A4D7A /* ymz280b.h in Headers */, - 8370B74717F615FE001A4D7A /* fm.h in Headers */, 17C8F1F50CBED286008D969D /* Ay_Apu.h in Headers */, + 83BB5E7B1C0842A600734457 /* iremga20.h in Headers */, 17C8F1F90CBED286008D969D /* Ay_Emu.h in Headers */, + 83BB5E4B1C0842A600734457 /* 2610intf.h in Headers */, 8370B74B17F615FE001A4D7A /* Gb_Cpu_run.h in Headers */, - 8370B7A117F615FE001A4D7A /* SegaPcm_Emu.h in Headers */, 17C8F1FA0CBED286008D969D /* blargg_common.h in Headers */, + 83BB5E4F1C0842A600734457 /* 262intf.h in Headers */, + 83BB5E791C0842A600734457 /* gb.h in Headers */, + 83BB5E491C0842A600734457 /* 2608intf.h in Headers */, 8370B75717F615FE001A4D7A /* Hes_Cpu_run.h in Headers */, + 83BB5E9D1C0842A600734457 /* pwm.h in Headers */, + 83BB5ECC1C0842A600734457 /* ymz280b.h in Headers */, 17C8F1FB0CBED286008D969D /* blargg_config.h in Headers */, - 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */, + 83BB5E631C0842A600734457 /* c6280.h in Headers */, + 83BB5ED21C0842A600734457 /* VGMPlay.h in Headers */, + 83BB5E8D1C0842A600734457 /* np_nes_dmc.h in Headers */, + 83BB5EA91C0842A600734457 /* scspdsp.h in Headers */, 17C8F1FC0CBED286008D969D /* blargg_endian.h in Headers */, 83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */, 17C8F1FD0CBED286008D969D /* blargg_source.h in Headers */, + 83BB5E521C0842A600734457 /* 3526intf.h in Headers */, + 83BB5E7D1C0842A600734457 /* k051649.h in Headers */, 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */, + 83BB5EA51C0842A600734457 /* scd_pcm.h in Headers */, 83FC5D79181B47FB00B917E5 /* smp.hpp in Headers */, - 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */, - 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */, 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */, - 8370B76417F615FE001A4D7A /* K054539_Emu.h in Headers */, - 8370B7BC17F615FE001A4D7A /* Ym2203_Emu.h in Headers */, + 83BB5E6A1C0842A600734457 /* emu2149.h in Headers */, + 83BB5EA71C0842A600734457 /* scsp.h in Headers */, 8370B78117F615FE001A4D7A /* Opl_Apu.h in Headers */, - 8370B79517F615FE001A4D7A /* s_deltat.h in Headers */, - 8370B77B17F615FE001A4D7A /* okim6258.h in Headers */, + 83BB5EC61C0842A600734457 /* ymf262.h in Headers */, + 83BB5E721C0842A600734457 /* es5506.h in Headers */, 8370B7B417F615FE001A4D7A /* Upsampler.h in Headers */, 8370B75417F615FE001A4D7A /* Hes_Apu_Adpcm.h in Headers */, 17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */, @@ -956,101 +1129,118 @@ 17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */, 17C8F2030CBED286008D969D /* Data_Reader.h in Headers */, 17C8F2050CBED286008D969D /* Dual_Resampler.h in Headers */, - 8370B76617F615FE001A4D7A /* k054539.h in Headers */, - 8370B78717F615FE001A4D7A /* qmix.h in Headers */, 8370B73517F615FE001A4D7A /* Blip_Buffer_impl.h in Headers */, + 83BB5EC01C0842A600734457 /* ym2413.h in Headers */, 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */, 17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */, + 83BB5E951C0842A600734457 /* Ootake_PSG.h in Headers */, + 83BB5E4D1C0842A600734457 /* 2612intf.h in Headers */, 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */, + 83BB5E821C0842A600734457 /* mamedef.h in Headers */, + 83BB5E501C0842A600734457 /* 281btone.h in Headers */, + 83BB5EB91C0842A600734457 /* ws_audio.h in Headers */, + 83BB5E611C0842A600734457 /* c352.h in Headers */, + 83BB5E6E1C0842A600734457 /* emutypes.h in Headers */, 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */, - 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */, - 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */, + 83BB5ED01C0842A600734457 /* VGMFile.h in Headers */, 83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */, 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */, 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */, 83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */, - 8370B77217F615FE001A4D7A /* nestypes.h in Headers */, 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */, 17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */, 8370B75217F615FE001A4D7A /* Gme_Loader.h in Headers */, - 8370B72F17F615FE001A4D7A /* adlib.h in Headers */, 8370B7B217F615FE001A4D7A /* Track_Filter.h in Headers */, 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */, + 83BB5EB41C0842A600734457 /* upd7759.h in Headers */, + 83BB5EA11C0842A600734457 /* rf5c68.h in Headers */, 17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */, 83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */, 17C8F2140CBED286008D969D /* Gme_File.h in Headers */, - 8370B76217F615FE001A4D7A /* k053260.h in Headers */, - 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */, - 8370B75C17F615FE001A4D7A /* K051649_Emu.h in Headers */, - 8370B73F17F615FE001A4D7A /* dac_control.h in Headers */, 17C8F2160CBED286008D969D /* gme.h in Headers */, 17C8F2190CBED286008D969D /* Gym_Emu.h in Headers */, 17C8F21B0CBED286008D969D /* Hes_Apu.h in Headers */, + 83BB5E771C0842A600734457 /* fmopl.h in Headers */, 17C8F21E0CBED286008D969D /* Hes_Cpu.h in Headers */, - 8370B76B17F615FE001A4D7A /* mathdefs.h in Headers */, - 8370B7C817F615FE001A4D7A /* ymdeltat.h in Headers */, 17C8F2200CBED286008D969D /* Hes_Emu.h in Headers */, - 8370B78517F615FE001A4D7A /* pwm.h in Headers */, - 8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */, + 83BB5E811C0842A600734457 /* k054539.h in Headers */, 8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */, - 8370B74517F615FE001A4D7A /* emuconfig.h in Headers */, + 83BB5E421C0842A600734457 /* 2151intf.h in Headers */, 17C8F2240CBED286008D969D /* Kss_Emu.h in Headers */, + 83BB5ED41C0842A600734457 /* resampler.h in Headers */, + 83BB5E9B1C0842A600734457 /* pokey.h in Headers */, + 83BB5ED11C0842A600734457 /* VGMPlay_Intf.h in Headers */, 17C8F2260CBED286008D969D /* Kss_Scc_Apu.h in Headers */, + 83BB5EB01C0842A600734457 /* sn76496.h in Headers */, + 83BB5E651C0842A600734457 /* c6280intf.h in Headers */, + 83BB5EC21C0842A600734457 /* ym2612.h in Headers */, 17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */, 17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */, 8370B78B17F615FE001A4D7A /* Resampler.h in Headers */, - 8370B74117F615FE001A4D7A /* dbopl.h in Headers */, 8370B74D17F615FE001A4D7A /* Gbs_Core.h in Headers */, + 83BB5E661C0842A600734457 /* ChipIncl.h in Headers */, + 83BB5EAE1C0842A600734457 /* sn76489.h in Headers */, + 83BB5E6C1C0842A600734457 /* emu2413.h in Headers */, + 83BB5E8B1C0842A600734457 /* np_nes_apu.h in Headers */, + 83BB5E9F1C0842A600734457 /* qsound.h in Headers */, + 83BB5E541C0842A600734457 /* 3812intf.h in Headers */, + 83BB5E8F1C0842A600734457 /* np_nes_fds.h in Headers */, + 83BB5E871C0842A600734457 /* nes_defs.h in Headers */, 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */, + 83BB5E841C0842A600734457 /* multipcm.h in Headers */, 8370B7AC17F615FE001A4D7A /* Sms_Fm_Apu.h in Headers */, - 8370B73D17F615FE001A4D7A /* Chip_Resampler.h in Headers */, + 83BB5EBA1C0842A600734457 /* ws_initialIo.h in Headers */, + 83BB5E5F1C0842A600734457 /* c140.h in Headers */, 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */, 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */, 8370B73817F615FE001A4D7A /* Bml_Parser.h in Headers */, - 8370B79117F615FE001A4D7A /* Rf5C164_Emu.h in Headers */, 17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */, - 8370B75817F615FE001A4D7A /* i_fmpac.h in Headers */, - 8370B73A17F615FE001A4D7A /* C140_Emu.h in Headers */, - 8370B77F17F615FE001A4D7A /* okim6295.h in Headers */, + 83BB5EB71C0842A600734457 /* vsu.h in Headers */, + 83BB5E991C0842A600734457 /* panning.h in Headers */, 17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */, 8370B73417F615FE001A4D7A /* blargg_errors.h in Headers */, 17C8F2380CBED286008D969D /* Nes_Oscs.h in Headers */, - 8370B74A17F615FE001A4D7A /* fmopl.h in Headers */, + 83BB5E441C0842A600734457 /* 2203intf.h in Headers */, + 83BB5EB51C0842A600734457 /* vrc7tone.h in Headers */, 17C8F23A0CBED286008D969D /* Nes_Vrc6_Apu.h in Headers */, 17C8F23C0CBED286008D969D /* Nsf_Emu.h in Headers */, - 8370B75917F615FE001A4D7A /* i_fmunit.h in Headers */, 17C8F23E0CBED286008D969D /* Nsfe_Emu.h in Headers */, - 8370B7BE17F615FE001A4D7A /* ym2413.h in Headers */, + 83BB5E891C0842A600734457 /* nes_intf.h in Headers */, + 83BB5EB21C0842A600734457 /* sn764intf.h in Headers */, + 83BB5E5D1C0842A600734457 /* ay_intf.h in Headers */, + 83BB5E5B1C0842A600734457 /* ay8910.h in Headers */, + 83BB5E701C0842A600734457 /* es5503.h in Headers */, + 83BB5E741C0842A600734457 /* fm.h in Headers */, 8370B73117F615FE001A4D7A /* Ay_Core.h in Headers */, + 83BB5E7F1C0842A600734457 /* k053260.h in Headers */, + 83BB5E911C0842A600734457 /* okim6258.h in Headers */, 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */, + 83BB5EC41C0842A600734457 /* ymdeltat.h in Headers */, 8370B76F17F615FE001A4D7A /* Nes_Mmc5_Apu.h in Headers */, 17C8F2400CBED286008D969D /* Sap_Apu.h in Headers */, 17C8F2450CBED286008D969D /* Sap_Emu.h in Headers */, - 8370B73C17F615FE001A4D7A /* c140.h in Headers */, + 83BB5E461C0842A600734457 /* 2413intf.h in Headers */, 8370B76E17F615FE001A4D7A /* Nes_Fds_Apu.h in Headers */, + 83BB5E561C0842A600734457 /* 8950intf.h in Headers */, 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */, - 8370B79917F615FE001A4D7A /* s_opl.h in Headers */, - 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */, + 83BB5E931C0842A600734457 /* okim6295.h in Headers */, 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */, 8370B7AA17F615FE001A4D7A /* Sgc_Impl.h in Headers */, - 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */, 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */, - 8370B75E17F615FE001A4D7A /* k051649.h in Headers */, - 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */, - 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */, + 83BB5E471C0842A600734457 /* 2413tone.h in Headers */, 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */, - 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */, - 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */, - 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */, 8370B76917F615FE001A4D7A /* Kss_Core.h in Headers */, 8370B7B617F615FE001A4D7A /* Vgm_Core.h in Headers */, + 83BB5ECA1C0842A600734457 /* ymf278b.h in Headers */, + 83BB5E571C0842A600734457 /* adlibemu.h in Headers */, 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */, - 8370B7BA17F615FE001A4D7A /* ym2151.h in Headers */, 17C8F2540CBED286008D969D /* Vgm_Emu.h in Headers */, 17C8F2560CBED286008D969D /* Ym2413_Emu.h in Headers */, - 8370B7CC17F615FE001A4D7A /* Ymz280b_Emu.h in Headers */, - 8370B76A17F615FE001A4D7A /* mamedef.h in Headers */, 17C8F2580CBED286008D969D /* Ym2612_Emu.h in Headers */, + 83BB5EC81C0842A600734457 /* ymf271.h in Headers */, + 83BB5E681C0842A600734457 /* dac_control.h in Headers */, + 83BB5EA31C0842A600734457 /* saa1099.h in Headers */, + 83BB5EAC1C0842A600734457 /* segapcm.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1106,6 +1296,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 83BB5E6D1C0842A600734457 /* emu2413_NESpatches.txt in Resources */, 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1117,123 +1308,149 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8370B74917F615FE001A4D7A /* fmopl.cpp in Sources */, + 83BB5E511C0842A600734457 /* 3526intf.c in Sources */, + 83BB5E941C0842A600734457 /* Ootake_PSG.c in Sources */, + 83BB5E451C0842A600734457 /* 2413intf.c in Sources */, 8370B78A17F615FE001A4D7A /* Resampler.cpp in Sources */, 8370B77017F615FE001A4D7A /* Nes_Vrc7_Apu.cpp in Sources */, + 83BB5EC91C0842A600734457 /* ymf278b.c in Sources */, + 83BB5E6F1C0842A600734457 /* es5503.c in Sources */, 8370B7B117F615FE001A4D7A /* Track_Filter.cpp in Sources */, - 8370B74617F615FE001A4D7A /* fm.c in Sources */, - 8370B76517F615FE001A4D7A /* k054539.c in Sources */, - 8370B7BF17F615FE001A4D7A /* Ym2608_Emu.cpp in Sources */, 17C8F1F40CBED286008D969D /* Ay_Apu.cpp in Sources */, - 8370B7A017F615FE001A4D7A /* SegaPcm_Emu.cpp in Sources */, + 83BB5E4C1C0842A600734457 /* 2612intf.c in Sources */, + 83BB5E751C0842A600734457 /* fm2612.c in Sources */, 17C8F1F60CBED286008D969D /* Ay_Cpu.cpp in Sources */, + 83BB5E901C0842A600734457 /* okim6258.c in Sources */, 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */, 8370B76D17F615FE001A4D7A /* Nes_Fds_Apu.cpp in Sources */, - 8370B7B917F615FE001A4D7A /* ym2151.c in Sources */, 17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */, 8370B79C17F615FE001A4D7A /* Sap_Core.cpp in Sources */, 17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */, + 83BB5EA21C0842A600734457 /* saa1099.c in Sources */, + 83BB5E981C0842A600734457 /* panning.c in Sources */, + 83BB5E9C1C0842A600734457 /* pwm.c in Sources */, 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */, - 8370B79817F615FE001A4D7A /* s_opl.c in Sources */, 8370B79217F615FE001A4D7A /* Rom_Data.cpp in Sources */, - 8370B7BB17F615FE001A4D7A /* Ym2203_Emu.cpp in Sources */, 8370B74E17F615FE001A4D7A /* Gbs_Cpu.cpp in Sources */, 17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */, - 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */, - 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */, + 83BB5ED31C0842A600734457 /* resampler.c in Sources */, + 83BB5E691C0842A600734457 /* emu2149.c in Sources */, 83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */, 17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */, + 83BB5EB61C0842A600734457 /* vsu.c in Sources */, 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */, - 8370B74817F615FE001A4D7A /* fm2612.c in Sources */, + 83BB5EBF1C0842A600734457 /* ym2413.c in Sources */, 8370B7A417F615FE001A4D7A /* Sgc_Core.cpp in Sources */, - 8370B79417F615FE001A4D7A /* s_deltat.c in Sources */, 17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */, 17C8F2080CBED286008D969D /* Fir_Resampler.cpp in Sources */, 8370B77617F615FE001A4D7A /* Nsf_Impl.cpp in Sources */, - 8370B76317F615FE001A4D7A /* K054539_Emu.cpp in Sources */, - 8370B7C917F615FE001A4D7A /* Ymf262_Emu.cpp in Sources */, + 83BB5ECE1C0842A600734457 /* ChipMapper.c in Sources */, + 83BB5E4A1C0842A600734457 /* 2610intf.c in Sources */, 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */, - 8370B74017F615FE001A4D7A /* dbopl.cpp in Sources */, - 8370B7CD17F615FE001A4D7A /* ymz280b.c in Sources */, + 83BB5EC31C0842A600734457 /* ymdeltat.c in Sources */, 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */, + 83BB5EC51C0842A600734457 /* ymf262.c in Sources */, 8370B7A917F615FE001A4D7A /* Sgc_Impl.cpp in Sources */, 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */, - 8370B75B17F615FE001A4D7A /* K051649_Emu.cpp in Sources */, 8370B74317F615FE001A4D7A /* Downsampler.cpp in Sources */, 17C8F2110CBED286008D969D /* Gbs_Emu.cpp in Sources */, + 83BB5E831C0842A600734457 /* multipcm.c in Sources */, 8370B73317F615FE001A4D7A /* blargg_errors.cpp in Sources */, - 8370B73B17F615FE001A4D7A /* c140.c in Sources */, - 8370B77C17F615FE001A4D7A /* Okim6295_Emu.cpp in Sources */, 17C8F2130CBED286008D969D /* Gme_File.cpp in Sources */, + 83BB5E761C0842A600734457 /* fmopl.c in Sources */, 17C8F2150CBED286008D969D /* gme.cpp in Sources */, + 83BB5E851C0842A600734457 /* nes_apu.c in Sources */, + 83BB5E4E1C0842A600734457 /* 262intf.c in Sources */, 17C8F2180CBED286008D969D /* Gym_Emu.cpp in Sources */, - 8370B78E17F615FE001A4D7A /* rf5c68.c in Sources */, + 83BB5E621C0842A600734457 /* c6280.c in Sources */, 8370B74C17F615FE001A4D7A /* Gbs_Core.cpp in Sources */, 17C8F21A0CBED286008D969D /* Hes_Apu.cpp in Sources */, 17C8F21D0CBED286008D969D /* Hes_Cpu.cpp in Sources */, 17C8F21F0CBED286008D969D /* Hes_Emu.cpp in Sources */, - 8370B78617F615FE001A4D7A /* qmix.c in Sources */, + 83BB5E5C1C0842A600734457 /* ay_intf.c in Sources */, 83FC5D5E181B47FB00B917E5 /* dsp.cpp in Sources */, 17C8F2210CBED286008D969D /* Kss_Cpu.cpp in Sources */, - 8370B76117F615FE001A4D7A /* k053260.c in Sources */, - 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */, 17C8F2230CBED286008D969D /* Kss_Emu.cpp in Sources */, + 83BB5E8C1C0842A600734457 /* np_nes_dmc.c in Sources */, + 83BB5E5A1C0842A600734457 /* ay8910.c in Sources */, 17C8F2250CBED286008D969D /* Kss_Scc_Apu.cpp in Sources */, - 8370B7BD17F615FE001A4D7A /* ym2413.c in Sources */, - 8370B79617F615FE001A4D7A /* s_logtbl.c in Sources */, 17C8F2280CBED286008D969D /* M3u_Playlist.cpp in Sources */, 17C8F22A0CBED286008D969D /* Multi_Buffer.cpp in Sources */, - 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */, 17C8F22C0CBED286008D969D /* Music_Emu.cpp in Sources */, - 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */, 83FC5D99181B675900B917E5 /* SPC_DSP.cpp in Sources */, + 83BB5E481C0842A600734457 /* 2608intf.c in Sources */, 17C8F22E0CBED286008D969D /* Nes_Apu.cpp in Sources */, 17C8F2310CBED286008D969D /* Nes_Cpu.cpp in Sources */, + 83BB5E731C0842A600734457 /* fm.c in Sources */, 17C8F2330CBED286008D969D /* Nes_Fme7_Apu.cpp in Sources */, - 8370B7A217F615FE001A4D7A /* segapcm.c in Sources */, 8370B7B517F615FE001A4D7A /* Vgm_Core.cpp in Sources */, - 8370B78417F615FE001A4D7A /* pwm.c in Sources */, + 83BB5EA61C0842A600734457 /* scsp.c in Sources */, 17C8F2350CBED286008D969D /* Nes_Namco_Apu.cpp in Sources */, 8370B7A617F615FE001A4D7A /* Sgc_Cpu.cpp in Sources */, - 8370B77A17F615FE001A4D7A /* okim6258.c in Sources */, + 83BB5E431C0842A600734457 /* 2203intf.c in Sources */, 17C8F2370CBED286008D969D /* Nes_Oscs.cpp in Sources */, + 83BB5E8E1C0842A600734457 /* np_nes_fds.c in Sources */, 17C8F2390CBED286008D969D /* Nes_Vrc6_Apu.cpp in Sources */, 8370B75317F615FE001A4D7A /* Hes_Apu_Adpcm.cpp in Sources */, - 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */, + 83BB5E7E1C0842A600734457 /* k053260.c in Sources */, + 83BB5EC11C0842A600734457 /* ym2612.c in Sources */, 17C8F23B0CBED286008D969D /* Nsf_Emu.cpp in Sources */, 83FC5DAD181B8B1900B917E5 /* spc700.cpp in Sources */, - 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */, - 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */, 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */, + 83BB5E8A1C0842A600734457 /* np_nes_apu.c in Sources */, + 83BB5ECD1C0842A600734457 /* VGMPlay.c in Sources */, + 83BB5E6B1C0842A600734457 /* emu2413.c in Sources */, + 83BB5E711C0842A600734457 /* es5506.c in Sources */, + 83BB5E801C0842A600734457 /* k054539.c in Sources */, + 83BB5EB31C0842A600734457 /* upd7759.c in Sources */, + 83BB5E7C1C0842A600734457 /* k051649.c in Sources */, + 83BB5E601C0842A600734457 /* c352.c in Sources */, + 83BB5E9E1C0842A600734457 /* qsound.c in Sources */, + 83BB5E581C0842A600734457 /* adlibemu_opl2.c in Sources */, + 83BB5E7A1C0842A600734457 /* iremga20.c in Sources */, 8370B77317F615FE001A4D7A /* Nsf_Core.cpp in Sources */, 8370B77517F615FE001A4D7A /* Nsf_Cpu.cpp in Sources */, + 83BB5EBD1C0842A600734457 /* ym2151.c in Sources */, + 83BB5EC71C0842A600734457 /* ymf271.c in Sources */, + 83BB5EA01C0842A600734457 /* rf5c68.c in Sources */, + 83BB5EB11C0842A600734457 /* sn764intf.c in Sources */, 8370B76817F615FE001A4D7A /* Kss_Core.cpp in Sources */, 17C8F23D0CBED286008D969D /* Nsfe_Emu.cpp in Sources */, - 8370B77E17F615FE001A4D7A /* okim6295.c in Sources */, 8370B7A717F615FE001A4D7A /* Sgc_Emu.cpp in Sources */, + 83BB5E881C0842A600734457 /* nes_intf.c in Sources */, 17C8F23F0CBED286008D969D /* Sap_Apu.cpp in Sources */, 8370B75117F615FE001A4D7A /* Gme_Loader.cpp in Sources */, - 8370B73E17F615FE001A4D7A /* dac_control.c in Sources */, - 8370B73917F615FE001A4D7A /* C140_Emu.cpp in Sources */, + 83BB5EA81C0842A600734457 /* scspdsp.c in Sources */, + 83BB5E781C0842A600734457 /* gb.c in Sources */, 17C8F2420CBED286008D969D /* Sap_Cpu.cpp in Sources */, 8370B73717F615FE001A4D7A /* Bml_Parser.cpp in Sources */, + 83BB5E641C0842A600734457 /* c6280intf.c in Sources */, + 83BB5EA41C0842A600734457 /* scd_pcm.c in Sources */, 17C8F2440CBED286008D969D /* Sap_Emu.cpp in Sources */, 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */, + 83BB5E411C0842A600734457 /* 2151intf.c in Sources */, + 83BB5ECB1C0842A600734457 /* ymz280b.c in Sources */, 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */, + 83BB5E5E1C0842A600734457 /* c140.c in Sources */, 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */, + 83BB5E551C0842A600734457 /* 8950intf.c in Sources */, 8370B7B317F615FE001A4D7A /* Upsampler.cpp in Sources */, - 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */, 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */, + 83BB5E591C0842A600734457 /* adlibemu_opl3.c in Sources */, + 83BB5E9A1C0842A600734457 /* pokey.c in Sources */, + 83BB5EAD1C0842A600734457 /* sn76489.c in Sources */, 8370B7AB17F615FE001A4D7A /* Sms_Fm_Apu.cpp in Sources */, + 83BB5EBB1C0842A600734457 /* x1_010.c in Sources */, 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */, - 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */, - 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */, 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */, + 83BB5E671C0842A600734457 /* dac_control.c in Sources */, + 83BB5EAB1C0842A600734457 /* segapcm.c in Sources */, + 83BB5E531C0842A600734457 /* 3812intf.c in Sources */, 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */, + 83BB5E921C0842A600734457 /* okim6295.c in Sources */, + 83BB5EB81C0842A600734457 /* ws_audio.c in Sources */, + 83BB5EAF1C0842A600734457 /* sn76496.c in Sources */, 17C8F2550CBED286008D969D /* Ym2413_Emu.cpp in Sources */, - 8370B79E17F615FE001A4D7A /* scd_pcm.c in Sources */, - 8370B75D17F615FE001A4D7A /* k051649.c in Sources */, - 8370B7B717F615FE001A4D7A /* Ym2151_Emu.cpp in Sources */, 17C8F2570CBED286008D969D /* Ym2612_Emu.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1271,6 +1488,7 @@ GCC_PREPROCESSOR_DEFINITIONS = ( HAVE_STDINT_H, "DEBUG=1", + NO_ZLIB, ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; @@ -1301,6 +1519,7 @@ GCC_PREPROCESSOR_DEFINITIONS = ( HAVE_STDINT_H, NDEBUG, + NO_ZLIB, ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; diff --git a/Frameworks/GME/gme/Ay_Apu.cpp b/Frameworks/GME/gme/Ay_Apu.cpp index 931e4e542..b06729c93 100644 --- a/Frameworks/GME/gme/Ay_Apu.cpp +++ b/Frameworks/GME/gme/Ay_Apu.cpp @@ -1,412 +1,412 @@ -// $package. http://www.slack.net/~ant/ - -#include "Ay_Apu.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// Emulation inaccuracies: -// * Noise isn't run when not in use -// * Changes to envelope and noise periods are delayed until next reload -// * Super-sonic tone should attenuate output to about 60%, not 50% - -// Tones above this frequency are treated as disabled tone at half volume. -// Power of two is more efficient (avoids division). -int const inaudible_freq = 16384; - -int const period_factor = 16; - -static byte const amp_table [16] = -{ -#define ENTRY( n ) byte (n * Ay_Apu::amp_range + 0.5) - // With channels tied together and 1K resistor to ground (as datasheet recommends), - // output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step. - ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625), - ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500), - ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000), - ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000), - - /* - // Measured from an AY-3-8910A chip with date code 8611. - - // Direct voltages without any load (very linear) - ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785), - ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032), - ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043), - ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000), - // With only some load - ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876), - ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388), - ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945), - ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000), - */ -#undef ENTRY -}; - -static byte const modes [8] = -{ -#define MODE( a0,a1, b0,b1, c0,c1 ) \ - (a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5) - MODE( 1,0, 1,0, 1,0 ), - MODE( 1,0, 0,0, 0,0 ), - MODE( 1,0, 0,1, 1,0 ), - MODE( 1,0, 1,1, 1,1 ), - MODE( 0,1, 0,1, 0,1 ), - MODE( 0,1, 1,1, 1,1 ), - MODE( 0,1, 1,0, 0,1 ), - MODE( 0,1, 0,0, 0,0 ), -}; - -void Ay_Apu::set_output( Blip_Buffer* b ) -{ - for ( int i = 0; i < osc_count; ++i ) - set_output( i, b ); -} - -Ay_Apu::Ay_Apu() -{ - // build full table of the upper 8 envelope waveforms - for ( int m = 8; m--; ) - { - byte* out = env_modes [m]; - int flags = modes [m]; - for ( int x = 3; --x >= 0; ) - { - int amp = flags & 1; - int end = flags >> 1 & 1; - int step = end - amp; - amp *= 15; - for ( int y = 16; --y >= 0; ) - { - *out++ = amp_table [amp]; - amp += step; - } - flags >>= 2; - } - } - - type_ = Ay8910; - set_output( NULL ); - volume( 1.0 ); - reset(); -} - -void Ay_Apu::reset() -{ - addr_ = 0; - last_time = 0; - noise_delay = 0; - noise_lfsr = 1; - - for ( osc_t* osc = &oscs [osc_count]; osc != oscs; ) - { - osc--; - osc->period = period_factor; - osc->delay = 0; - osc->last_amp = 0; - osc->phase = 0; - } - - for ( int i = sizeof regs; --i >= 0; ) - regs [i] = 0; - regs [7] = 0xFF; - write_data_( 13, 0 ); -} - -int Ay_Apu::read() -{ - static byte const masks [reg_count] = { - 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F, - 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00 - }; - if (!(type_ & 0x10)) return regs [addr_] & masks [addr_]; - else return regs [addr_]; -} - -void Ay_Apu::write_data_( int addr, int data ) -{ - assert( (unsigned) addr < reg_count ); - - if ( (unsigned) addr >= 14 ) - dprintf( "Wrote to I/O port %02X\n", (int) addr ); - - // envelope mode - if ( addr == 13 ) - { - if ( !(data & 8) ) // convert modes 0-7 to proper equivalents - data = (data & 4) ? 15 : 9; - env_wave = env_modes [data - 7]; - env_pos = -48; - env_delay = 0; // will get set to envelope period in run_until() - } - regs [addr] = data; - - // handle period changes accurately - int i = addr >> 1; - if ( i < osc_count ) - { - blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) + - regs [i * 2] * period_factor; - if ( !period ) - period = period_factor; - - // adjust time of next timer expiration based on change in period - osc_t& osc = oscs [i]; - if ( (osc.delay += period - osc.period) < 0 ) - osc.delay = 0; - osc.period = period; - } - - // TODO: same as above for envelope timer, and it also has a divide by two after it -} - -int const noise_off = 0x08; -int const tone_off = 0x01; - -void Ay_Apu::run_until( blip_time_t final_end_time ) -{ - require( final_end_time >= last_time ); - - // noise period and initial values - blip_time_t const noise_period_factor = period_factor * 2; // verified - blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor; - if ( !noise_period ) - noise_period = noise_period_factor; - blip_time_t const old_noise_delay = noise_delay; - unsigned const old_noise_lfsr = noise_lfsr; - - // envelope period - int env_step_scale = ((type_ & 0xF0) == 0x00) ? 1 : 0; - blip_time_t const env_period_factor = period_factor << env_step_scale; // verified - blip_time_t env_period = (regs [12] * 0x100 + regs [11]) * env_period_factor; - if ( !env_period ) - env_period = env_period_factor; // same as period 1 on my AY chip - if ( !env_delay ) - env_delay = env_period; - - // run each osc separately - for ( int index = 0; index < osc_count; index++ ) - { - osc_t* const osc = &oscs [index]; - int osc_mode = regs [7] >> index; - - // output - Blip_Buffer* const osc_output = osc->output; - if ( !osc_output ) - continue; - osc_output->set_modified(); - - // period - int half_vol = 0; - blip_time_t inaudible_period = (unsigned) (osc_output->clock_rate() + - inaudible_freq) / (unsigned) (inaudible_freq * 2); - if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) - { - half_vol = 1; // Actually around 60%, but 50% is close enough - osc_mode |= tone_off; - } - - // envelope - blip_time_t start_time = last_time; - blip_time_t end_time = final_end_time; - int const vol_mode = regs [0x08 + index]; - int const vol_mode_mask = type_ == Ay8914 ? 0x30 : 0x10; - int volume = amp_table [vol_mode & 0x0F] >> (half_vol + env_step_scale); - int osc_env_pos = env_pos; - if ( vol_mode & vol_mode_mask ) - { - volume = env_wave [osc_env_pos] >> (half_vol + env_step_scale); - if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); - // use envelope only if it's a repeating wave or a ramp that hasn't finished - if ( !(regs [13] & 1) || osc_env_pos < -32 ) - { - end_time = start_time + env_delay; - if ( end_time >= final_end_time ) - end_time = final_end_time; - - //if ( !(regs [12] | regs [11]) ) - // dprintf( "Used envelope period 0\n" ); - } - else if ( !volume ) - { - osc_mode = noise_off | tone_off; - } - } - else if ( !volume ) - { - osc_mode = noise_off | tone_off; - } - - // tone time - blip_time_t const period = osc->period; - blip_time_t time = start_time + osc->delay; - if ( osc_mode & tone_off ) // maintain tone's phase when off - { - int count = (final_end_time - time + period - 1) / period; - time += count * period; - osc->phase ^= count & 1; - } - - // noise time - blip_time_t ntime = final_end_time; - unsigned noise_lfsr = 1; - if ( !(osc_mode & noise_off) ) - { - ntime = start_time + old_noise_delay; - noise_lfsr = old_noise_lfsr; - //if ( (regs [6] & 0x1F) == 0 ) - // dprintf( "Used noise period 0\n" ); - } - - // The following efficiently handles several cases (least demanding first): - // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC - // * Just tone or just noise, envelope disabled - // * Envelope controlling tone and/or noise - // * Tone and noise disabled, envelope enabled with high frequency - // * Tone and noise together - // * Tone and noise together with envelope - - // This loop only runs one iteration if envelope is disabled. If envelope - // is being used as a waveform (tone and noise disabled), this loop will - // still be reasonably efficient since the bulk of it will be skipped. - while ( 1 ) - { - // current amplitude - int amp = 0; - if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) ) - amp = volume; - { - int delta = amp - osc->last_amp; - if ( delta ) - { - osc->last_amp = amp; - synth_.offset( start_time, delta, osc_output ); - } - } - - // Run wave and noise interleved with each catching up to the other. - // If one or both are disabled, their "current time" will be past end time, - // so there will be no significant performance hit. - if ( ntime < end_time || time < end_time ) - { - // Since amplitude was updated above, delta will always be +/- volume, - // so we can avoid using last_amp every time to calculate the delta. - int delta = amp * 2 - volume; - int delta_non_zero = delta != 0; - int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 ); - do - { - // run noise - blip_time_t end = end_time; - if ( end_time > time ) end = time; - if ( phase & delta_non_zero ) - { - while ( ntime <= end ) // must advance *past* time to avoid hang - { - int changed = noise_lfsr + 1; - noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); - if ( changed & 2 ) - { - delta = -delta; - synth_.offset( ntime, delta, osc_output ); - } - ntime += noise_period; - } - } - else - { - // 20 or more noise periods on average for some music - int remain = end - ntime; - int count = remain / noise_period; - if ( remain >= 0 ) - ntime += noise_period + count * noise_period; - } - - // run tone - end = end_time; - if ( end_time > ntime ) end = ntime; - if ( noise_lfsr & delta_non_zero ) - { - while ( time < end ) - { - delta = -delta; - synth_.offset( time, delta, osc_output ); - time += period; - - // alternate (less-efficient) implementation - //phase ^= 1; - } - phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); - check( phase == (delta > 0) ); - } - else - { - // loop usually runs less than once - //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period ); - - while ( time < end ) - { - time += period; - phase ^= 1; - } - } - } - while ( time < end_time || ntime < end_time ); - - osc->last_amp = (delta + volume) >> 1; - if ( !(osc_mode & tone_off) ) - osc->phase = phase; - } - - if ( end_time >= final_end_time ) - break; // breaks first time when envelope is disabled - - // next envelope step - if ( ++osc_env_pos >= 0 ) - osc_env_pos -= 32; - volume = env_wave [osc_env_pos] >> (half_vol + env_step_scale); - if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); - - start_time = end_time; - end_time += env_period; - if ( end_time > final_end_time ) - end_time = final_end_time; - } - osc->delay = time - final_end_time; - - if ( !(osc_mode & noise_off) ) - { - noise_delay = ntime - final_end_time; - this->noise_lfsr = noise_lfsr; - } - } - - // TODO: optimized saw wave envelope? - - // maintain envelope phase - blip_time_t remain = final_end_time - last_time - env_delay; - if ( remain >= 0 ) - { - int count = (remain + env_period) / env_period; - env_pos += count; - if ( env_pos >= 0 ) - env_pos = (env_pos & 31) - 32; - remain -= count * env_period; - assert( -remain <= env_period ); - } - env_delay = -remain; - assert( env_delay > 0 ); - assert( env_pos < 0 ); - - last_time = final_end_time; -} +// $package. http://www.slack.net/~ant/ + +#include "Ay_Apu.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Emulation inaccuracies: +// * Noise isn't run when not in use +// * Changes to envelope and noise periods are delayed until next reload +// * Super-sonic tone should attenuate output to about 60%, not 50% + +// Tones above this frequency are treated as disabled tone at half volume. +// Power of two is more efficient (avoids division). +int const inaudible_freq = 16384; + +int const period_factor = 16; + +static byte const amp_table [16] = +{ +#define ENTRY( n ) byte (n * Ay_Apu::amp_range + 0.5) + // With channels tied together and 1K resistor to ground (as datasheet recommends), + // output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step. + ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625), + ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500), + ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000), + ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000), + + /* + // Measured from an AY-3-8910A chip with date code 8611. + + // Direct voltages without any load (very linear) + ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785), + ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032), + ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043), + ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000), + // With only some load + ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876), + ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388), + ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945), + ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000), + */ +#undef ENTRY +}; + +static byte const modes [8] = +{ +#define MODE( a0,a1, b0,b1, c0,c1 ) \ + (a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5) + MODE( 1,0, 1,0, 1,0 ), + MODE( 1,0, 0,0, 0,0 ), + MODE( 1,0, 0,1, 1,0 ), + MODE( 1,0, 1,1, 1,1 ), + MODE( 0,1, 0,1, 0,1 ), + MODE( 0,1, 1,1, 1,1 ), + MODE( 0,1, 1,0, 0,1 ), + MODE( 0,1, 0,0, 0,0 ), +}; + +void Ay_Apu::set_output( Blip_Buffer* b ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, b ); +} + +Ay_Apu::Ay_Apu() +{ + // build full table of the upper 8 envelope waveforms + for ( int m = 8; m--; ) + { + byte* out = env_modes [m]; + int flags = modes [m]; + for ( int x = 3; --x >= 0; ) + { + int amp = flags & 1; + int end = flags >> 1 & 1; + int step = end - amp; + amp *= 15; + for ( int y = 16; --y >= 0; ) + { + *out++ = amp_table [amp]; + amp += step; + } + flags >>= 2; + } + } + + type_ = Ay8910; + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +void Ay_Apu::reset() +{ + addr_ = 0; + last_time = 0; + noise_delay = 0; + noise_lfsr = 1; + + for ( osc_t* osc = &oscs [osc_count]; osc != oscs; ) + { + osc--; + osc->period = period_factor; + osc->delay = 0; + osc->last_amp = 0; + osc->phase = 0; + } + + for ( int i = sizeof regs; --i >= 0; ) + regs [i] = 0; + regs [7] = 0xFF; + write_data_( 13, 0 ); +} + +int Ay_Apu::read() +{ + static byte const masks [reg_count] = { + 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F, + 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00 + }; + if (!(type_ & 0x10)) return regs [addr_] & masks [addr_]; + else return regs [addr_]; +} + +void Ay_Apu::write_data_( int addr, int data ) +{ + assert( (unsigned) addr < reg_count ); + + if ( (unsigned) addr >= 14 ) + dprintf( "Wrote to I/O port %02X\n", (int) addr ); + + // envelope mode + if ( addr == 13 ) + { + if ( !(data & 8) ) // convert modes 0-7 to proper equivalents + data = (data & 4) ? 15 : 9; + env_wave = env_modes [data - 7]; + env_pos = -48; + env_delay = 0; // will get set to envelope period in run_until() + } + regs [addr] = data; + + // handle period changes accurately + int i = addr >> 1; + if ( i < osc_count ) + { + blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) + + regs [i * 2] * period_factor; + if ( !period ) + period = period_factor; + + // adjust time of next timer expiration based on change in period + osc_t& osc = oscs [i]; + if ( (osc.delay += period - osc.period) < 0 ) + osc.delay = 0; + osc.period = period; + } + + // TODO: same as above for envelope timer, and it also has a divide by two after it +} + +int const noise_off = 0x08; +int const tone_off = 0x01; + +void Ay_Apu::run_until( blip_time_t final_end_time ) +{ + require( final_end_time >= last_time ); + + // noise period and initial values + blip_time_t const noise_period_factor = period_factor * 2; // verified + blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor; + if ( !noise_period ) + noise_period = noise_period_factor; + blip_time_t const old_noise_delay = noise_delay; + unsigned const old_noise_lfsr = noise_lfsr; + + // envelope period + int env_step_scale = ((type_ & 0xF0) == 0x00) ? 1 : 0; + blip_time_t const env_period_factor = period_factor << env_step_scale; // verified + blip_time_t env_period = (regs [12] * 0x100 + regs [11]) * env_period_factor; + if ( !env_period ) + env_period = env_period_factor; // same as period 1 on my AY chip + if ( !env_delay ) + env_delay = env_period; + + // run each osc separately + for ( int index = 0; index < osc_count; index++ ) + { + osc_t* const osc = &oscs [index]; + int osc_mode = regs [7] >> index; + + // output + Blip_Buffer* const osc_output = osc->output; + if ( !osc_output ) + continue; + osc_output->set_modified(); + + // period + int half_vol = 0; + blip_time_t inaudible_period = (unsigned) (osc_output->clock_rate() + + inaudible_freq) / (unsigned) (inaudible_freq * 2); + if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) + { + half_vol = 1; // Actually around 60%, but 50% is close enough + osc_mode |= tone_off; + } + + // envelope + blip_time_t start_time = last_time; + blip_time_t end_time = final_end_time; + int const vol_mode = regs [0x08 + index]; + int const vol_mode_mask = type_ == Ay8914 ? 0x30 : 0x10; + int volume = amp_table [vol_mode & 0x0F] >> (half_vol + env_step_scale); + int osc_env_pos = env_pos; + if ( vol_mode & vol_mode_mask ) + { + volume = env_wave [osc_env_pos] >> (half_vol + env_step_scale); + if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); + // use envelope only if it's a repeating wave or a ramp that hasn't finished + if ( !(regs [13] & 1) || osc_env_pos < -32 ) + { + end_time = start_time + env_delay; + if ( end_time >= final_end_time ) + end_time = final_end_time; + + //if ( !(regs [12] | regs [11]) ) + // dprintf( "Used envelope period 0\n" ); + } + else if ( !volume ) + { + osc_mode = noise_off | tone_off; + } + } + else if ( !volume ) + { + osc_mode = noise_off | tone_off; + } + + // tone time + blip_time_t const period = osc->period; + blip_time_t time = start_time + osc->delay; + if ( osc_mode & tone_off ) // maintain tone's phase when off + { + int count = (final_end_time - time + period - 1) / period; + time += count * period; + osc->phase ^= count & 1; + } + + // noise time + blip_time_t ntime = final_end_time; + unsigned noise_lfsr = 1; + if ( !(osc_mode & noise_off) ) + { + ntime = start_time + old_noise_delay; + noise_lfsr = old_noise_lfsr; + //if ( (regs [6] & 0x1F) == 0 ) + // dprintf( "Used noise period 0\n" ); + } + + // The following efficiently handles several cases (least demanding first): + // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC + // * Just tone or just noise, envelope disabled + // * Envelope controlling tone and/or noise + // * Tone and noise disabled, envelope enabled with high frequency + // * Tone and noise together + // * Tone and noise together with envelope + + // This loop only runs one iteration if envelope is disabled. If envelope + // is being used as a waveform (tone and noise disabled), this loop will + // still be reasonably efficient since the bulk of it will be skipped. + while ( 1 ) + { + // current amplitude + int amp = 0; + if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) ) + amp = volume; + { + int delta = amp - osc->last_amp; + if ( delta ) + { + osc->last_amp = amp; + synth_.offset( start_time, delta, osc_output ); + } + } + + // Run wave and noise interleved with each catching up to the other. + // If one or both are disabled, their "current time" will be past end time, + // so there will be no significant performance hit. + if ( ntime < end_time || time < end_time ) + { + // Since amplitude was updated above, delta will always be +/- volume, + // so we can avoid using last_amp every time to calculate the delta. + int delta = amp * 2 - volume; + int delta_non_zero = delta != 0; + int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 ); + do + { + // run noise + blip_time_t end = end_time; + if ( end_time > time ) end = time; + if ( phase & delta_non_zero ) + { + while ( ntime <= end ) // must advance *past* time to avoid hang + { + int changed = noise_lfsr + 1; + noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); + if ( changed & 2 ) + { + delta = -delta; + synth_.offset( ntime, delta, osc_output ); + } + ntime += noise_period; + } + } + else + { + // 20 or more noise periods on average for some music + int remain = end - ntime; + int count = remain / noise_period; + if ( remain >= 0 ) + ntime += noise_period + count * noise_period; + } + + // run tone + end = end_time; + if ( end_time > ntime ) end = ntime; + if ( noise_lfsr & delta_non_zero ) + { + while ( time < end ) + { + delta = -delta; + synth_.offset( time, delta, osc_output ); + time += period; + + // alternate (less-efficient) implementation + //phase ^= 1; + } + phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); + check( phase == (delta > 0) ); + } + else + { + // loop usually runs less than once + //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period ); + + while ( time < end ) + { + time += period; + phase ^= 1; + } + } + } + while ( time < end_time || ntime < end_time ); + + osc->last_amp = (delta + volume) >> 1; + if ( !(osc_mode & tone_off) ) + osc->phase = phase; + } + + if ( end_time >= final_end_time ) + break; // breaks first time when envelope is disabled + + // next envelope step + if ( ++osc_env_pos >= 0 ) + osc_env_pos -= 32; + volume = env_wave [osc_env_pos] >> (half_vol + env_step_scale); + if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); + + start_time = end_time; + end_time += env_period; + if ( end_time > final_end_time ) + end_time = final_end_time; + } + osc->delay = time - final_end_time; + + if ( !(osc_mode & noise_off) ) + { + noise_delay = ntime - final_end_time; + this->noise_lfsr = noise_lfsr; + } + } + + // TODO: optimized saw wave envelope? + + // maintain envelope phase + blip_time_t remain = final_end_time - last_time - env_delay; + if ( remain >= 0 ) + { + int count = (remain + env_period) / env_period; + env_pos += count; + if ( env_pos >= 0 ) + env_pos = (env_pos & 31) - 32; + remain -= count * env_period; + assert( -remain <= env_period ); + } + env_delay = -remain; + assert( env_delay > 0 ); + assert( env_pos < 0 ); + + last_time = final_end_time; +} diff --git a/Frameworks/GME/gme/Ay_Apu.h b/Frameworks/GME/gme/Ay_Apu.h index 314772f31..ddeea19bc 100644 --- a/Frameworks/GME/gme/Ay_Apu.h +++ b/Frameworks/GME/gme/Ay_Apu.h @@ -1,123 +1,123 @@ -// AY-3-8910 sound chip emulator - -// $package -#ifndef AY_APU_H -#define AY_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Ay_Apu { -public: -// Basics - enum Ay_Apu_Type - { - Ay8910 = 0, - Ay8912, - Ay8913, - Ay8914, - Ym2149 = 0x10, - Ym3439, - Ymz284, - Ymz294, - Ym2203 = 0x20, - Ym2608, - Ym2610, - Ym2610b - }; - - void set_type( Ay_Apu_Type type ) { type_ = type; } - - // Sets buffer to generate sound into, or 0 to mute. - void set_output( Blip_Buffer* ); - - // Writes to address register - void write_addr( int data ) { addr_ = data & 0x0F; } - - // Emulates to time t, then writes to current data register - void write_data( blip_time_t t, int data ) { run_until( t ); write_data_( addr_, data ); } - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Reads from current data register - int read(); - - // Resets sound chip - void reset(); - - // Number of registers - enum { reg_count = 16 }; - - // Same as set_output(), but for a particular channel - enum { osc_count = 3 }; - void set_output( int chan, Blip_Buffer* ); - - // Sets overall volume, where 1.0 is normal - void volume( double v ) { synth_.volume( 0.7/osc_count/amp_range * v ); } - - // Sets treble equalization - void treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } - -private: - // noncopyable - Ay_Apu( const Ay_Apu& ); - Ay_Apu& operator = ( const Ay_Apu& ); - -// Implementation -public: - Ay_Apu(); - BLARGG_DISABLE_NOTHROW - typedef BOOST::uint8_t byte; - -private: - struct osc_t - { - blip_time_t period; - blip_time_t delay; - short last_amp; - short phase; - Blip_Buffer* output; - } oscs [osc_count]; - - Ay_Apu_Type type_; - - blip_time_t last_time; - byte addr_; - byte regs [reg_count]; - - blip_time_t noise_delay; - unsigned noise_lfsr; - - blip_time_t env_delay; - byte const* env_wave; - int env_pos; - byte env_modes [8] [48]; // values already passed through volume table - - void write_data_( int addr, int data ); - void run_until( blip_time_t ); - -public: - enum { amp_range = 255 }; - Blip_Synth_Norm synth_; // used by Ay_Core for beeper sound -}; - -inline void Ay_Apu::set_output( int i, Blip_Buffer* out ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = out; -} - -inline void Ay_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - last_time -= time; - assert( last_time >= 0 ); -} - -#endif +// AY-3-8910 sound chip emulator + +// $package +#ifndef AY_APU_H +#define AY_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Ay_Apu { +public: +// Basics + enum Ay_Apu_Type + { + Ay8910 = 0, + Ay8912, + Ay8913, + Ay8914, + Ym2149 = 0x10, + Ym3439, + Ymz284, + Ymz294, + Ym2203 = 0x20, + Ym2608, + Ym2610, + Ym2610b + }; + + void set_type( Ay_Apu_Type type ) { type_ = type; } + + // Sets buffer to generate sound into, or 0 to mute. + void set_output( Blip_Buffer* ); + + // Writes to address register + void write_addr( int data ) { addr_ = data & 0x0F; } + + // Emulates to time t, then writes to current data register + void write_data( blip_time_t t, int data ) { run_until( t ); write_data_( addr_, data ); } + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Reads from current data register + int read(); + + // Resets sound chip + void reset(); + + // Number of registers + enum { reg_count = 16 }; + + // Same as set_output(), but for a particular channel + enum { osc_count = 3 }; + void set_output( int chan, Blip_Buffer* ); + + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth_.volume( 0.7/osc_count/amp_range * v ); } + + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } + +private: + // noncopyable + Ay_Apu( const Ay_Apu& ); + Ay_Apu& operator = ( const Ay_Apu& ); + +// Implementation +public: + Ay_Apu(); + BLARGG_DISABLE_NOTHROW + typedef BOOST::uint8_t byte; + +private: + struct osc_t + { + blip_time_t period; + blip_time_t delay; + short last_amp; + short phase; + Blip_Buffer* output; + } oscs [osc_count]; + + Ay_Apu_Type type_; + + blip_time_t last_time; + byte addr_; + byte regs [reg_count]; + + blip_time_t noise_delay; + unsigned noise_lfsr; + + blip_time_t env_delay; + byte const* env_wave; + int env_pos; + byte env_modes [8] [48]; // values already passed through volume table + + void write_data_( int addr, int data ); + void run_until( blip_time_t ); + +public: + enum { amp_range = 255 }; + Blip_Synth_Norm synth_; // used by Ay_Core for beeper sound +}; + +inline void Ay_Apu::set_output( int i, Blip_Buffer* out ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = out; +} + +inline void Ay_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + last_time -= time; + assert( last_time >= 0 ); +} + +#endif diff --git a/Frameworks/GME/gme/Ay_Core.cpp b/Frameworks/GME/gme/Ay_Core.cpp index c54a73e9a..09a24db4e 100644 --- a/Frameworks/GME/gme/Ay_Core.cpp +++ b/Frameworks/GME/gme/Ay_Core.cpp @@ -1,190 +1,190 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ay_Core.h" - -/* Copyright (C) 2006-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -inline void Ay_Core::disable_beeper() -{ - beeper_mask = 0; - last_beeper = 0; -} - -Ay_Core::Ay_Core() -{ - beeper_output = NULL; - disable_beeper(); -} - -Ay_Core::~Ay_Core() { } - -void Ay_Core::set_beeper_output( Blip_Buffer* b ) -{ - beeper_output = b; - if ( b && !cpc_mode ) - beeper_mask = 0x10; - else - disable_beeper(); -} - -void Ay_Core::start_track( registers_t const& r, addr_t play ) -{ - play_addr = play; - - memset( mem_.padding1, 0xFF, sizeof mem_.padding1 ); - - int const mirrored = 0x80; // this much is mirrored after end of memory - memset( mem_.ram + mem_size + mirrored, 0xFF, sizeof mem_.ram - mem_size - mirrored ); - memcpy( mem_.ram + mem_size, mem_.ram, mirrored ); // some code wraps around (ugh) - - cpu.reset( mem_.padding1, mem_.padding1 ); - cpu.map_mem( 0, mem_size, mem_.ram, mem_.ram ); - cpu.r = r; - - beeper_delta = (int) (apu_.amp_range * 0.8); - last_beeper = 0; - next_play = play_period; - spectrum_mode = false; - cpc_mode = false; - cpc_latch = 0; - set_beeper_output( beeper_output ); - apu_.reset(); - - // a few tunes rely on channels having tone enabled at the beginning - apu_.write_addr( 7 ); - apu_.write_data( 0, 0x38 ); - -} - -// Emulation - -void Ay_Core::cpu_out_( time_t time, addr_t addr, int data ) -{ - // Spectrum - if ( !cpc_mode ) - { - switch ( addr & 0xFEFF ) - { - case 0xFEFD: - spectrum_mode = true; - apu_.write_addr( data ); - return; - - case 0xBEFD: - spectrum_mode = true; - apu_.write_data( time, data ); - return; - } - } - - // CPC - if ( !spectrum_mode ) - { - switch ( addr >> 8 ) - { - case 0xF6: - switch ( data & 0xC0 ) - { - case 0xC0: - apu_.write_addr( cpc_latch ); - goto enable_cpc; - - case 0x80: - apu_.write_data( time, cpc_latch ); - goto enable_cpc; - } - break; - - case 0xF4: - cpc_latch = data; - goto enable_cpc; - } - } - - dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); - return; - -enable_cpc: - if ( !cpc_mode ) - { - cpc_mode = true; - disable_beeper(); - set_cpc_callback.f( set_cpc_callback.data ); - } -} - -int Ay_Core::cpu_in( addr_t addr ) -{ - // keyboard read and other things - if ( (addr & 0xFF) == 0xFE ) - return 0xFF; // other values break some beeper tunes - - dprintf( "Unmapped IN : $%04X\n", addr ); - return 0xFF; -} - -void Ay_Core::end_frame( time_t* end ) -{ - cpu.set_time( 0 ); - - // Since detection of CPC mode will halve clock rate during the frame - // and thus generate up to twice as much sound, we must generate half - // as much until mode is known. - if ( !(spectrum_mode | cpc_mode) ) - *end /= 2; - - while ( cpu.time() < *end ) - { - run_cpu( min( *end, next_play ) ); - - if ( cpu.time() >= next_play ) - { - // next frame - next_play += play_period; - - if ( cpu.r.iff1 ) - { - // interrupt enabled - - if ( mem_.ram [cpu.r.pc] == 0x76 ) - cpu.r.pc++; // advance past HALT instruction - - cpu.r.iff1 = 0; - cpu.r.iff2 = 0; - - mem_.ram [--cpu.r.sp] = byte (cpu.r.pc >> 8); - mem_.ram [--cpu.r.sp] = byte (cpu.r.pc); - - // fixed interrupt - cpu.r.pc = 0x38; - cpu.adjust_time( 12 ); - - if ( cpu.r.im == 2 ) - { - // vectored interrupt - addr_t addr = cpu.r.i * 0x100 + 0xFF; - cpu.r.pc = mem_.ram [(addr + 1) & 0xFFFF] * 0x100 + mem_.ram [addr]; - cpu.adjust_time( 6 ); - } - } - } - } - - // End time frame - *end = cpu.time(); - next_play -= *end; - check( next_play >= 0 ); - cpu.adjust_time( -*end ); - apu_.end_frame( *end ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Ay_Core.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +inline void Ay_Core::disable_beeper() +{ + beeper_mask = 0; + last_beeper = 0; +} + +Ay_Core::Ay_Core() +{ + beeper_output = NULL; + disable_beeper(); +} + +Ay_Core::~Ay_Core() { } + +void Ay_Core::set_beeper_output( Blip_Buffer* b ) +{ + beeper_output = b; + if ( b && !cpc_mode ) + beeper_mask = 0x10; + else + disable_beeper(); +} + +void Ay_Core::start_track( registers_t const& r, addr_t play ) +{ + play_addr = play; + + memset( mem_.padding1, 0xFF, sizeof mem_.padding1 ); + + int const mirrored = 0x80; // this much is mirrored after end of memory + memset( mem_.ram + mem_size + mirrored, 0xFF, sizeof mem_.ram - mem_size - mirrored ); + memcpy( mem_.ram + mem_size, mem_.ram, mirrored ); // some code wraps around (ugh) + + cpu.reset( mem_.padding1, mem_.padding1 ); + cpu.map_mem( 0, mem_size, mem_.ram, mem_.ram ); + cpu.r = r; + + beeper_delta = (int) (apu_.amp_range * 0.8); + last_beeper = 0; + next_play = play_period; + spectrum_mode = false; + cpc_mode = false; + cpc_latch = 0; + set_beeper_output( beeper_output ); + apu_.reset(); + + // a few tunes rely on channels having tone enabled at the beginning + apu_.write_addr( 7 ); + apu_.write_data( 0, 0x38 ); + +} + +// Emulation + +void Ay_Core::cpu_out_( time_t time, addr_t addr, int data ) +{ + // Spectrum + if ( !cpc_mode ) + { + switch ( addr & 0xFEFF ) + { + case 0xFEFD: + spectrum_mode = true; + apu_.write_addr( data ); + return; + + case 0xBEFD: + spectrum_mode = true; + apu_.write_data( time, data ); + return; + } + } + + // CPC + if ( !spectrum_mode ) + { + switch ( addr >> 8 ) + { + case 0xF6: + switch ( data & 0xC0 ) + { + case 0xC0: + apu_.write_addr( cpc_latch ); + goto enable_cpc; + + case 0x80: + apu_.write_data( time, cpc_latch ); + goto enable_cpc; + } + break; + + case 0xF4: + cpc_latch = data; + goto enable_cpc; + } + } + + dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); + return; + +enable_cpc: + if ( !cpc_mode ) + { + cpc_mode = true; + disable_beeper(); + set_cpc_callback.f( set_cpc_callback.data ); + } +} + +int Ay_Core::cpu_in( addr_t addr ) +{ + // keyboard read and other things + if ( (addr & 0xFF) == 0xFE ) + return 0xFF; // other values break some beeper tunes + + dprintf( "Unmapped IN : $%04X\n", addr ); + return 0xFF; +} + +void Ay_Core::end_frame( time_t* end ) +{ + cpu.set_time( 0 ); + + // Since detection of CPC mode will halve clock rate during the frame + // and thus generate up to twice as much sound, we must generate half + // as much until mode is known. + if ( !(spectrum_mode | cpc_mode) ) + *end /= 2; + + while ( cpu.time() < *end ) + { + run_cpu( min( *end, next_play ) ); + + if ( cpu.time() >= next_play ) + { + // next frame + next_play += play_period; + + if ( cpu.r.iff1 ) + { + // interrupt enabled + + if ( mem_.ram [cpu.r.pc] == 0x76 ) + cpu.r.pc++; // advance past HALT instruction + + cpu.r.iff1 = 0; + cpu.r.iff2 = 0; + + mem_.ram [--cpu.r.sp] = byte (cpu.r.pc >> 8); + mem_.ram [--cpu.r.sp] = byte (cpu.r.pc); + + // fixed interrupt + cpu.r.pc = 0x38; + cpu.adjust_time( 12 ); + + if ( cpu.r.im == 2 ) + { + // vectored interrupt + addr_t addr = cpu.r.i * 0x100 + 0xFF; + cpu.r.pc = mem_.ram [(addr + 1) & 0xFFFF] * 0x100 + mem_.ram [addr]; + cpu.adjust_time( 6 ); + } + } + } + } + + // End time frame + *end = cpu.time(); + next_play -= *end; + check( next_play >= 0 ); + cpu.adjust_time( -*end ); + apu_.end_frame( *end ); +} diff --git a/Frameworks/GME/gme/Ay_Core.h b/Frameworks/GME/gme/Ay_Core.h index 96c57c0e0..f7c9acc51 100644 --- a/Frameworks/GME/gme/Ay_Core.h +++ b/Frameworks/GME/gme/Ay_Core.h @@ -1,81 +1,81 @@ -// Sinclair Spectrum AY music emulator core - -// Game_Music_Emu $vers -#ifndef AY_CORE_H -#define AY_CORE_H - -#include "Z80_Cpu.h" -#include "Ay_Apu.h" - -class Ay_Core { -public: - - // Clock count - typedef int time_t; - - // Sound chip access, to assign it to Blip_Buffer etc. - Ay_Apu& apu() { return apu_; } - - // Sets beeper sound buffer, or NULL to mute it. Volume and treble EQ of - // beeper are set by APU. - void set_beeper_output( Blip_Buffer* ); - - // Sets time between calls to play routine. Can be changed while playing. - void set_play_period( time_t p ) { play_period = p; } - - // 64K memory to load code and data into before starting track. Caller - // must parse the AY file. - BOOST::uint8_t* mem() { return mem_.ram; } - enum { mem_size = 0x10000 }; - enum { ram_addr = 0x4000 }; // where official RAM starts - - // Starts track using specified register values, and sets play routine that - // is called periodically - typedef Z80_Cpu::registers_t registers_t; - typedef int addr_t; - void start_track( registers_t const&, addr_t play ); - - // Ends time frame of at most *end clocks and sets *end to number of clocks - // emulated. Until Spectrum/CPC mode is determined, *end is HALVED. - void end_frame( time_t* end ); - - // Called when CPC hardware is first accessed. AY file format doesn't specify - // which sound hardware is used, so it must be determined during playback - // based on which sound port is first used. - blargg_callback set_cpc_callback; - -// Implementation -public: - Ay_Core(); - ~Ay_Core(); - -private: - Blip_Buffer* beeper_output; - int beeper_delta; - int last_beeper; - int beeper_mask; - - addr_t play_addr; - time_t play_period; - time_t next_play; - - int cpc_latch; - bool spectrum_mode; - bool cpc_mode; - - // large items - Z80_Cpu cpu; - struct { - BOOST::uint8_t padding1 [0x100]; - BOOST::uint8_t ram [mem_size + 0x100]; - } mem_; - Ay_Apu apu_; - - int cpu_in( addr_t ); - void cpu_out( time_t, addr_t, int data ); - void cpu_out_( time_t, addr_t, int data ); - bool run_cpu( time_t end ); - void disable_beeper(); -}; - -#endif +// Sinclair Spectrum AY music emulator core + +// Game_Music_Emu $vers +#ifndef AY_CORE_H +#define AY_CORE_H + +#include "Z80_Cpu.h" +#include "Ay_Apu.h" + +class Ay_Core { +public: + + // Clock count + typedef int time_t; + + // Sound chip access, to assign it to Blip_Buffer etc. + Ay_Apu& apu() { return apu_; } + + // Sets beeper sound buffer, or NULL to mute it. Volume and treble EQ of + // beeper are set by APU. + void set_beeper_output( Blip_Buffer* ); + + // Sets time between calls to play routine. Can be changed while playing. + void set_play_period( time_t p ) { play_period = p; } + + // 64K memory to load code and data into before starting track. Caller + // must parse the AY file. + BOOST::uint8_t* mem() { return mem_.ram; } + enum { mem_size = 0x10000 }; + enum { ram_addr = 0x4000 }; // where official RAM starts + + // Starts track using specified register values, and sets play routine that + // is called periodically + typedef Z80_Cpu::registers_t registers_t; + typedef int addr_t; + void start_track( registers_t const&, addr_t play ); + + // Ends time frame of at most *end clocks and sets *end to number of clocks + // emulated. Until Spectrum/CPC mode is determined, *end is HALVED. + void end_frame( time_t* end ); + + // Called when CPC hardware is first accessed. AY file format doesn't specify + // which sound hardware is used, so it must be determined during playback + // based on which sound port is first used. + blargg_callback set_cpc_callback; + +// Implementation +public: + Ay_Core(); + ~Ay_Core(); + +private: + Blip_Buffer* beeper_output; + int beeper_delta; + int last_beeper; + int beeper_mask; + + addr_t play_addr; + time_t play_period; + time_t next_play; + + int cpc_latch; + bool spectrum_mode; + bool cpc_mode; + + // large items + Z80_Cpu cpu; + struct { + BOOST::uint8_t padding1 [0x100]; + BOOST::uint8_t ram [mem_size + 0x100]; + } mem_; + Ay_Apu apu_; + + int cpu_in( addr_t ); + void cpu_out( time_t, addr_t, int data ); + void cpu_out_( time_t, addr_t, int data ); + bool run_cpu( time_t end ); + void disable_beeper(); +}; + +#endif diff --git a/Frameworks/GME/gme/Ay_Cpu.cpp b/Frameworks/GME/gme/Ay_Cpu.cpp index 40fbff6f6..632cd60b3 100644 --- a/Frameworks/GME/gme/Ay_Cpu.cpp +++ b/Frameworks/GME/gme/Ay_Cpu.cpp @@ -1,59 +1,59 @@ -// $package. http://www.slack.net/~ant/ - -#include "Ay_Core.h" - -#include "blargg_endian.h" -//#include "z80_cpu_log.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void Ay_Core::cpu_out( time_t time, addr_t addr, int data ) -{ - if ( (addr & 0xFF) == 0xFE ) - { - check( !cpc_mode ); - spectrum_mode = !cpc_mode; - - // beeper_mask and last_beeper are 0 if (cpc_mode || !beeper_output) - if ( (data &= beeper_mask) != last_beeper ) - { - last_beeper = data; - int delta = -beeper_delta; - beeper_delta = delta; - Blip_Buffer* bb = beeper_output; - bb->set_modified(); - apu_.synth_.offset( time, delta, bb ); - } - } - else - { - cpu_out_( time, addr, data ); - } -} - -#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data ) -#define IN_PORT( addr ) cpu_in( addr ) -#define FLAT_MEM mem -#define CPU cpu - -#define CPU_BEGIN \ -bool Ay_Core::run_cpu( time_t end_time ) \ -{\ - cpu.set_end_time( end_time );\ - byte* const mem = mem_.ram; // cache - - #include "Z80_Cpu_run.h" - - return warning; -} +// $package. http://www.slack.net/~ant/ + +#include "Ay_Core.h" + +#include "blargg_endian.h" +//#include "z80_cpu_log.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Ay_Core::cpu_out( time_t time, addr_t addr, int data ) +{ + if ( (addr & 0xFF) == 0xFE ) + { + check( !cpc_mode ); + spectrum_mode = !cpc_mode; + + // beeper_mask and last_beeper are 0 if (cpc_mode || !beeper_output) + if ( (data &= beeper_mask) != last_beeper ) + { + last_beeper = data; + int delta = -beeper_delta; + beeper_delta = delta; + Blip_Buffer* bb = beeper_output; + bb->set_modified(); + apu_.synth_.offset( time, delta, bb ); + } + } + else + { + cpu_out_( time, addr, data ); + } +} + +#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data ) +#define IN_PORT( addr ) cpu_in( addr ) +#define FLAT_MEM mem +#define CPU cpu + +#define CPU_BEGIN \ +bool Ay_Core::run_cpu( time_t end_time ) \ +{\ + cpu.set_end_time( end_time );\ + byte* const mem = mem_.ram; // cache + + #include "Z80_Cpu_run.h" + + return warning; +} diff --git a/Frameworks/GME/gme/Ay_Emu.h b/Frameworks/GME/gme/Ay_Emu.h index 7da69a57e..0959d6801 100644 --- a/Frameworks/GME/gme/Ay_Emu.h +++ b/Frameworks/GME/gme/Ay_Emu.h @@ -1,60 +1,60 @@ -// Sinclair Spectrum AY music file emulator - -// Game_Music_Emu $vers -#ifndef AY_EMU_H -#define AY_EMU_H - -#include "Classic_Emu.h" -#include "Ay_Core.h" - -class Ay_Emu : public Classic_Emu { -public: - // AY file header - struct header_t - { - enum { size = 0x14 }; - - byte tag [8]; - byte vers; - byte player; - byte unused [2]; - byte author [2]; - byte comment [2]; - byte max_track; - byte first_track; - byte track_info [2]; - }; - - static gme_type_t static_type() { return gme_ay_type; } - -// Implementation -public: - Ay_Emu(); - ~Ay_Emu(); - - struct file_t { - header_t const* header; - byte const* tracks; - byte const* end; // end of file data - }; - - blargg_err_t hash_( Hash_Function& out ) const; - -protected: - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t load_mem_( byte const [], int ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - -private: - file_t file; - Ay_Core core; - - void enable_cpc(); - static void enable_cpc_( void* data ); -}; - -#endif +// Sinclair Spectrum AY music file emulator + +// Game_Music_Emu $vers +#ifndef AY_EMU_H +#define AY_EMU_H + +#include "Classic_Emu.h" +#include "Ay_Core.h" + +class Ay_Emu : public Classic_Emu { +public: + // AY file header + struct header_t + { + enum { size = 0x14 }; + + byte tag [8]; + byte vers; + byte player; + byte unused [2]; + byte author [2]; + byte comment [2]; + byte max_track; + byte first_track; + byte track_info [2]; + }; + + static gme_type_t static_type() { return gme_ay_type; } + +// Implementation +public: + Ay_Emu(); + ~Ay_Emu(); + + struct file_t { + header_t const* header; + byte const* tracks; + byte const* end; // end of file data + }; + + blargg_err_t hash_( Hash_Function& out ) const; + +protected: + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + +private: + file_t file; + Ay_Core core; + + void enable_cpc(); + static void enable_cpc_( void* data ); +}; + +#endif diff --git a/Frameworks/GME/gme/C140_Emu.cpp b/Frameworks/GME/gme/C140_Emu.cpp deleted file mode 100644 index c34c09b6f..000000000 --- a/Frameworks/GME/gme/C140_Emu.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "C140_Emu.h" -#include "c140.h" - -C140_Emu::C140_Emu() { chip = 0; } - -C140_Emu::~C140_Emu() -{ - if ( chip ) device_stop_c140( chip ); -} - -int C140_Emu::set_rate( int type, double sample_rate, double clock_rate ) -{ - if ( chip ) - { - device_stop_c140( chip ); - chip = 0; - } - - chip = device_start_c140( sample_rate, clock_rate, type ); - if ( !chip ) - return 1; - - reset(); - return 0; -} - -void C140_Emu::reset() -{ - device_reset_c140( chip ); - c140_set_mute_mask( chip, 0 ); -} - -void C140_Emu::write( int addr, int data ) -{ - c140_w( chip, addr, data ); -} - -void C140_Emu::write_rom( int size, int start, int length, void * data ) -{ - c140_write_rom( chip, size, start, length, (const UINT8 *) data ); -} - -void C140_Emu::mute_voices( int mask ) -{ - c140_set_mute_mask( chip, mask ); -} - -void C140_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - c140_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/C140_Emu.h b/Frameworks/GME/gme/C140_Emu.h deleted file mode 100644 index 8011e91a6..000000000 --- a/Frameworks/GME/gme/C140_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// C140 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef C140_EMU_H -#define C140_EMU_H - -class C140_Emu { - void* chip; -public: - C140_Emu(); - ~C140_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int type, double sample_rate, double clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 24 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Chip_Resampler.h b/Frameworks/GME/gme/Chip_Resampler.h deleted file mode 100644 index 276a50379..000000000 --- a/Frameworks/GME/gme/Chip_Resampler.h +++ /dev/null @@ -1,147 +0,0 @@ -// Fir_Resampler chip emulator container that mixes into the output buffer - -// Game_Music_Emu $vers -#ifndef CHIP_RESAMPLER_H -#define CHIP_RESAMPLER_H - -#include "blargg_source.h" - -#include "Fir_Resampler.h" -typedef Fir_Resampler_Norm Chip_Resampler_Downsampler; - -int const resampler_extra = 0; //34; - -template -class Chip_Resampler_Emu : public Emu { - int last_time; - short* out; - typedef short dsample_t; - enum { disabled_time = -1 }; - enum { gain_bits = 14 }; - blargg_vector sample_buf; - int sample_buf_size; - int oversamples_per_frame; - int buf_pos; - int buffered; - int resampler_size; - int gain_; - - Chip_Resampler_Downsampler resampler; - - void mix_samples( short * buf, int count ) - { - dsample_t * inptr = sample_buf.begin(); - for ( unsigned i = 0; i < count * 2; i++ ) - { - int sample = inptr[i]; - sample += buf[i]; - if ((short)sample != sample) sample = 0x7FFF ^ (sample >> 31); - buf[i] = sample; - } - } - -public: - Chip_Resampler_Emu() { last_time = disabled_time; out = NULL; } - blargg_err_t setup( double oversample, double rolloff, double gain ) - { - gain_ = (int) ((1 << gain_bits) * gain); - RETURN_ERR( resampler.set_rate( oversample ) ); - return reset_resampler(); - } - - blargg_err_t reset() - { - Emu::reset(); - resampler.clear(); - return blargg_ok; - } - - blargg_err_t reset_resampler() - { - unsigned int pairs; - double rate = resampler.rate(); - if ( rate >= 1.0 ) pairs = 64.0 * rate; - else pairs = 64.0 / rate; - RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) ); - resize( pairs ); - resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2); - RETURN_ERR( resampler.resize_buffer( resampler_size ) ); - return blargg_ok; - } - - void resize( int pairs ) - { - int new_sample_buf_size = pairs * 2; - //new_sample_buf_size = new_sample_buf_size / 4 * 4; // TODO: needed only for 3:2 downsampler - if ( sample_buf_size != new_sample_buf_size ) - { - if ( (unsigned) new_sample_buf_size > sample_buf.size() ) - { - check( false ); - return; - } - sample_buf_size = new_sample_buf_size; - oversamples_per_frame = int (pairs * resampler.rate()) * 2 + 2; - clear(); - } - } - - void clear() - { - buf_pos = buffered = 0; - resampler.clear(); - } - - void enable( bool b = true ) { last_time = b ? 0 : disabled_time; } - bool enabled() const { return last_time != disabled_time; } - void begin_frame( short* buf ) { out = buf; last_time = 0; } - - int run_until( int time ) - { - int count = time - last_time; - while ( count > 0 ) - { - if ( last_time < 0 ) - return false; - last_time = time; - if ( buffered ) - { - int samples_to_copy = buffered; - if ( samples_to_copy > count ) samples_to_copy = count; - memcpy( out, sample_buf.begin(), samples_to_copy * sizeof(short) * 2 ); - memcpy( sample_buf.begin(), sample_buf.begin() + samples_to_copy * 2, ( buffered - samples_to_copy ) * 2 * sizeof(short) ); - buffered -= samples_to_copy; - count -= samples_to_copy; - continue; - } - int sample_count = oversamples_per_frame - resampler.written() + resampler_extra; - memset( resampler.buffer(), 0, sample_count * sizeof(*resampler.buffer()) ); - Emu::run( sample_count >> 1, resampler.buffer() ); - for ( unsigned i = 0; i < sample_count; i++ ) - { - dsample_t * ptr = resampler.buffer() + i; - *ptr = ( *ptr * gain_ ) >> gain_bits; - } - short* p = out; - resampler.write( sample_count ); - sample_count = resampler.read( sample_buf.begin(), count * 2 > sample_buf_size ? sample_buf_size : count * 2 ) >> 1; - if ( sample_count > count ) - { - out += count * Emu::out_chan_count; - mix_samples( p, count ); - memmove( sample_buf.begin(), sample_buf.begin() + count * 2, (sample_count - count) * 2 * sizeof(short) ); - buffered = sample_count - count; - return true; - } - else if (!sample_count) return true; - out += sample_count * Emu::out_chan_count; - mix_samples( p, sample_count ); - count -= sample_count; - } - return true; - } -}; - - - -#endif diff --git a/Frameworks/GME/gme/Classic_Emu.cpp b/Frameworks/GME/gme/Classic_Emu.cpp index f02d79b2a..2e4cc9e8e 100644 --- a/Frameworks/GME/gme/Classic_Emu.cpp +++ b/Frameworks/GME/gme/Classic_Emu.cpp @@ -1,124 +1,124 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Classic_Emu.h" - -#include "Multi_Buffer.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Classic_Emu::Classic_Emu() -{ - buf = NULL; - stereo_buffer = NULL; - voice_types = NULL; - - // avoid inconsistency in our duplicated constants - assert( (int) wave_type == (int) Multi_Buffer::wave_type ); - assert( (int) noise_type == (int) Multi_Buffer::noise_type ); - assert( (int) mixed_type == (int) Multi_Buffer::mixed_type ); -} - -Classic_Emu::~Classic_Emu() -{ - delete stereo_buffer; - delete effects_buffer_; - effects_buffer_ = NULL; -} - -void Classic_Emu::set_equalizer_( equalizer_t const& eq ) -{ - Music_Emu::set_equalizer_( eq ); - update_eq( eq.treble ); - if ( buf ) - buf->bass_freq( (int) equalizer().bass ); -} - -blargg_err_t Classic_Emu::set_sample_rate_( int rate ) -{ - if ( !buf ) - { - if ( !stereo_buffer ) - CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer ); - buf = stereo_buffer; - } - return buf->set_sample_rate( rate, 1000 / 20 ); -} - -void Classic_Emu::mute_voices_( int mask ) -{ - Music_Emu::mute_voices_( mask ); - for ( int i = voice_count(); i--; ) - { - if ( mask & (1 << i) ) - { - set_voice( i, NULL, NULL, NULL ); - } - else - { - Multi_Buffer::channel_t ch = buf->channel( i ); - assert( (ch.center && ch.left && ch.right) || - (!ch.center && !ch.left && !ch.right) ); // all or nothing - set_voice( i, ch.center, ch.left, ch.right ); - } - } -} - -void Classic_Emu::change_clock_rate( int rate ) -{ - clock_rate_ = rate; - buf->clock_rate( rate ); -} - -blargg_err_t Classic_Emu::setup_buffer( int rate ) -{ - change_clock_rate( rate ); - RETURN_ERR( buf->set_channel_count( voice_count(), voice_types ) ); - set_equalizer( equalizer() ); - buf_changed_count = buf->channels_changed_count(); - return blargg_ok; -} - -blargg_err_t Classic_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - buf->clear(); - return blargg_ok; -} - -blargg_err_t Classic_Emu::play_( int count, sample_t out [] ) -{ - // read from buffer, then refill buffer and repeat if necessary - int remain = count; - while ( remain ) - { - buf->disable_immediate_removal(); - remain -= buf->read_samples( &out [count - remain], remain ); - if ( remain ) - { - if ( buf_changed_count != buf->channels_changed_count() ) - { - buf_changed_count = buf->channels_changed_count(); - remute_voices(); - } - - // TODO: use more accurate length calculation - int msec = buf->length(); - blip_time_t clocks_emulated = msec * clock_rate_ / 1000 - 100; - RETURN_ERR( run_clocks( clocks_emulated, msec ) ); - assert( clocks_emulated ); - buf->end_frame( clocks_emulated ); - } - } - return blargg_ok; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Classic_Emu.h" + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Classic_Emu::Classic_Emu() +{ + buf = NULL; + stereo_buffer = NULL; + voice_types = NULL; + + // avoid inconsistency in our duplicated constants + assert( (int) wave_type == (int) Multi_Buffer::wave_type ); + assert( (int) noise_type == (int) Multi_Buffer::noise_type ); + assert( (int) mixed_type == (int) Multi_Buffer::mixed_type ); +} + +Classic_Emu::~Classic_Emu() +{ + delete stereo_buffer; + delete effects_buffer_; + effects_buffer_ = NULL; +} + +void Classic_Emu::set_equalizer_( equalizer_t const& eq ) +{ + Music_Emu::set_equalizer_( eq ); + update_eq( eq.treble ); + if ( buf ) + buf->bass_freq( (int) equalizer().bass ); +} + +blargg_err_t Classic_Emu::set_sample_rate_( int rate ) +{ + if ( !buf ) + { + if ( !stereo_buffer ) + CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer ); + buf = stereo_buffer; + } + return buf->set_sample_rate( rate, 1000 / 20 ); +} + +void Classic_Emu::mute_voices_( int mask ) +{ + Music_Emu::mute_voices_( mask ); + for ( int i = voice_count(); i--; ) + { + if ( mask & (1 << i) ) + { + set_voice( i, NULL, NULL, NULL ); + } + else + { + Multi_Buffer::channel_t ch = buf->channel( i ); + assert( (ch.center && ch.left && ch.right) || + (!ch.center && !ch.left && !ch.right) ); // all or nothing + set_voice( i, ch.center, ch.left, ch.right ); + } + } +} + +void Classic_Emu::change_clock_rate( int rate ) +{ + clock_rate_ = rate; + buf->clock_rate( rate ); +} + +blargg_err_t Classic_Emu::setup_buffer( int rate ) +{ + change_clock_rate( rate ); + RETURN_ERR( buf->set_channel_count( voice_count(), voice_types ) ); + set_equalizer( equalizer() ); + buf_changed_count = buf->channels_changed_count(); + return blargg_ok; +} + +blargg_err_t Classic_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + buf->clear(); + return blargg_ok; +} + +blargg_err_t Classic_Emu::play_( int count, sample_t out [] ) +{ + // read from buffer, then refill buffer and repeat if necessary + int remain = count; + while ( remain ) + { + buf->disable_immediate_removal(); + remain -= buf->read_samples( &out [count - remain], remain ); + if ( remain ) + { + if ( buf_changed_count != buf->channels_changed_count() ) + { + buf_changed_count = buf->channels_changed_count(); + remute_voices(); + } + + // TODO: use more accurate length calculation + int msec = buf->length(); + blip_time_t clocks_emulated = msec * clock_rate_ / 1000 - 100; + RETURN_ERR( run_clocks( clocks_emulated, msec ) ); + assert( clocks_emulated ); + buf->end_frame( clocks_emulated ); + } + } + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Classic_Emu.h b/Frameworks/GME/gme/Classic_Emu.h index 4495400f1..0164ba83b 100644 --- a/Frameworks/GME/gme/Classic_Emu.h +++ b/Frameworks/GME/gme/Classic_Emu.h @@ -1,79 +1,79 @@ -// Common aspects of emulators which use Blip_Buffer for sound output - -// Game_Music_Emu $vers -#ifndef CLASSIC_EMU_H -#define CLASSIC_EMU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" -#include "Music_Emu.h" - -class Classic_Emu : public Music_Emu { -protected: -// Derived interface - - // Advertises type of sound on each voice, so Effects_Buffer can better choose - // what effect to apply (pan, echo, surround). Constant can have value added so - // that voices of the same type can be spread around the stereo sound space. - enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; - void set_voice_types( int const types [] ) { voice_types = types; } - - // Sets up Blip_Buffers after loading file - blargg_err_t setup_buffer( int clock_rate ); - - // Clock rate of Blip_buffers - int clock_rate() const { return clock_rate_; } - - // Changes clock rate of Blip_Buffers (experimental) - void change_clock_rate( int ); - -// Overrides should do the indicated task - - // Set Blip_Buffer(s) voice outputs to, or mute voice if pointer is NULL - virtual void set_voice( int index, Blip_Buffer* center, - Blip_Buffer* left, Blip_Buffer* right ) BLARGG_PURE( ; ) - - // Update equalization - virtual void update_eq( blip_eq_t const& ) BLARGG_PURE( ; ) - - // Start track - virtual blargg_err_t start_track_( int track ) BLARGG_PURE( ; ) - - // Run for at most msec or time_io clocks, then set time_io to number of clocks - // actually run for. After returning, Blip_Buffers have time frame of time_io clocks - // ended. - virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) BLARGG_PURE( ; ) - -// Internal -public: - Classic_Emu(); - ~Classic_Emu(); - virtual void set_buffer( Multi_Buffer* ); - -protected: - virtual blargg_err_t set_sample_rate_( int sample_rate ); - virtual void mute_voices_( int ); - virtual void set_equalizer_( equalizer_t const& ); - virtual blargg_err_t play_( int, sample_t [] ); - -private: - Multi_Buffer* buf; - Multi_Buffer* stereo_buffer; // NULL if using custom buffer - int clock_rate_; - unsigned buf_changed_count; - int const* voice_types; -}; - -inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf ) -{ - assert( !buf && new_buf ); - buf = new_buf; -} - -inline void Classic_Emu::set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ) { } - -inline void Classic_Emu::update_eq( blip_eq_t const& ) { } - -inline blargg_err_t Classic_Emu::run_clocks( blip_time_t&, int ) { return blargg_ok; } - -#endif +// Common aspects of emulators which use Blip_Buffer for sound output + +// Game_Music_Emu $vers +#ifndef CLASSIC_EMU_H +#define CLASSIC_EMU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" +#include "Music_Emu.h" + +class Classic_Emu : public Music_Emu { +protected: +// Derived interface + + // Advertises type of sound on each voice, so Effects_Buffer can better choose + // what effect to apply (pan, echo, surround). Constant can have value added so + // that voices of the same type can be spread around the stereo sound space. + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + void set_voice_types( int const types [] ) { voice_types = types; } + + // Sets up Blip_Buffers after loading file + blargg_err_t setup_buffer( int clock_rate ); + + // Clock rate of Blip_buffers + int clock_rate() const { return clock_rate_; } + + // Changes clock rate of Blip_Buffers (experimental) + void change_clock_rate( int ); + +// Overrides should do the indicated task + + // Set Blip_Buffer(s) voice outputs to, or mute voice if pointer is NULL + virtual void set_voice( int index, Blip_Buffer* center, + Blip_Buffer* left, Blip_Buffer* right ) BLARGG_PURE( ; ) + + // Update equalization + virtual void update_eq( blip_eq_t const& ) BLARGG_PURE( ; ) + + // Start track + virtual blargg_err_t start_track_( int track ) BLARGG_PURE( ; ) + + // Run for at most msec or time_io clocks, then set time_io to number of clocks + // actually run for. After returning, Blip_Buffers have time frame of time_io clocks + // ended. + virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) BLARGG_PURE( ; ) + +// Internal +public: + Classic_Emu(); + ~Classic_Emu(); + virtual void set_buffer( Multi_Buffer* ); + +protected: + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual void mute_voices_( int ); + virtual void set_equalizer_( equalizer_t const& ); + virtual blargg_err_t play_( int, sample_t [] ); + +private: + Multi_Buffer* buf; + Multi_Buffer* stereo_buffer; // NULL if using custom buffer + int clock_rate_; + unsigned buf_changed_count; + int const* voice_types; +}; + +inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf ) +{ + assert( !buf && new_buf ); + buf = new_buf; +} + +inline void Classic_Emu::set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ) { } + +inline void Classic_Emu::update_eq( blip_eq_t const& ) { } + +inline blargg_err_t Classic_Emu::run_clocks( blip_time_t&, int ) { return blargg_ok; } + +#endif diff --git a/Frameworks/GME/gme/Data_Reader.cpp b/Frameworks/GME/gme/Data_Reader.cpp index 5bbfbf551..81546c9cc 100755 --- a/Frameworks/GME/gme/Data_Reader.cpp +++ b/Frameworks/GME/gme/Data_Reader.cpp @@ -1,315 +1,315 @@ -// File_Extractor 0.4.0. http://www.slack.net/~ant/ - -#include "Data_Reader.h" - -#include "blargg_endian.h" -#include -#include -#include - -/* Copyright (C) 2005-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -const char Data_Reader::eof_error [] = "Unexpected end of file"; - -blargg_err_t Data_Reader::read( void* p, long s ) -{ - long result = read_avail( p, s ); - if ( result != s ) - { - if ( result >= 0 && result < s ) - return eof_error; - - return "Read error"; - } - - return 0; -} - -blargg_err_t Data_Reader::skip( long count ) -{ - char buf [512]; - while ( count ) - { - long n = sizeof buf; - if ( n > count ) - n = count; - count -= n; - RETURN_ERR( read( buf, n ) ); - } - return 0; -} - -long File_Reader::remain() const { return size() - tell(); } - -blargg_err_t File_Reader::skip( long n ) -{ - assert( n >= 0 ); - if ( !n ) - return 0; - return seek( tell() + n ); -} - -// Subset_Reader - -Subset_Reader::Subset_Reader( Data_Reader* dr, long size ) -{ - in = dr; - remain_ = dr->remain(); - if ( remain_ > size ) - remain_ = size; -} - -long Subset_Reader::remain() const { return remain_; } - -long Subset_Reader::read_avail( void* p, long s ) -{ - if ( s > remain_ ) - s = remain_; - remain_ -= s; - return in->read_avail( p, s ); -} - -// Remaining_Reader - -Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r ) -{ - header = (char const*) h; - header_end = header + size; - in = r; -} - -long Remaining_Reader::remain() const { return header_end - header + in->remain(); } - -long Remaining_Reader::read_first( void* out, long count ) -{ - long first = header_end - header; - if ( first ) - { - if ( first > count ) - first = count; - void const* old = header; - header += first; - memcpy( out, old, first ); - } - return first; -} - -long Remaining_Reader::read_avail( void* out, long count ) -{ - long first = read_first( out, count ); - long second = count - first; - if ( second ) - { - second = in->read_avail( (char*) out + first, second ); - if ( second <= 0 ) - return second; - } - return first + second; -} - -blargg_err_t Remaining_Reader::read( void* out, long count ) -{ - long first = read_first( out, count ); - long second = count - first; - if ( !second ) - return 0; - return in->read( (char*) out + first, second ); -} - -// Mem_File_Reader - -Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : - begin( (const char*) p ), - size_( s ) -{ - pos = 0; -} - -long Mem_File_Reader::size() const { return size_; } - -long Mem_File_Reader::read_avail( void* p, long s ) -{ - long r = remain(); - if ( s > r ) - s = r; - memcpy( p, begin + pos, s ); - pos += s; - return s; -} - -long Mem_File_Reader::tell() const { return pos; } - -blargg_err_t Mem_File_Reader::seek( long n ) -{ - if ( n > size_ ) - return eof_error; - pos = n; - return 0; -} - -// Callback_Reader - -Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : - callback( c ), - data( d ) -{ - remain_ = size; -} - -long Callback_Reader::remain() const { return remain_; } - -long Callback_Reader::read_avail( void* out, long count ) -{ - if ( count > remain_ ) - count = remain_; - if ( Callback_Reader::read( out, count ) ) - count = -1; - return count; -} - -blargg_err_t Callback_Reader::read( void* out, long count ) -{ - if ( count > remain_ ) - return eof_error; - return callback( data, out, count ); -} - -// Std_File_Reader - -Std_File_Reader::Std_File_Reader() : file_( 0 ) { } - -Std_File_Reader::~Std_File_Reader() { close(); } - -blargg_err_t Std_File_Reader::open( const char* path ) -{ - file_ = fopen( path, "rb" ); - if ( !file_ ) - return "Couldn't open file"; - return 0; -} - -long Std_File_Reader::size() const -{ - long pos = tell(); - fseek( (FILE*) file_, 0, SEEK_END ); - long result = tell(); - fseek( (FILE*) file_, pos, SEEK_SET ); - return result; -} - -long Std_File_Reader::read_avail( void* p, long s ) -{ - return fread( p, 1, s, (FILE*) file_ ); -} - -blargg_err_t Std_File_Reader::read( void* p, long s ) -{ - if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) - return 0; - if ( feof( (FILE*) file_ ) ) - return eof_error; - return "Couldn't read from file"; -} - -long Std_File_Reader::tell() const { return ftell( (FILE*) file_ ); } - -blargg_err_t Std_File_Reader::seek( long n ) -{ - if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) - return 0; - if ( n > size() ) - return eof_error; - return "Error seeking in file"; -} - -void Std_File_Reader::close() -{ - if ( file_ ) - { - fclose( (FILE*) file_ ); - file_ = 0; - } -} - -// Gzip_File_Reader - -#ifdef HAVE_ZLIB_H - -#include "zlib.h" - -static const char* get_gzip_eof( const char* path, long* eof ) -{ - FILE* file = fopen( path, "rb" ); - if ( !file ) - return "Couldn't open file"; - - unsigned char buf [4]; - if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) - { - fseek( file, -4, SEEK_END ); - fread( buf, 4, 1, file ); - *eof = get_le32( buf ); - } - else - { - fseek( file, 0, SEEK_END ); - *eof = ftell( file ); - } - const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : 0; - fclose( file ); - return err; -} - -Gzip_File_Reader::Gzip_File_Reader() : file_( 0 ) { } - -Gzip_File_Reader::~Gzip_File_Reader() { close(); } - -blargg_err_t Gzip_File_Reader::open( const char* path ) -{ - close(); - - RETURN_ERR( get_gzip_eof( path, &size_ ) ); - - file_ = gzopen( path, "rb" ); - if ( !file_ ) - return "Couldn't open file"; - - return 0; -} - -long Gzip_File_Reader::size() const { return size_; } - -long Gzip_File_Reader::read_avail( void* p, long s ) { return gzread( file_, p, s ); } - -long Gzip_File_Reader::tell() const { return gztell( file_ ); } - -blargg_err_t Gzip_File_Reader::seek( long n ) -{ - if ( gzseek( file_, n, SEEK_SET ) >= 0 ) - return 0; - if ( n > size_ ) - return eof_error; - return "Error seeking in file"; -} - -void Gzip_File_Reader::close() -{ - if ( file_ ) - { - gzclose( file_ ); - file_ = 0; - } -} - -#endif +// File_Extractor 0.4.0. http://www.slack.net/~ant/ + +#include "Data_Reader.h" + +#include "blargg_endian.h" +#include +#include +#include + +/* Copyright (C) 2005-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +const char Data_Reader::eof_error [] = "Unexpected end of file"; + +blargg_err_t Data_Reader::read( void* p, long s ) +{ + long result = read_avail( p, s ); + if ( result != s ) + { + if ( result >= 0 && result < s ) + return eof_error; + + return "Read error"; + } + + return 0; +} + +blargg_err_t Data_Reader::skip( long count ) +{ + char buf [512]; + while ( count ) + { + long n = sizeof buf; + if ( n > count ) + n = count; + count -= n; + RETURN_ERR( read( buf, n ) ); + } + return 0; +} + +long File_Reader::remain() const { return size() - tell(); } + +blargg_err_t File_Reader::skip( long n ) +{ + assert( n >= 0 ); + if ( !n ) + return 0; + return seek( tell() + n ); +} + +// Subset_Reader + +Subset_Reader::Subset_Reader( Data_Reader* dr, long size ) +{ + in = dr; + remain_ = dr->remain(); + if ( remain_ > size ) + remain_ = size; +} + +long Subset_Reader::remain() const { return remain_; } + +long Subset_Reader::read_avail( void* p, long s ) +{ + if ( s > remain_ ) + s = remain_; + remain_ -= s; + return in->read_avail( p, s ); +} + +// Remaining_Reader + +Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r ) +{ + header = (char const*) h; + header_end = header + size; + in = r; +} + +long Remaining_Reader::remain() const { return header_end - header + in->remain(); } + +long Remaining_Reader::read_first( void* out, long count ) +{ + long first = header_end - header; + if ( first ) + { + if ( first > count ) + first = count; + void const* old = header; + header += first; + memcpy( out, old, first ); + } + return first; +} + +long Remaining_Reader::read_avail( void* out, long count ) +{ + long first = read_first( out, count ); + long second = count - first; + if ( second ) + { + second = in->read_avail( (char*) out + first, second ); + if ( second <= 0 ) + return second; + } + return first + second; +} + +blargg_err_t Remaining_Reader::read( void* out, long count ) +{ + long first = read_first( out, count ); + long second = count - first; + if ( !second ) + return 0; + return in->read( (char*) out + first, second ); +} + +// Mem_File_Reader + +Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : + begin( (const char*) p ), + size_( s ) +{ + pos = 0; +} + +long Mem_File_Reader::size() const { return size_; } + +long Mem_File_Reader::read_avail( void* p, long s ) +{ + long r = remain(); + if ( s > r ) + s = r; + memcpy( p, begin + pos, s ); + pos += s; + return s; +} + +long Mem_File_Reader::tell() const { return pos; } + +blargg_err_t Mem_File_Reader::seek( long n ) +{ + if ( n > size_ ) + return eof_error; + pos = n; + return 0; +} + +// Callback_Reader + +Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : + callback( c ), + data( d ) +{ + remain_ = size; +} + +long Callback_Reader::remain() const { return remain_; } + +long Callback_Reader::read_avail( void* out, long count ) +{ + if ( count > remain_ ) + count = remain_; + if ( Callback_Reader::read( out, count ) ) + count = -1; + return count; +} + +blargg_err_t Callback_Reader::read( void* out, long count ) +{ + if ( count > remain_ ) + return eof_error; + return callback( data, out, count ); +} + +// Std_File_Reader + +Std_File_Reader::Std_File_Reader() : file_( 0 ) { } + +Std_File_Reader::~Std_File_Reader() { close(); } + +blargg_err_t Std_File_Reader::open( const char* path ) +{ + file_ = fopen( path, "rb" ); + if ( !file_ ) + return "Couldn't open file"; + return 0; +} + +long Std_File_Reader::size() const +{ + long pos = tell(); + fseek( (FILE*) file_, 0, SEEK_END ); + long result = tell(); + fseek( (FILE*) file_, pos, SEEK_SET ); + return result; +} + +long Std_File_Reader::read_avail( void* p, long s ) +{ + return fread( p, 1, s, (FILE*) file_ ); +} + +blargg_err_t Std_File_Reader::read( void* p, long s ) +{ + if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) + return 0; + if ( feof( (FILE*) file_ ) ) + return eof_error; + return "Couldn't read from file"; +} + +long Std_File_Reader::tell() const { return ftell( (FILE*) file_ ); } + +blargg_err_t Std_File_Reader::seek( long n ) +{ + if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) + return 0; + if ( n > size() ) + return eof_error; + return "Error seeking in file"; +} + +void Std_File_Reader::close() +{ + if ( file_ ) + { + fclose( (FILE*) file_ ); + file_ = 0; + } +} + +// Gzip_File_Reader + +#ifdef HAVE_ZLIB_H + +#include "zlib.h" + +static const char* get_gzip_eof( const char* path, long* eof ) +{ + FILE* file = fopen( path, "rb" ); + if ( !file ) + return "Couldn't open file"; + + unsigned char buf [4]; + if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) + { + fseek( file, -4, SEEK_END ); + fread( buf, 4, 1, file ); + *eof = get_le32( buf ); + } + else + { + fseek( file, 0, SEEK_END ); + *eof = ftell( file ); + } + const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : 0; + fclose( file ); + return err; +} + +Gzip_File_Reader::Gzip_File_Reader() : file_( 0 ) { } + +Gzip_File_Reader::~Gzip_File_Reader() { close(); } + +blargg_err_t Gzip_File_Reader::open( const char* path ) +{ + close(); + + RETURN_ERR( get_gzip_eof( path, &size_ ) ); + + file_ = gzopen( path, "rb" ); + if ( !file_ ) + return "Couldn't open file"; + + return 0; +} + +long Gzip_File_Reader::size() const { return size_; } + +long Gzip_File_Reader::read_avail( void* p, long s ) { return gzread( file_, p, s ); } + +long Gzip_File_Reader::tell() const { return gztell( file_ ); } + +blargg_err_t Gzip_File_Reader::seek( long n ) +{ + if ( gzseek( file_, n, SEEK_SET ) >= 0 ) + return 0; + if ( n > size_ ) + return eof_error; + return "Error seeking in file"; +} + +void Gzip_File_Reader::close() +{ + if ( file_ ) + { + gzclose( file_ ); + file_ = 0; + } +} + +#endif diff --git a/Frameworks/GME/gme/Data_Reader.h b/Frameworks/GME/gme/Data_Reader.h index 00b53b9e2..2684677c1 100755 --- a/Frameworks/GME/gme/Data_Reader.h +++ b/Frameworks/GME/gme/Data_Reader.h @@ -1,151 +1,151 @@ -// Data reader interface for uniform access - -// File_Extractor 0.4.0 -#ifndef DATA_READER_H -#define DATA_READER_H - -#include "blargg_common.h" - -// Supports reading and finding out how many bytes are remaining -class Data_Reader { -public: - virtual ~Data_Reader() { } - - static const char eof_error []; // returned by read() when request goes beyond end - - // Read at most count bytes and return number actually read, or <= 0 if error - virtual long read_avail( void*, long n ) = 0; - - // Read exactly count bytes and return error if they couldn't be read - virtual blargg_err_t read( void*, long count ); - - // Number of bytes remaining until end of file - virtual long remain() const = 0; - - // Read and discard count bytes - virtual blargg_err_t skip( long count ); - -public: - Data_Reader() { } - typedef blargg_err_t error_t; // deprecated -private: - // noncopyable - Data_Reader( const Data_Reader& ); - Data_Reader& operator = ( const Data_Reader& ); -}; - -// Supports seeking in addition to Data_Reader operations -class File_Reader : public Data_Reader { -public: - // Size of file - virtual long size() const = 0; - - // Current position in file - virtual long tell() const = 0; - - // Go to new position - virtual blargg_err_t seek( long ) = 0; - - long remain() const; - blargg_err_t skip( long n ); -}; - -// Disk file reader -class Std_File_Reader : public File_Reader { -public: - blargg_err_t open( const char* path ); - void close(); - -public: - Std_File_Reader(); - ~Std_File_Reader(); - long size() const; - blargg_err_t read( void*, long ); - long read_avail( void*, long ); - long tell() const; - blargg_err_t seek( long ); -private: - void* file_; -}; - -// Treats range of memory as a file -class Mem_File_Reader : public File_Reader { -public: - Mem_File_Reader( const void*, long size ); - -public: - long size() const; - long read_avail( void*, long ); - long tell() const; - blargg_err_t seek( long ); -private: - const char* const begin; - const long size_; - long pos; -}; - -// Makes it look like there are only count bytes remaining -class Subset_Reader : public Data_Reader { -public: - Subset_Reader( Data_Reader*, long count ); - -public: - long remain() const; - long read_avail( void*, long ); -private: - Data_Reader* in; - long remain_; -}; - -// Joins already-read header and remaining data into original file (to avoid seeking) -class Remaining_Reader : public Data_Reader { -public: - Remaining_Reader( void const* header, long size, Data_Reader* ); - -public: - long remain() const; - long read_avail( void*, long ); - blargg_err_t read( void*, long ); -private: - char const* header; - char const* header_end; - Data_Reader* in; - long read_first( void* out, long count ); -}; - -// Invokes callback function to read data. Size of data must be specified in advance. -class Callback_Reader : public Data_Reader { -public: - typedef const char* (*callback_t)( void* data, void* out, long count ); - Callback_Reader( callback_t, long size, void* data = 0 ); -public: - long read_avail( void*, long ); - blargg_err_t read( void*, long ); - long remain() const; -private: - callback_t const callback; - void* const data; - long remain_; -}; - -#ifdef HAVE_ZLIB_H -// Gzip compressed file reader -class Gzip_File_Reader : public File_Reader { -public: - blargg_err_t open( const char* path ); - void close(); - -public: - Gzip_File_Reader(); - ~Gzip_File_Reader(); - long size() const; - long read_avail( void*, long ); - long tell() const; - blargg_err_t seek( long ); -private: - void* file_; - long size_; -}; -#endif - -#endif +// Data reader interface for uniform access + +// File_Extractor 0.4.0 +#ifndef DATA_READER_H +#define DATA_READER_H + +#include "blargg_common.h" + +// Supports reading and finding out how many bytes are remaining +class Data_Reader { +public: + virtual ~Data_Reader() { } + + static const char eof_error []; // returned by read() when request goes beyond end + + // Read at most count bytes and return number actually read, or <= 0 if error + virtual long read_avail( void*, long n ) = 0; + + // Read exactly count bytes and return error if they couldn't be read + virtual blargg_err_t read( void*, long count ); + + // Number of bytes remaining until end of file + virtual long remain() const = 0; + + // Read and discard count bytes + virtual blargg_err_t skip( long count ); + +public: + Data_Reader() { } + typedef blargg_err_t error_t; // deprecated +private: + // noncopyable + Data_Reader( const Data_Reader& ); + Data_Reader& operator = ( const Data_Reader& ); +}; + +// Supports seeking in addition to Data_Reader operations +class File_Reader : public Data_Reader { +public: + // Size of file + virtual long size() const = 0; + + // Current position in file + virtual long tell() const = 0; + + // Go to new position + virtual blargg_err_t seek( long ) = 0; + + long remain() const; + blargg_err_t skip( long n ); +}; + +// Disk file reader +class Std_File_Reader : public File_Reader { +public: + blargg_err_t open( const char* path ); + void close(); + +public: + Std_File_Reader(); + ~Std_File_Reader(); + long size() const; + blargg_err_t read( void*, long ); + long read_avail( void*, long ); + long tell() const; + blargg_err_t seek( long ); +private: + void* file_; +}; + +// Treats range of memory as a file +class Mem_File_Reader : public File_Reader { +public: + Mem_File_Reader( const void*, long size ); + +public: + long size() const; + long read_avail( void*, long ); + long tell() const; + blargg_err_t seek( long ); +private: + const char* const begin; + const long size_; + long pos; +}; + +// Makes it look like there are only count bytes remaining +class Subset_Reader : public Data_Reader { +public: + Subset_Reader( Data_Reader*, long count ); + +public: + long remain() const; + long read_avail( void*, long ); +private: + Data_Reader* in; + long remain_; +}; + +// Joins already-read header and remaining data into original file (to avoid seeking) +class Remaining_Reader : public Data_Reader { +public: + Remaining_Reader( void const* header, long size, Data_Reader* ); + +public: + long remain() const; + long read_avail( void*, long ); + blargg_err_t read( void*, long ); +private: + char const* header; + char const* header_end; + Data_Reader* in; + long read_first( void* out, long count ); +}; + +// Invokes callback function to read data. Size of data must be specified in advance. +class Callback_Reader : public Data_Reader { +public: + typedef const char* (*callback_t)( void* data, void* out, long count ); + Callback_Reader( callback_t, long size, void* data = 0 ); +public: + long read_avail( void*, long ); + blargg_err_t read( void*, long ); + long remain() const; +private: + callback_t const callback; + void* const data; + long remain_; +}; + +#ifdef HAVE_ZLIB_H +// Gzip compressed file reader +class Gzip_File_Reader : public File_Reader { +public: + blargg_err_t open( const char* path ); + void close(); + +public: + Gzip_File_Reader(); + ~Gzip_File_Reader(); + long size() const; + long read_avail( void*, long ); + long tell() const; + blargg_err_t seek( long ); +private: + void* file_; + long size_; +}; +#endif + +#endif diff --git a/Frameworks/GME/gme/Downsampler.cpp b/Frameworks/GME/gme/Downsampler.cpp index 9df5eb2be..b37bf2ca8 100644 --- a/Frameworks/GME/gme/Downsampler.cpp +++ b/Frameworks/GME/gme/Downsampler.cpp @@ -1,74 +1,74 @@ -// $package. http://www.slack.net/~ant/ - -#include "Downsampler.h" - -/* Copyright (C) 2004-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const shift = 14; -int const unit = 1 << shift; - -void Downsampler::clear_() -{ - pos = 0; - Resampler::clear_(); -} - -Downsampler::Downsampler() -{ - clear(); -} - -blargg_err_t Downsampler::set_rate_( double new_factor ) -{ - step = (int) (new_factor * unit + 0.5); - return Resampler::set_rate_( 1.0 / unit * step ); -} - -Resampler::sample_t const* Downsampler::resample_( sample_t** out_, - sample_t const* out_end, sample_t const in [], int in_size ) -{ - in_size -= write_offset; - if ( in_size > 0 ) - { - sample_t* BLARGG_RESTRICT out = *out_; - sample_t const* const in_end = in + in_size; - - int const step = this->step; - int pos = this->pos; - - // TODO: IIR filter, then linear resample - // TODO: detect skipped sample, allowing merging of IIR and resample? - - do - { - #define INTERP( i, out )\ - out = (in [0 + i] * (unit - pos) + ((in [2 + i] + in [4 + i] + in [6 + i]) << shift) +\ - in [8 + i] * pos) >> (shift + 2); - - int out_0; - INTERP( 0, out_0 ) - INTERP( 1, out [0] = out_0; out [1] ) - out += stereo; - - pos += step; - in += ((unsigned) pos >> shift) * stereo; - pos &= unit - 1; - } - while ( in < in_end && out < out_end ); - - this->pos = pos; - *out_ = out; - } - return in; -} +// $package. http://www.slack.net/~ant/ + +#include "Downsampler.h" + +/* Copyright (C) 2004-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const shift = 14; +int const unit = 1 << shift; + +void Downsampler::clear_() +{ + pos = 0; + Resampler::clear_(); +} + +Downsampler::Downsampler() +{ + clear(); +} + +blargg_err_t Downsampler::set_rate_( double new_factor ) +{ + step = (int) (new_factor * unit + 0.5); + return Resampler::set_rate_( 1.0 / unit * step ); +} + +Resampler::sample_t const* Downsampler::resample_( sample_t** out_, + sample_t const* out_end, sample_t const in [], int in_size ) +{ + in_size -= write_offset; + if ( in_size > 0 ) + { + sample_t* BLARGG_RESTRICT out = *out_; + sample_t const* const in_end = in + in_size; + + int const step = this->step; + int pos = this->pos; + + // TODO: IIR filter, then linear resample + // TODO: detect skipped sample, allowing merging of IIR and resample? + + do + { + #define INTERP( i, out )\ + out = (in [0 + i] * (unit - pos) + ((in [2 + i] + in [4 + i] + in [6 + i]) << shift) +\ + in [8 + i] * pos) >> (shift + 2); + + int out_0; + INTERP( 0, out_0 ) + INTERP( 1, out [0] = out_0; out [1] ) + out += stereo; + + pos += step; + in += ((unsigned) pos >> shift) * stereo; + pos &= unit - 1; + } + while ( in < in_end && out < out_end ); + + this->pos = pos; + *out_ = out; + } + return in; +} diff --git a/Frameworks/GME/gme/Downsampler.h b/Frameworks/GME/gme/Downsampler.h index a26dec466..7ac6272b0 100644 --- a/Frameworks/GME/gme/Downsampler.h +++ b/Frameworks/GME/gme/Downsampler.h @@ -1,25 +1,25 @@ -// Linear downsampler with pre-low-pass - -// $package -#ifndef DOWNSAMPLER_H -#define DOWNSAMPLER_H - -#include "Resampler.h" - -class Downsampler : public Resampler { -public: - Downsampler(); - -protected: - virtual blargg_err_t set_rate_( double ); - virtual void clear_(); - virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); - -private: - enum { stereo = 2 }; - enum { write_offset = 8 * stereo }; - int pos; - int step; -}; - -#endif +// Linear downsampler with pre-low-pass + +// $package +#ifndef DOWNSAMPLER_H +#define DOWNSAMPLER_H + +#include "Resampler.h" + +class Downsampler : public Resampler { +public: + Downsampler(); + +protected: + virtual blargg_err_t set_rate_( double ); + virtual void clear_(); + virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); + +private: + enum { stereo = 2 }; + enum { write_offset = 8 * stereo }; + int pos; + int step; +}; + +#endif diff --git a/Frameworks/GME/gme/Dual_Resampler.h b/Frameworks/GME/gme/Dual_Resampler.h index 2bdfb4afa..3a98dc60c 100644 --- a/Frameworks/GME/gme/Dual_Resampler.h +++ b/Frameworks/GME/gme/Dual_Resampler.h @@ -1,61 +1,61 @@ -// Combination of Fir_Resampler and Stereo_Buffer mixing. Used by Sega FM emulators. - -// Game_Music_Emu $vers -#ifndef DUAL_RESAMPLER_H -#define DUAL_RESAMPLER_H - -#include "Multi_Buffer.h" - -#if GME_VGM_FAST_RESAMPLER - #include "Downsampler.h" - typedef Downsampler Dual_Resampler_Downsampler; -#else - #include "Fir_Resampler.h" - typedef Fir_Resampler_Norm Dual_Resampler_Downsampler; -#endif - -class Dual_Resampler { -public: - typedef short dsample_t; - - blargg_err_t setup( double oversample, double rolloff, double gain ); - double rate() const { return resampler.rate(); } - blargg_err_t reset( int max_pairs ); - void resize( int pairs_per_frame ); - void clear(); - - void dual_play( int count, dsample_t out [], Stereo_Buffer&, Stereo_Buffer** secondary_buf_set = NULL, int secondary_buf_set_count = 0 ); - - blargg_callback set_callback; - -// Implementation -public: - Dual_Resampler(); - ~Dual_Resampler(); - -private: - enum { gain_bits = 14 }; - blargg_vector sample_buf; - int sample_buf_size; - int oversamples_per_frame; - int buf_pos; - int buffered; - int resampler_size; - int gain_; - - Dual_Resampler_Downsampler resampler; - void mix_samples( Stereo_Buffer&, dsample_t [], int, Stereo_Buffer**, int ); - void mix_mono( Stereo_Buffer&, dsample_t [], int ); - void mix_stereo( Stereo_Buffer&, dsample_t [], int ); - void mix_extra_mono( Stereo_Buffer&, dsample_t [], int ); - void mix_extra_stereo( Stereo_Buffer&, dsample_t [], int ); - int play_frame_( Stereo_Buffer&, dsample_t [], Stereo_Buffer**, int ); -}; - -inline blargg_err_t Dual_Resampler::setup( double oversample, double rolloff, double gain ) -{ - gain_ = (int) ((1 << gain_bits) * gain); - return resampler.set_rate( oversample ); -} - -#endif +// Combination of Fir_Resampler and Stereo_Buffer mixing. Used by Sega FM emulators. + +// Game_Music_Emu $vers +#ifndef DUAL_RESAMPLER_H +#define DUAL_RESAMPLER_H + +#include "Multi_Buffer.h" + +#if GME_VGM_FAST_RESAMPLER + #include "Downsampler.h" + typedef Downsampler Dual_Resampler_Downsampler; +#else + #include "Fir_Resampler.h" + typedef Fir_Resampler_Norm Dual_Resampler_Downsampler; +#endif + +class Dual_Resampler { +public: + typedef short dsample_t; + + blargg_err_t setup( double oversample, double rolloff, double gain ); + double rate() const { return resampler.rate(); } + blargg_err_t reset( int max_pairs ); + void resize( int pairs_per_frame ); + void clear(); + + void dual_play( int count, dsample_t out [], Stereo_Buffer&, Stereo_Buffer** secondary_buf_set = NULL, int secondary_buf_set_count = 0 ); + + blargg_callback set_callback; + +// Implementation +public: + Dual_Resampler(); + ~Dual_Resampler(); + +private: + enum { gain_bits = 14 }; + blargg_vector sample_buf; + int sample_buf_size; + int oversamples_per_frame; + int buf_pos; + int buffered; + int resampler_size; + int gain_; + + Dual_Resampler_Downsampler resampler; + void mix_samples( Stereo_Buffer&, dsample_t [], int, Stereo_Buffer**, int ); + void mix_mono( Stereo_Buffer&, dsample_t [], int ); + void mix_stereo( Stereo_Buffer&, dsample_t [], int ); + void mix_extra_mono( Stereo_Buffer&, dsample_t [], int ); + void mix_extra_stereo( Stereo_Buffer&, dsample_t [], int ); + int play_frame_( Stereo_Buffer&, dsample_t [], Stereo_Buffer**, int ); +}; + +inline blargg_err_t Dual_Resampler::setup( double oversample, double rolloff, double gain ) +{ + gain_ = (int) ((1 << gain_bits) * gain); + return resampler.set_rate( oversample ); +} + +#endif diff --git a/Frameworks/GME/gme/Effects_Buffer.cpp b/Frameworks/GME/gme/Effects_Buffer.cpp index 496c00bb0..56bc81ff1 100644 --- a/Frameworks/GME/gme/Effects_Buffer.cpp +++ b/Frameworks/GME/gme/Effects_Buffer.cpp @@ -18,13 +18,13 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef BLARGG_ENABLE_OPTIMIZER #include BLARGG_ENABLE_OPTIMIZER #endif - -int const fixed_shift = 12; -#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift)) -#define FROM_FIXED( f ) ((f) >> fixed_shift) - -int const max_read = 2560; // determines minimum delay - + +int const fixed_shift = 12; +#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift)) +#define FROM_FIXED( f ) ((f) >> fixed_shift) + +int const max_read = 2560; // determines minimum delay + Effects_Buffer::Effects_Buffer( int max_bufs, int echo_size_ ) : Multi_Buffer( stereo ) { echo_size = max( max_read * (int) stereo, echo_size_ & ~1 ); diff --git a/Frameworks/GME/gme/Effects_Buffer.h b/Frameworks/GME/gme/Effects_Buffer.h index cd6c620f8..984073a7c 100644 --- a/Frameworks/GME/gme/Effects_Buffer.h +++ b/Frameworks/GME/gme/Effects_Buffer.h @@ -1,149 +1,149 @@ -// Multi-channel effects buffer with echo and individual panning for each channel - -// Game_Music_Emu $vers -#ifndef EFFECTS_BUFFER_H -#define EFFECTS_BUFFER_H - -#include "Multi_Buffer.h" - -// See Simple_Effects_Buffer (below) for a simpler interface - -class Effects_Buffer : public Multi_Buffer { -public: - // To reduce memory usage, fewer buffers can be used (with a best-fit - // approach if there are too few), and maximum echo delay can be reduced - Effects_Buffer( int max_bufs = 32, int echo_size = 24 * 1024 ); - - struct pan_vol_t - { - float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal - float pan; // -1.0 = left, 0.0 = center, +1.0 = right - }; - - // Global configuration - struct config_t - { - bool enabled; // false = disable all effects - - // Current sound is echoed at adjustable left/right delay, - // with reduced treble and volume (feedback). - float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent - int delay [2]; // left, right delays (msec) - float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony - pan_vol_t side_chans [2]; // left and right side channel volume and pan - }; - config_t& config() { return config_; } - - // Limits of delay (msec) - int min_delay() const; - int max_delay() const; - - // Per-channel configuration. Two or more channels with matching parameters are - // optimized to internally use the same buffer. - struct chan_config_t : pan_vol_t - { - // (inherited from pan_vol_t) - //float vol; // these only affect center channel - //float pan; - bool surround; // if true, negates left volume to put sound in back - bool echo; // false = channel doesn't have any echo - }; - chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; } - - // Applies any changes made to config() and chan_config() - virtual void apply_config(); - -// Implementation -public: - ~Effects_Buffer(); - blargg_err_t set_sample_rate( int samples_per_sec, int msec = blip_default_length ); - blargg_err_t set_channel_count( int, int const* = NULL ); - void clock_rate( int ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t ); - int read_samples( blip_sample_t [], int ); - int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } - enum { stereo = 2 }; - typedef int fixed_t; - -protected: - enum { extra_chans = stereo * stereo }; - -private: - config_t config_; - int clock_rate_; - int bass_freq_; - - int echo_size; - - struct chan_t - { - fixed_t vol [stereo]; - chan_config_t cfg; - channel_t channel; - }; - blargg_vector chans; - - struct buf_t : Tracked_Blip_Buffer - { - // nasty: Blip_Buffer has something called fixed_t - Effects_Buffer::fixed_t vol [stereo]; - bool echo; - - void* operator new ( size_t, void* p ) { return p; } - void operator delete ( void* ) { } - - ~buf_t() { } - }; - buf_t* bufs; - int bufs_size; - int bufs_max; // bufs_size <= bufs_max, to limit memory usage - Stereo_Mixer mixer; - - struct { - int delay [stereo]; - fixed_t treble; - fixed_t feedback; - fixed_t low_pass [stereo]; - } s; - - blargg_vector echo; - int echo_pos; - - bool no_effects; - bool no_echo; - - void assign_buffers(); - void clear_echo(); - void mix_effects( blip_sample_t out [], int pair_count ); - blargg_err_t new_bufs( int size ); - void delete_bufs(); -}; - -// Simpler interface and lower memory usage -class Simple_Effects_Buffer : public Effects_Buffer { -public: - struct config_t - { - bool enabled; // false = disable all effects - - float echo; // 0.0 = none, 1.0 = lots - float stereo; // 0.0 = channels in center, 1.0 = channels on left/right - bool surround; // true = put some channels in back - }; - config_t& config() { return config_; } - - // Applies any changes made to config() - void apply_config(); - -// Implementation -public: - Simple_Effects_Buffer(); -private: - config_t config_; - void chan_config(); // hide -}; - -#endif +// Multi-channel effects buffer with echo and individual panning for each channel + +// Game_Music_Emu $vers +#ifndef EFFECTS_BUFFER_H +#define EFFECTS_BUFFER_H + +#include "Multi_Buffer.h" + +// See Simple_Effects_Buffer (below) for a simpler interface + +class Effects_Buffer : public Multi_Buffer { +public: + // To reduce memory usage, fewer buffers can be used (with a best-fit + // approach if there are too few), and maximum echo delay can be reduced + Effects_Buffer( int max_bufs = 32, int echo_size = 24 * 1024 ); + + struct pan_vol_t + { + float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal + float pan; // -1.0 = left, 0.0 = center, +1.0 = right + }; + + // Global configuration + struct config_t + { + bool enabled; // false = disable all effects + + // Current sound is echoed at adjustable left/right delay, + // with reduced treble and volume (feedback). + float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent + int delay [2]; // left, right delays (msec) + float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony + pan_vol_t side_chans [2]; // left and right side channel volume and pan + }; + config_t& config() { return config_; } + + // Limits of delay (msec) + int min_delay() const; + int max_delay() const; + + // Per-channel configuration. Two or more channels with matching parameters are + // optimized to internally use the same buffer. + struct chan_config_t : pan_vol_t + { + // (inherited from pan_vol_t) + //float vol; // these only affect center channel + //float pan; + bool surround; // if true, negates left volume to put sound in back + bool echo; // false = channel doesn't have any echo + }; + chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; } + + // Applies any changes made to config() and chan_config() + virtual void apply_config(); + +// Implementation +public: + ~Effects_Buffer(); + blargg_err_t set_sample_rate( int samples_per_sec, int msec = blip_default_length ); + blargg_err_t set_channel_count( int, int const* = NULL ); + void clock_rate( int ); + void bass_freq( int ); + void clear(); + channel_t channel( int ); + void end_frame( blip_time_t ); + int read_samples( blip_sample_t [], int ); + int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + enum { stereo = 2 }; + typedef int fixed_t; + +protected: + enum { extra_chans = stereo * stereo }; + +private: + config_t config_; + int clock_rate_; + int bass_freq_; + + int echo_size; + + struct chan_t + { + fixed_t vol [stereo]; + chan_config_t cfg; + channel_t channel; + }; + blargg_vector chans; + + struct buf_t : Tracked_Blip_Buffer + { + // nasty: Blip_Buffer has something called fixed_t + Effects_Buffer::fixed_t vol [stereo]; + bool echo; + + void* operator new ( size_t, void* p ) { return p; } + void operator delete ( void* ) { } + + ~buf_t() { } + }; + buf_t* bufs; + int bufs_size; + int bufs_max; // bufs_size <= bufs_max, to limit memory usage + Stereo_Mixer mixer; + + struct { + int delay [stereo]; + fixed_t treble; + fixed_t feedback; + fixed_t low_pass [stereo]; + } s; + + blargg_vector echo; + int echo_pos; + + bool no_effects; + bool no_echo; + + void assign_buffers(); + void clear_echo(); + void mix_effects( blip_sample_t out [], int pair_count ); + blargg_err_t new_bufs( int size ); + void delete_bufs(); +}; + +// Simpler interface and lower memory usage +class Simple_Effects_Buffer : public Effects_Buffer { +public: + struct config_t + { + bool enabled; // false = disable all effects + + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back + }; + config_t& config() { return config_; } + + // Applies any changes made to config() + void apply_config(); + +// Implementation +public: + Simple_Effects_Buffer(); +private: + config_t config_; + void chan_config(); // hide +}; + +#endif diff --git a/Frameworks/GME/gme/Fir_Resampler.cpp b/Frameworks/GME/gme/Fir_Resampler.cpp index 5dddf29b6..da7cdc0f0 100644 --- a/Frameworks/GME/gme/Fir_Resampler.cpp +++ b/Frameworks/GME/gme/Fir_Resampler.cpp @@ -1,123 +1,123 @@ -// $package. http://www.slack.net/~ant/ - -#include "Fir_Resampler.h" - -#include - -/* Copyright (C) 2004-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#undef PI -#define PI 3.1415926535897932384626433832795029 - -static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, - int count, short* out ) -{ - double const maxh = 256; - double const step = PI / maxh * spacing; - double const to_w = maxh * 2 / width; - double const pow_a_n = pow( rolloff, maxh ); - scale /= maxh * 2; - - double angle = (count / 2 - 1 + offset) * -step; - while ( count-- ) - { - *out++ = 0; - double w = angle * to_w; - if ( fabs( w ) < PI ) - { - double rolloff_cos_a = rolloff * cos( angle ); - double num = 1 - rolloff_cos_a - - pow_a_n * cos( maxh * angle ) + - pow_a_n * rolloff * cos( (maxh - 1) * angle ); - double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - double sinc = scale * num / den - scale; - - out [-1] = (short) (cos( w ) * sinc + sinc); - } - angle += step; - } -} - -Fir_Resampler_::Fir_Resampler_( int width, sample_t impulses_ [] ) : - width_( width ), - impulses( impulses_ ) -{ - imp = NULL; -} - -void Fir_Resampler_::clear_() -{ - imp = impulses; - Resampler::clear_(); -} - -blargg_err_t Fir_Resampler_::set_rate_( double new_factor ) -{ - double const rolloff = 0.999; - double const gain = 1.0; - - // determine number of sub-phases that yield lowest error - double ratio_ = 0.0; - int res = -1; - { - double least_error = 2; - double pos = 0; - for ( int r = 1; r <= max_res; r++ ) - { - pos += new_factor; - double nearest = floor( pos + 0.5 ); - double error = fabs( pos - nearest ); - if ( error < least_error ) - { - res = r; - ratio_ = nearest / res; - least_error = error; - } - } - } - RETURN_ERR( Resampler::set_rate_( ratio_ ) ); - - // how much of input is used for each output sample - int const step = stereo * (int) floor( ratio_ ); - double fraction = fmod( ratio_, 1.0 ); - - double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; - double pos = 0.0; - //int input_per_cycle = 0; - sample_t* out = impulses; - for ( int n = res; --n >= 0; ) - { - gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, - double (0x7FFF * gain * filter), (int) width_, out ); - out += width_; - - int cur_step = step; - pos += fraction; - if ( pos >= 0.9999999 ) - { - pos -= 1.0; - cur_step += stereo; - } - - *out++ = (cur_step - width_ * 2 + 4) * sizeof (sample_t); - *out++ = 4 * sizeof (sample_t); - //input_per_cycle += cur_step; - } - // last offset moves back to beginning of impulses - out [-1] -= (char*) out - (char*) impulses; - - imp = impulses; - - return blargg_ok; -} +// $package. http://www.slack.net/~ant/ + +#include "Fir_Resampler.h" + +#include + +/* Copyright (C) 2004-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, + int count, short* out ) +{ + double const maxh = 256; + double const step = PI / maxh * spacing; + double const to_w = maxh * 2 / width; + double const pow_a_n = pow( rolloff, maxh ); + scale /= maxh * 2; + + double angle = (count / 2 - 1 + offset) * -step; + while ( count-- ) + { + *out++ = 0; + double w = angle * to_w; + if ( fabs( w ) < PI ) + { + double rolloff_cos_a = rolloff * cos( angle ); + double num = 1 - rolloff_cos_a - + pow_a_n * cos( maxh * angle ) + + pow_a_n * rolloff * cos( (maxh - 1) * angle ); + double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + double sinc = scale * num / den - scale; + + out [-1] = (short) (cos( w ) * sinc + sinc); + } + angle += step; + } +} + +Fir_Resampler_::Fir_Resampler_( int width, sample_t impulses_ [] ) : + width_( width ), + impulses( impulses_ ) +{ + imp = NULL; +} + +void Fir_Resampler_::clear_() +{ + imp = impulses; + Resampler::clear_(); +} + +blargg_err_t Fir_Resampler_::set_rate_( double new_factor ) +{ + double const rolloff = 0.999; + double const gain = 1.0; + + // determine number of sub-phases that yield lowest error + double ratio_ = 0.0; + int res = -1; + { + double least_error = 2; + double pos = 0; + for ( int r = 1; r <= max_res; r++ ) + { + pos += new_factor; + double nearest = floor( pos + 0.5 ); + double error = fabs( pos - nearest ); + if ( error < least_error ) + { + res = r; + ratio_ = nearest / res; + least_error = error; + } + } + } + RETURN_ERR( Resampler::set_rate_( ratio_ ) ); + + // how much of input is used for each output sample + int const step = stereo * (int) floor( ratio_ ); + double fraction = fmod( ratio_, 1.0 ); + + double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + //int input_per_cycle = 0; + sample_t* out = impulses; + for ( int n = res; --n >= 0; ) + { + gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, + double (0x7FFF * gain * filter), (int) width_, out ); + out += width_; + + int cur_step = step; + pos += fraction; + if ( pos >= 0.9999999 ) + { + pos -= 1.0; + cur_step += stereo; + } + + *out++ = (cur_step - width_ * 2 + 4) * sizeof (sample_t); + *out++ = 4 * sizeof (sample_t); + //input_per_cycle += cur_step; + } + // last offset moves back to beginning of impulses + out [-1] -= (char*) out - (char*) impulses; + + imp = impulses; + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Fir_Resampler.h b/Frameworks/GME/gme/Fir_Resampler.h index 2344493c1..491b6e898 100644 --- a/Frameworks/GME/gme/Fir_Resampler.h +++ b/Frameworks/GME/gme/Fir_Resampler.h @@ -1,101 +1,101 @@ -// Finite impulse response (FIR) resampler with adjustable FIR size - -// $package -#ifndef FIR_RESAMPLER_H -#define FIR_RESAMPLER_H - -#include "Resampler.h" - -template -class Fir_Resampler; - -// Use one of these typedefs -typedef Fir_Resampler< 8> Fir_Resampler_Fast; -typedef Fir_Resampler<16> Fir_Resampler_Norm; -typedef Fir_Resampler<24> Fir_Resampler_Good; - -// Implementation -class Fir_Resampler_ : public Resampler { -protected: - virtual blargg_err_t set_rate_( double ); - virtual void clear_(); - -protected: - enum { stereo = 2 }; - enum { max_res = 32 }; // TODO: eliminate and keep impulses on freestore? - sample_t const* imp; - int const width_; - sample_t* impulses; - - Fir_Resampler_( int width, sample_t [] ); -}; - -// Width is number of points in FIR. More points give better quality and -// rolloff effectiveness, and take longer to calculate. -template -class Fir_Resampler : public Fir_Resampler_ { - enum { min_width = (width < 4 ? 4 : width) }; - enum { adj_width = min_width / 4 * 4 + 2 }; - enum { write_offset = adj_width * stereo }; - short impulses [max_res * (adj_width + 2)]; -public: - Fir_Resampler() : Fir_Resampler_( adj_width, impulses ) { } - -protected: - virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); -}; - -template -Resampler::sample_t const* Fir_Resampler::resample_( sample_t** out_, - sample_t const* out_end, sample_t const in [], int in_size ) -{ - in_size -= write_offset; - if ( in_size > 0 ) - { - sample_t* BLARGG_RESTRICT out = *out_; - sample_t const* const in_end = in + in_size; - sample_t const* imp = this->imp; - - do - { - // accumulate in extended precision - int pt = imp [0]; - int l = pt * in [0]; - int r = pt * in [1]; - if ( out >= out_end ) - break; - for ( int n = (adj_width - 2) / 2; n; --n ) - { - pt = imp [1]; - l += pt * in [2]; - r += pt * in [3]; - - // pre-increment more efficient on some RISC processors - imp += 2; - pt = imp [0]; - r += pt * in [5]; - in += 4; - l += pt * in [0]; - } - pt = imp [1]; - l += pt * in [2]; - r += pt * in [3]; - - // these two "samples" after the end of the impulse give the - // proper offsets to the next input sample and next impulse - in = (sample_t const*) ((char const*) in + imp [2]); // some negative value - imp = (sample_t const*) ((char const*) imp + imp [3]); // small positive or large negative - - out [0] = sample_t (l >> 15); - out [1] = sample_t (r >> 15); - out += 2; - } - while ( in < in_end ); - - this->imp = imp; - *out_ = out; - } - return in; -} - -#endif +// Finite impulse response (FIR) resampler with adjustable FIR size + +// $package +#ifndef FIR_RESAMPLER_H +#define FIR_RESAMPLER_H + +#include "Resampler.h" + +template +class Fir_Resampler; + +// Use one of these typedefs +typedef Fir_Resampler< 8> Fir_Resampler_Fast; +typedef Fir_Resampler<16> Fir_Resampler_Norm; +typedef Fir_Resampler<24> Fir_Resampler_Good; + +// Implementation +class Fir_Resampler_ : public Resampler { +protected: + virtual blargg_err_t set_rate_( double ); + virtual void clear_(); + +protected: + enum { stereo = 2 }; + enum { max_res = 32 }; // TODO: eliminate and keep impulses on freestore? + sample_t const* imp; + int const width_; + sample_t* impulses; + + Fir_Resampler_( int width, sample_t [] ); +}; + +// Width is number of points in FIR. More points give better quality and +// rolloff effectiveness, and take longer to calculate. +template +class Fir_Resampler : public Fir_Resampler_ { + enum { min_width = (width < 4 ? 4 : width) }; + enum { adj_width = min_width / 4 * 4 + 2 }; + enum { write_offset = adj_width * stereo }; + short impulses [max_res * (adj_width + 2)]; +public: + Fir_Resampler() : Fir_Resampler_( adj_width, impulses ) { } + +protected: + virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); +}; + +template +Resampler::sample_t const* Fir_Resampler::resample_( sample_t** out_, + sample_t const* out_end, sample_t const in [], int in_size ) +{ + in_size -= write_offset; + if ( in_size > 0 ) + { + sample_t* BLARGG_RESTRICT out = *out_; + sample_t const* const in_end = in + in_size; + sample_t const* imp = this->imp; + + do + { + // accumulate in extended precision + int pt = imp [0]; + int l = pt * in [0]; + int r = pt * in [1]; + if ( out >= out_end ) + break; + for ( int n = (adj_width - 2) / 2; n; --n ) + { + pt = imp [1]; + l += pt * in [2]; + r += pt * in [3]; + + // pre-increment more efficient on some RISC processors + imp += 2; + pt = imp [0]; + r += pt * in [5]; + in += 4; + l += pt * in [0]; + } + pt = imp [1]; + l += pt * in [2]; + r += pt * in [3]; + + // these two "samples" after the end of the impulse give the + // proper offsets to the next input sample and next impulse + in = (sample_t const*) ((char const*) in + imp [2]); // some negative value + imp = (sample_t const*) ((char const*) imp + imp [3]); // small positive or large negative + + out [0] = sample_t (l >> 15); + out [1] = sample_t (r >> 15); + out += 2; + } + while ( in < in_end ); + + this->imp = imp; + *out_ = out; + } + return in; +} + +#endif diff --git a/Frameworks/GME/gme/Gb_Apu.cpp b/Frameworks/GME/gme/Gb_Apu.cpp index 35b77e574..ff5c1479c 100644 --- a/Frameworks/GME/gme/Gb_Apu.cpp +++ b/Frameworks/GME/gme/Gb_Apu.cpp @@ -1,407 +1,407 @@ -// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ - -#include "Gb_Apu.h" - -//#include "gb_apu_logger.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const vol_reg = 0xFF24; -int const stereo_reg = 0xFF25; -int const status_reg = 0xFF26; -int const wave_ram = 0xFF30; - -int const power_mask = 0x80; - -void Gb_Apu::treble_eq( blip_eq_t const& eq ) -{ - norm_synth.treble_eq( eq ); - fast_synth.treble_eq( eq ); -} - -inline int Gb_Apu::calc_output( int osc ) const -{ - int bits = regs [stereo_reg - io_addr] >> osc; - return (bits >> 3 & 2) | (bits & 1); -} - -void Gb_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) - require( !center || (center && !left && !right) || (center && left && right) ); - require( (unsigned) i < osc_count ); // fails if you pass invalid osc index - - if ( !center || !left || !right ) - { - left = center; - right = center; - } - - Gb_Osc& o = *oscs [i]; - o.outputs [1] = right; - o.outputs [2] = left; - o.outputs [3] = center; - o.output = o.outputs [calc_output( i )]; -} - -void Gb_Apu::synth_volume( int iv ) -{ - double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; - norm_synth.volume( v ); - fast_synth.volume( v ); -} - -void Gb_Apu::apply_volume() -{ - // TODO: Doesn't handle differing left and right volumes (panning). - // Not worth the complexity. - int data = regs [vol_reg - io_addr]; - int left = data >> 4 & 7; - int right = data & 7; - //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); - //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); - synth_volume( max( left, right ) + 1 ); -} - -void Gb_Apu::volume( double v ) -{ - if ( volume_ != v ) - { - volume_ = v; - apply_volume(); - } -} - -void Gb_Apu::reset_regs() -{ - for ( int i = 0; i < 0x20; i++ ) - regs [i] = 0; - - square1.reset(); - square2.reset(); - wave .reset(); - noise .reset(); - - apply_volume(); -} - -void Gb_Apu::reset_lengths() -{ - square1.length_ctr = 64; - square2.length_ctr = 64; - wave .length_ctr = 256; - noise .length_ctr = 64; -} - -void Gb_Apu::reduce_clicks( bool reduce ) -{ - reduce_clicks_ = reduce; - - // Click reduction makes DAC off generate same output as volume 0 - int dac_off_amp = 0; - if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks - dac_off_amp = -Gb_Osc::dac_bias; - - for ( int i = 0; i < osc_count; i++ ) - oscs [i]->dac_off_amp = dac_off_amp; - - // AGB always eliminates clicks on wave channel using same method - if ( wave.mode == mode_agb ) - wave.dac_off_amp = -Gb_Osc::dac_bias; -} - -void Gb_Apu::reset( mode_t mode, bool agb_wave ) -{ - // Hardware mode - if ( agb_wave ) - mode = mode_agb; // using AGB wave features implies AGB hardware - wave.agb_mask = agb_wave ? 0xFF : 0; - for ( int i = 0; i < osc_count; i++ ) - oscs [i]->mode = mode; - reduce_clicks( reduce_clicks_ ); - - // Reset state - frame_time = 0; - last_time = 0; - frame_phase = 0; - - reset_regs(); - reset_lengths(); - - // Load initial wave RAM - static byte const initial_wave [2] [16] = { - {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, - {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, - }; - for ( int b = 2; --b >= 0; ) - { - // Init both banks (does nothing if not in AGB mode) - // TODO: verify that this works - write_register( 0, 0xFF1A, b * 0x40 ); - for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) - write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); - } -} - -void Gb_Apu::set_tempo( double t ) -{ - frame_period = 4194304 / 512; // 512 Hz - if ( t != 1.0 ) - frame_period = t ? blip_time_t (frame_period / t) : blip_time_t(0); -} - -Gb_Apu::Gb_Apu() -{ - wave.wave_ram = ®s [wave_ram - io_addr]; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = &wave; - oscs [3] = &noise; - - for ( int i = osc_count; --i >= 0; ) - { - Gb_Osc& o = *oscs [i]; - o.regs = ®s [i * 5]; - o.output = NULL; - o.outputs [0] = NULL; - o.outputs [1] = NULL; - o.outputs [2] = NULL; - o.outputs [3] = NULL; - o.norm_synth = &norm_synth; - o.fast_synth = &fast_synth; - } - - reduce_clicks_ = false; - set_tempo( 1.0 ); - volume_ = 1.0; - reset(); -} - -void Gb_Apu::run_until_( blip_time_t end_time ) -{ - if ( !frame_period ) - frame_time += end_time - last_time; - - while ( true ) - { - // run oscillators - blip_time_t time = end_time; - if ( time > frame_time ) - time = frame_time; - - square1.run( last_time, time ); - square2.run( last_time, time ); - wave .run( last_time, time ); - noise .run( last_time, time ); - last_time = time; - - if ( time == end_time ) - break; - - // run frame sequencer - assert( frame_period ); - frame_time += frame_period * Gb_Osc::clk_mul; - switch ( frame_phase++ ) - { - case 2: - case 6: - // 128 Hz - square1.clock_sweep(); - case 0: - case 4: - // 256 Hz - square1.clock_length(); - square2.clock_length(); - wave .clock_length(); - noise .clock_length(); - break; - - case 7: - // 64 Hz - frame_phase = 0; - square1.clock_envelope(); - square2.clock_envelope(); - noise .clock_envelope(); - } - } -} - -inline void Gb_Apu::run_until( blip_time_t time ) -{ - require( time >= last_time ); // end_time must not be before previous time - if ( time > last_time ) - run_until_( time ); -} - -void Gb_Apu::end_frame( blip_time_t end_time ) -{ - #ifdef LOG_FRAME - LOG_FRAME( end_time ); - #endif - - if ( end_time > last_time ) - run_until( end_time ); - - frame_time -= end_time; - assert( frame_time >= 0 ); - - last_time -= end_time; - assert( last_time >= 0 ); -} - -void Gb_Apu::silence_osc( Gb_Osc& o ) -{ - int delta = -o.last_amp; - if ( reduce_clicks_ ) - delta += o.dac_off_amp; - - if ( delta ) - { - o.last_amp = o.dac_off_amp; - if ( o.output ) - { - o.output->set_modified(); - fast_synth.offset( last_time, delta, o.output ); - } - } -} - -void Gb_Apu::apply_stereo() -{ - for ( int i = osc_count; --i >= 0; ) - { - Gb_Osc& o = *oscs [i]; - Blip_Buffer* out = o.outputs [calc_output( i )]; - if ( o.output != out ) - { - silence_osc( o ); - o.output = out; - } - } -} - -void Gb_Apu::write_register( blip_time_t time, int addr, int data ) -{ - require( (unsigned) data < 0x100 ); - - int reg = addr - io_addr; - if ( (unsigned) reg >= io_size ) - { - require( false ); - return; - } - - #ifdef LOG_WRITE - LOG_WRITE( time, addr, data ); - #endif - - if ( addr < status_reg && !(regs [status_reg - io_addr] & power_mask) ) - { - // Power is off - - // length counters can only be written in DMG mode - if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) - return; - - if ( reg < 10 ) - data &= 0x3F; // clear square duty - } - - run_until( time ); - - if ( addr >= wave_ram ) - { - wave.write( addr, data ); - } - else - { - int old_data = regs [reg]; - regs [reg] = data; - - if ( addr < vol_reg ) - { - // Oscillator - write_osc( reg, old_data, data ); - } - else if ( addr == vol_reg && data != old_data ) - { - // Master volume - for ( int i = osc_count; --i >= 0; ) - silence_osc( *oscs [i] ); - - apply_volume(); - } - else if ( addr == stereo_reg ) - { - // Stereo panning - apply_stereo(); - } - else if ( addr == status_reg && (data ^ old_data) & power_mask ) - { - // Power control - frame_phase = 0; - for ( int i = osc_count; --i >= 0; ) - silence_osc( *oscs [i] ); - - reset_regs(); - if ( wave.mode != mode_dmg ) - reset_lengths(); - - regs [status_reg - io_addr] = data; - } - } -} - -int Gb_Apu::read_register( blip_time_t time, int addr ) -{ - if ( addr >= status_reg ) - run_until( time ); - - int reg = addr - io_addr; - if ( (unsigned) reg >= io_size ) - { - require( false ); - return 0; - } - - if ( addr >= wave_ram ) - return wave.read( addr ); - - // Value read back has some bits always set - static byte const masks [] = { - 0x80,0x3F,0x00,0xFF,0xBF, - 0xFF,0x3F,0x00,0xFF,0xBF, - 0x7F,0xFF,0x9F,0xFF,0xBF, - 0xFF,0xFF,0x00,0x00,0xBF, - 0x00,0x00,0x70, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF - }; - int mask = masks [reg]; - if ( wave.agb_mask && (reg == 10 || reg == 12) ) - mask = 0x1F; // extra implemented bits in wave regs on AGB - int data = regs [reg] | mask; - - // Status register - if ( addr == status_reg ) - { - data &= 0xF0; - data |= (int) square1.enabled << 0; - data |= (int) square2.enabled << 1; - data |= (int) wave .enabled << 2; - data |= (int) noise .enabled << 3; - } - - return data; -} +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +//#include "gb_apu_logger.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const vol_reg = 0xFF24; +int const stereo_reg = 0xFF25; +int const status_reg = 0xFF26; +int const wave_ram = 0xFF30; + +int const power_mask = 0x80; + +void Gb_Apu::treble_eq( blip_eq_t const& eq ) +{ + norm_synth.treble_eq( eq ); + fast_synth.treble_eq( eq ); +} + +inline int Gb_Apu::calc_output( int osc ) const +{ + int bits = regs [stereo_reg - io_addr] >> osc; + return (bits >> 3 & 2) | (bits & 1); +} + +void Gb_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Gb_Osc& o = *oscs [i]; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; +} + +void Gb_Apu::synth_volume( int iv ) +{ + double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; + norm_synth.volume( v ); + fast_synth.volume( v ); +} + +void Gb_Apu::apply_volume() +{ + // TODO: Doesn't handle differing left and right volumes (panning). + // Not worth the complexity. + int data = regs [vol_reg - io_addr]; + int left = data >> 4 & 7; + int right = data & 7; + //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); + //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); + synth_volume( max( left, right ) + 1 ); +} + +void Gb_Apu::volume( double v ) +{ + if ( volume_ != v ) + { + volume_ = v; + apply_volume(); + } +} + +void Gb_Apu::reset_regs() +{ + for ( int i = 0; i < 0x20; i++ ) + regs [i] = 0; + + square1.reset(); + square2.reset(); + wave .reset(); + noise .reset(); + + apply_volume(); +} + +void Gb_Apu::reset_lengths() +{ + square1.length_ctr = 64; + square2.length_ctr = 64; + wave .length_ctr = 256; + noise .length_ctr = 64; +} + +void Gb_Apu::reduce_clicks( bool reduce ) +{ + reduce_clicks_ = reduce; + + // Click reduction makes DAC off generate same output as volume 0 + int dac_off_amp = 0; + if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks + dac_off_amp = -Gb_Osc::dac_bias; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->dac_off_amp = dac_off_amp; + + // AGB always eliminates clicks on wave channel using same method + if ( wave.mode == mode_agb ) + wave.dac_off_amp = -Gb_Osc::dac_bias; +} + +void Gb_Apu::reset( mode_t mode, bool agb_wave ) +{ + // Hardware mode + if ( agb_wave ) + mode = mode_agb; // using AGB wave features implies AGB hardware + wave.agb_mask = agb_wave ? 0xFF : 0; + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->mode = mode; + reduce_clicks( reduce_clicks_ ); + + // Reset state + frame_time = 0; + last_time = 0; + frame_phase = 0; + + reset_regs(); + reset_lengths(); + + // Load initial wave RAM + static byte const initial_wave [2] [16] = { + {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, + {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, + }; + for ( int b = 2; --b >= 0; ) + { + // Init both banks (does nothing if not in AGB mode) + // TODO: verify that this works + write_register( 0, 0xFF1A, b * 0x40 ); + for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) + write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); + } +} + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 512; // 512 Hz + if ( t != 1.0 ) + frame_period = t ? blip_time_t (frame_period / t) : blip_time_t(0); +} + +Gb_Apu::Gb_Apu() +{ + wave.wave_ram = ®s [wave_ram - io_addr]; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = &wave; + oscs [3] = &noise; + + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + o.regs = ®s [i * 5]; + o.output = NULL; + o.outputs [0] = NULL; + o.outputs [1] = NULL; + o.outputs [2] = NULL; + o.outputs [3] = NULL; + o.norm_synth = &norm_synth; + o.fast_synth = &fast_synth; + } + + reduce_clicks_ = false; + set_tempo( 1.0 ); + volume_ = 1.0; + reset(); +} + +void Gb_Apu::run_until_( blip_time_t end_time ) +{ + if ( !frame_period ) + frame_time += end_time - last_time; + + while ( true ) + { + // run oscillators + blip_time_t time = end_time; + if ( time > frame_time ) + time = frame_time; + + square1.run( last_time, time ); + square2.run( last_time, time ); + wave .run( last_time, time ); + noise .run( last_time, time ); + last_time = time; + + if ( time == end_time ) + break; + + // run frame sequencer + assert( frame_period ); + frame_time += frame_period * Gb_Osc::clk_mul; + switch ( frame_phase++ ) + { + case 2: + case 6: + // 128 Hz + square1.clock_sweep(); + case 0: + case 4: + // 256 Hz + square1.clock_length(); + square2.clock_length(); + wave .clock_length(); + noise .clock_length(); + break; + + case 7: + // 64 Hz + frame_phase = 0; + square1.clock_envelope(); + square2.clock_envelope(); + noise .clock_envelope(); + } + } +} + +inline void Gb_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); // end_time must not be before previous time + if ( time > last_time ) + run_until_( time ); +} + +void Gb_Apu::end_frame( blip_time_t end_time ) +{ + #ifdef LOG_FRAME + LOG_FRAME( end_time ); + #endif + + if ( end_time > last_time ) + run_until( end_time ); + + frame_time -= end_time; + assert( frame_time >= 0 ); + + last_time -= end_time; + assert( last_time >= 0 ); +} + +void Gb_Apu::silence_osc( Gb_Osc& o ) +{ + int delta = -o.last_amp; + if ( reduce_clicks_ ) + delta += o.dac_off_amp; + + if ( delta ) + { + o.last_amp = o.dac_off_amp; + if ( o.output ) + { + o.output->set_modified(); + fast_synth.offset( last_time, delta, o.output ); + } + } +} + +void Gb_Apu::apply_stereo() +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + Blip_Buffer* out = o.outputs [calc_output( i )]; + if ( o.output != out ) + { + silence_osc( o ); + o.output = out; + } + } +} + +void Gb_Apu::write_register( blip_time_t time, int addr, int data ) +{ + require( (unsigned) data < 0x100 ); + + int reg = addr - io_addr; + if ( (unsigned) reg >= io_size ) + { + require( false ); + return; + } + + #ifdef LOG_WRITE + LOG_WRITE( time, addr, data ); + #endif + + if ( addr < status_reg && !(regs [status_reg - io_addr] & power_mask) ) + { + // Power is off + + // length counters can only be written in DMG mode + if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) + return; + + if ( reg < 10 ) + data &= 0x3F; // clear square duty + } + + run_until( time ); + + if ( addr >= wave_ram ) + { + wave.write( addr, data ); + } + else + { + int old_data = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) + { + // Oscillator + write_osc( reg, old_data, data ); + } + else if ( addr == vol_reg && data != old_data ) + { + // Master volume + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + apply_volume(); + } + else if ( addr == stereo_reg ) + { + // Stereo panning + apply_stereo(); + } + else if ( addr == status_reg && (data ^ old_data) & power_mask ) + { + // Power control + frame_phase = 0; + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + reset_regs(); + if ( wave.mode != mode_dmg ) + reset_lengths(); + + regs [status_reg - io_addr] = data; + } + } +} + +int Gb_Apu::read_register( blip_time_t time, int addr ) +{ + if ( addr >= status_reg ) + run_until( time ); + + int reg = addr - io_addr; + if ( (unsigned) reg >= io_size ) + { + require( false ); + return 0; + } + + if ( addr >= wave_ram ) + return wave.read( addr ); + + // Value read back has some bits always set + static byte const masks [] = { + 0x80,0x3F,0x00,0xFF,0xBF, + 0xFF,0x3F,0x00,0xFF,0xBF, + 0x7F,0xFF,0x9F,0xFF,0xBF, + 0xFF,0xFF,0x00,0x00,0xBF, + 0x00,0x00,0x70, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + int mask = masks [reg]; + if ( wave.agb_mask && (reg == 10 || reg == 12) ) + mask = 0x1F; // extra implemented bits in wave regs on AGB + int data = regs [reg] | mask; + + // Status register + if ( addr == status_reg ) + { + data &= 0xF0; + data |= (int) square1.enabled << 0; + data |= (int) square2.enabled << 1; + data |= (int) wave .enabled << 2; + data |= (int) noise .enabled << 3; + } + + return data; +} diff --git a/Frameworks/GME/gme/Gb_Apu.h b/Frameworks/GME/gme/Gb_Apu.h index ebc72687c..3b0596ac3 100644 --- a/Frameworks/GME/gme/Gb_Apu.h +++ b/Frameworks/GME/gme/Gb_Apu.h @@ -1,193 +1,193 @@ -// Nintendo Game Boy sound hardware emulator with save state support - -// Gb_Snd_Emu $vers -#ifndef GB_APU_H -#define GB_APU_H - -#include "Gb_Oscs.h" - -struct gb_apu_state_t; - -class Gb_Apu { -public: -// Basics - - // Sets buffer(s) to generate sound into, or NULL to mute. If only center is not NULL, - // output is mono. - void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Emulates to time t, then writes data to addr - void write_register( blip_time_t t, int addr, int data ); - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Clock rate sound hardware runs at - enum { clock_rate = 4194304 * GB_APU_OVERCLOCK }; - - // Registers are at io_addr to io_addr+io_size-1 - enum { io_addr = 0xFF10 }; - enum { io_size = 0x30 }; - - // Emulates to time t, then reads from addr - int read_register( blip_time_t t, int addr ); - - // Resets hardware to state after power, BEFORE boot ROM runs. Mode selects - // sound hardware. If agb_wave is true, enables AGB's extra wave features. - enum mode_t { - mode_dmg, // Game Boy monochrome - mode_cgb, // Game Boy Color - mode_agb // Game Boy Advance - }; - void reset( mode_t mode = mode_cgb, bool agb_wave = false ); - - // Same as set_output(), but for a particular channel - // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise - enum { osc_count = 4 }; // 0 <= chan < osc_count - void set_output( int chan, Blip_Buffer* center, - Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Sets overall volume, where 1.0 is normal - void volume( double ); - - // Sets treble equalization - void treble_eq( blip_eq_t const& ); - - // Treble and bass values for various hardware. - enum { - speaker_treble = -47, // speaker on system - speaker_bass = 2000, - dmg_treble = 0, // headphones on each system - dmg_bass = 30, - cgb_treble = 0, - cgb_bass = 300, // CGB has much less bass - agb_treble = 0, - agb_bass = 30 - }; - - // If true, reduces clicking by disabling DAC biasing. Note that this reduces - // emulation accuracy, since the clicks are authentic. - void reduce_clicks( bool reduce = true ); - - // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the - // tempo in a music player. - void set_tempo( double ); - - // Saves full emulation state to state_out. Data format is portable and - // includes some extra space to avoid expansion in case more state needs - // to be stored in the future. - void save_state( gb_apu_state_t* state_out ); - - // Loads state. You should call reset() BEFORE this. - blargg_err_t load_state( gb_apu_state_t const& in ); - -private: - // noncopyable - Gb_Apu( const Gb_Apu& ); - Gb_Apu& operator = ( const Gb_Apu& ); - -// Implementation -public: - Gb_Apu(); - - // Use set_output() in place of these - BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) - BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ); ) - BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) - BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) - - BLARGG_DEPRECATED_TEXT( enum { start_addr = 0xFF10 }; ) - BLARGG_DEPRECATED_TEXT( enum { end_addr = 0xFF3F }; ) - BLARGG_DEPRECATED_TEXT( enum { register_count = end_addr - start_addr + 1 }; ) - -private: - Gb_Osc* oscs [osc_count]; - blip_time_t last_time; // time sound emulator has been run to - blip_time_t frame_period; // clocks between each frame sequencer step - double volume_; - bool reduce_clicks_; - - Gb_Sweep_Square square1; - Gb_Square square2; - Gb_Wave wave; - Gb_Noise noise; - blip_time_t frame_time; // time of next frame sequencer action - int frame_phase; // phase of next frame sequencer step - enum { regs_size = io_size + 0x10 }; - BOOST::uint8_t regs [regs_size];// last values written to registers - - // large objects after everything else - Blip_Synth_Norm norm_synth; - Blip_Synth_Fast fast_synth; - - void reset_lengths(); - void reset_regs(); - int calc_output( int osc ) const; - void apply_stereo(); - void apply_volume(); - void synth_volume( int ); - void run_until_( blip_time_t ); - void run_until( blip_time_t ); - void silence_osc( Gb_Osc& ); - void write_osc( int reg, int old_data, int data ); - const char* save_load( gb_apu_state_t*, bool save ); - void save_load2( gb_apu_state_t*, bool save ); - friend class Gb_Apu2; -}; - -// Format of save state. Should be stable across versions of the library, -// with earlier versions properly opening later save states. Includes some -// room for expansion so the state size shouldn't increase. -struct gb_apu_state_t -{ -#if GB_APU_CUSTOM_STATE - // Values stored as plain int so your code can read/write them easily. - // Structure can NOT be written to disk, since format is not portable. - typedef int val_t; -#else - // Values written in portable little-endian format, allowing structure - // to be written directly to disk. - typedef unsigned char val_t [4]; -#endif - - enum { format0 = 0x50414247 }; // 'GBAP' - - val_t format; // format of all following data - val_t version; // later versions just add fields to end - - unsigned char regs [0x40]; - val_t frame_time; - val_t frame_phase; - - val_t sweep_freq; - val_t sweep_delay; - val_t sweep_enabled; - val_t sweep_neg; - val_t noise_divider; - val_t wave_buf; - - val_t delay [4]; - val_t length_ctr [4]; - val_t phase [4]; - val_t enabled [4]; - - val_t env_delay [3]; - val_t env_volume [3]; - val_t env_enabled [3]; - - val_t unused [13]; // for future expansion -}; - -inline void Gb_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - for ( int i = osc_count; --i >= 0; ) - set_output( i, c, l, r ); -} - -BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c ) { set_output( c, c, c ); } ) -BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) - -#endif +// Nintendo Game Boy sound hardware emulator with save state support + +// Gb_Snd_Emu $vers +#ifndef GB_APU_H +#define GB_APU_H + +#include "Gb_Oscs.h" + +struct gb_apu_state_t; + +class Gb_Apu { +public: +// Basics + + // Sets buffer(s) to generate sound into, or NULL to mute. If only center is not NULL, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to addr + void write_register( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Clock rate sound hardware runs at + enum { clock_rate = 4194304 * GB_APU_OVERCLOCK }; + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0xFF10 }; + enum { io_size = 0x30 }; + + // Emulates to time t, then reads from addr + int read_register( blip_time_t t, int addr ); + + // Resets hardware to state after power, BEFORE boot ROM runs. Mode selects + // sound hardware. If agb_wave is true, enables AGB's extra wave features. + enum mode_t { + mode_dmg, // Game Boy monochrome + mode_cgb, // Game Boy Color + mode_agb // Game Boy Advance + }; + void reset( mode_t mode = mode_cgb, bool agb_wave = false ); + + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise + enum { osc_count = 4 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, + Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Sets overall volume, where 1.0 is normal + void volume( double ); + + // Sets treble equalization + void treble_eq( blip_eq_t const& ); + + // Treble and bass values for various hardware. + enum { + speaker_treble = -47, // speaker on system + speaker_bass = 2000, + dmg_treble = 0, // headphones on each system + dmg_bass = 30, + cgb_treble = 0, + cgb_bass = 300, // CGB has much less bass + agb_treble = 0, + agb_bass = 30 + }; + + // If true, reduces clicking by disabling DAC biasing. Note that this reduces + // emulation accuracy, since the clicks are authentic. + void reduce_clicks( bool reduce = true ); + + // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the + // tempo in a music player. + void set_tempo( double ); + + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( gb_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( gb_apu_state_t const& in ); + +private: + // noncopyable + Gb_Apu( const Gb_Apu& ); + Gb_Apu& operator = ( const Gb_Apu& ); + +// Implementation +public: + Gb_Apu(); + + // Use set_output() in place of these + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) + BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ); ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0xFF10 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0xFF3F }; ) + BLARGG_DEPRECATED_TEXT( enum { register_count = end_addr - start_addr + 1 }; ) + +private: + Gb_Osc* oscs [osc_count]; + blip_time_t last_time; // time sound emulator has been run to + blip_time_t frame_period; // clocks between each frame sequencer step + double volume_; + bool reduce_clicks_; + + Gb_Sweep_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + blip_time_t frame_time; // time of next frame sequencer action + int frame_phase; // phase of next frame sequencer step + enum { regs_size = io_size + 0x10 }; + BOOST::uint8_t regs [regs_size];// last values written to registers + + // large objects after everything else + Blip_Synth_Norm norm_synth; + Blip_Synth_Fast fast_synth; + + void reset_lengths(); + void reset_regs(); + int calc_output( int osc ) const; + void apply_stereo(); + void apply_volume(); + void synth_volume( int ); + void run_until_( blip_time_t ); + void run_until( blip_time_t ); + void silence_osc( Gb_Osc& ); + void write_osc( int reg, int old_data, int data ); + const char* save_load( gb_apu_state_t*, bool save ); + void save_load2( gb_apu_state_t*, bool save ); + friend class Gb_Apu2; +}; + +// Format of save state. Should be stable across versions of the library, +// with earlier versions properly opening later save states. Includes some +// room for expansion so the state size shouldn't increase. +struct gb_apu_state_t +{ +#if GB_APU_CUSTOM_STATE + // Values stored as plain int so your code can read/write them easily. + // Structure can NOT be written to disk, since format is not portable. + typedef int val_t; +#else + // Values written in portable little-endian format, allowing structure + // to be written directly to disk. + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414247 }; // 'GBAP' + + val_t format; // format of all following data + val_t version; // later versions just add fields to end + + unsigned char regs [0x40]; + val_t frame_time; + val_t frame_phase; + + val_t sweep_freq; + val_t sweep_delay; + val_t sweep_enabled; + val_t sweep_neg; + val_t noise_divider; + val_t wave_buf; + + val_t delay [4]; + val_t length_ctr [4]; + val_t phase [4]; + val_t enabled [4]; + + val_t env_delay [3]; + val_t env_volume [3]; + val_t env_enabled [3]; + + val_t unused [13]; // for future expansion +}; + +inline void Gb_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} + +BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c ) { set_output( c, c, c ); } ) +BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) + +#endif diff --git a/Frameworks/GME/gme/Gb_Cpu.cpp b/Frameworks/GME/gme/Gb_Cpu.cpp index c8b7005a0..a88416b16 100644 --- a/Frameworks/GME/gme/Gb_Cpu.cpp +++ b/Frameworks/GME/gme/Gb_Cpu.cpp @@ -1,51 +1,51 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Gb_Cpu.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -inline void Gb_Cpu::set_code_page( int i, void* p ) -{ - byte* p2 = STATIC_CAST(byte*,p) - GB_CPU_OFFSET( i * page_size ); - cpu_state_.code_map [i] = p2; - cpu_state->code_map [i] = p2; -} - -void Gb_Cpu::reset( void* unmapped ) -{ - check( cpu_state == &cpu_state_ ); - cpu_state = &cpu_state_; - - cpu_state_.time = 0; - - for ( int i = 0; i < page_count + 1; ++i ) - set_code_page( i, unmapped ); - - memset( &r, 0, sizeof r ); - - blargg_verify_byte_order(); -} - -void Gb_Cpu::map_code( addr_t start, int size, void* data ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= mem_size ); - - for ( int offset = 0; offset < size; offset += page_size ) - set_code_page( (start + offset) >> page_bits, STATIC_CAST(char*,data) + offset ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gb_Cpu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +inline void Gb_Cpu::set_code_page( int i, void* p ) +{ + byte* p2 = STATIC_CAST(byte*,p) - GB_CPU_OFFSET( i * page_size ); + cpu_state_.code_map [i] = p2; + cpu_state->code_map [i] = p2; +} + +void Gb_Cpu::reset( void* unmapped ) +{ + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; + + cpu_state_.time = 0; + + for ( int i = 0; i < page_count + 1; ++i ) + set_code_page( i, unmapped ); + + memset( &r, 0, sizeof r ); + + blargg_verify_byte_order(); +} + +void Gb_Cpu::map_code( addr_t start, int size, void* data ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= mem_size ); + + for ( int offset = 0; offset < size; offset += page_size ) + set_code_page( (start + offset) >> page_bits, STATIC_CAST(char*,data) + offset ); +} diff --git a/Frameworks/GME/gme/Gb_Cpu.h b/Frameworks/GME/gme/Gb_Cpu.h index eb9fc81a1..f8902bc77 100644 --- a/Frameworks/GME/gme/Gb_Cpu.h +++ b/Frameworks/GME/gme/Gb_Cpu.h @@ -1,82 +1,82 @@ -// Nintendo Game Boy CPU emulator - -// Game_Music_Emu $vers -#ifndef GB_CPU_H -#define GB_CPU_H - -#include "blargg_common.h" - -class Gb_Cpu { -public: - typedef int addr_t; - typedef BOOST::uint8_t byte; - - enum { mem_size = 0x10000 }; - - // Clears registers and map all pages to unmapped - void reset( void* unmapped = NULL ); - - // Maps code memory (memory accessed via the program counter). Start and size - // must be multiple of page_size. - enum { page_bits = 13 }; - enum { page_size = 1 << page_bits }; - void map_code( addr_t start, int size, void* code ); - - // Accesses emulated memory as CPU does - byte* get_code( addr_t ); - - // Game Boy Z-80 registers. NOT kept updated during emulation. - struct core_regs_t { - BOOST::uint16_t bc, de, hl, fa; - }; - - struct registers_t : core_regs_t { - int pc; // more than 16 bits to allow overflow detection - BOOST::uint16_t sp; - }; - registers_t r; - - // Base address for RST vectors, to simplify GBS player (normally 0) - addr_t rst_base; - - // Current time. - int time() const { return cpu_state->time; } - - // Changes time. Must not be called during emulation. - // Should be negative, because emulation stops once it becomes >= 0. - void set_time( int t ) { cpu_state->time = t; } - - // Emulator reads this many bytes past end of a page - enum { cpu_padding = 8 }; - - -// Implementation -public: - Gb_Cpu() : rst_base( 0 ) { cpu_state = &cpu_state_; } - enum { page_count = mem_size >> page_bits }; - - struct cpu_state_t { - byte* code_map [page_count + 1]; - int time; - }; - cpu_state_t* cpu_state; // points to state_ or a local copy within run() - cpu_state_t cpu_state_; - -private: - void set_code_page( int, void* ); -}; - -#define GB_CPU_PAGE( addr ) ((unsigned) (addr) >> Gb_Cpu::page_bits) - -#if BLARGG_NONPORTABLE - #define GB_CPU_OFFSET( addr ) (addr) -#else - #define GB_CPU_OFFSET( addr ) ((addr) & (Gb_Cpu::page_size - 1)) -#endif - -inline BOOST::uint8_t* Gb_Cpu::get_code( addr_t addr ) -{ - return cpu_state_.code_map [GB_CPU_PAGE( addr )] + GB_CPU_OFFSET( addr ); -} - -#endif +// Nintendo Game Boy CPU emulator + +// Game_Music_Emu $vers +#ifndef GB_CPU_H +#define GB_CPU_H + +#include "blargg_common.h" + +class Gb_Cpu { +public: + typedef int addr_t; + typedef BOOST::uint8_t byte; + + enum { mem_size = 0x10000 }; + + // Clears registers and map all pages to unmapped + void reset( void* unmapped = NULL ); + + // Maps code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. + enum { page_bits = 13 }; + enum { page_size = 1 << page_bits }; + void map_code( addr_t start, int size, void* code ); + + // Accesses emulated memory as CPU does + byte* get_code( addr_t ); + + // Game Boy Z-80 registers. NOT kept updated during emulation. + struct core_regs_t { + BOOST::uint16_t bc, de, hl, fa; + }; + + struct registers_t : core_regs_t { + int pc; // more than 16 bits to allow overflow detection + BOOST::uint16_t sp; + }; + registers_t r; + + // Base address for RST vectors, to simplify GBS player (normally 0) + addr_t rst_base; + + // Current time. + int time() const { return cpu_state->time; } + + // Changes time. Must not be called during emulation. + // Should be negative, because emulation stops once it becomes >= 0. + void set_time( int t ) { cpu_state->time = t; } + + // Emulator reads this many bytes past end of a page + enum { cpu_padding = 8 }; + + +// Implementation +public: + Gb_Cpu() : rst_base( 0 ) { cpu_state = &cpu_state_; } + enum { page_count = mem_size >> page_bits }; + + struct cpu_state_t { + byte* code_map [page_count + 1]; + int time; + }; + cpu_state_t* cpu_state; // points to state_ or a local copy within run() + cpu_state_t cpu_state_; + +private: + void set_code_page( int, void* ); +}; + +#define GB_CPU_PAGE( addr ) ((unsigned) (addr) >> Gb_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define GB_CPU_OFFSET( addr ) (addr) +#else + #define GB_CPU_OFFSET( addr ) ((addr) & (Gb_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t* Gb_Cpu::get_code( addr_t addr ) +{ + return cpu_state_.code_map [GB_CPU_PAGE( addr )] + GB_CPU_OFFSET( addr ); +} + +#endif diff --git a/Frameworks/GME/gme/Gb_Cpu_run.h b/Frameworks/GME/gme/Gb_Cpu_run.h index 69ac8f68f..fc845e14d 100644 --- a/Frameworks/GME/gme/Gb_Cpu_run.h +++ b/Frameworks/GME/gme/Gb_Cpu_run.h @@ -1,1183 +1,1183 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#if 0 -/* Define these macros in the source file before #including this file. -- Parameters might be expressions, so they are best evaluated only once, -though they NEVER have side-effects, so multiple evaluation is OK. -- Output parameters might be a multiple-assignment expression like "a=x", -so they must NOT be parenthesized. -- Macros "returning" void may use a {} statement block. */ - - // 0 <= addr <= 0xFFFF + page_size - // time functions can be used - int READ_MEM( addr_t ); - void WRITE_MEM( addr_t, int data ); - - // Access of 0xFF00 + offset - // 0 <= offset <= 0xFF - int READ_IO( int offset ); - void WRITE_IO( int offset, int data ); - - // Often-used instructions use this instead of READ_MEM - void READ_FAST( addr_t, int& out ); - -// The following can be used within macros: - - // Current time - cpu_time_t TIME(); -#endif - -/* Copyright (C) 2003-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -// Common instructions: -// -// 365880 FA LD A,(nn) -// 355863 20 JR NZ -// 313655 21 LD HL,nn -// 274580 28 JR Z -// 252878 FE CP n -// 230541 7E LD A,(HL) -// 226209 2A LD A,(HL+) -// 217467 CD CALL -// 212034 C9 RET -// 208376 CB CB prefix -// -// 27486 CB 7E BIT 7,(HL) -// 15925 CB 76 BIT 6,(HL) -// 13035 CB 19 RR C -// 11557 CB 7F BIT 7,A -// 10898 CB 37 SWAP A -// 10208 CB 66 BIT 4,(HL) - -// Allows MWCW debugger to step through code properly -#ifdef CPU_BEGIN - CPU_BEGIN -#endif - -#define TIME() s.time - -#define CODE_PAGE( addr ) s.code_map [GB_CPU_PAGE( addr )] -#define READ_CODE( addr ) (CODE_PAGE( addr ) [GB_CPU_OFFSET( addr )]) - -// Flags with hex value for clarity when used as mask. -// Stored in indicated variable during emulation. -int const z80 = 0x80; // cz -int const n40 = 0x40; // ph -int const h20 = 0x20; // ph -int const c10 = 0x10; // cz - -#define SET_FLAGS( in )\ -{\ - cz = ((in) << 4 & 0x100) + (~(in) >> 7 & 1);\ - ph = (~(in) << 2 & 0x100) + ((in) >> 1 & 0x10);\ -} - -// random bits in cz to catch misuse of them -#define SET_FLAGS_DEBUG( in )\ -{\ - cz = ((in) << 4 & 0x100) | (rand() & ~0x1FF) | ((in) & 0x80 ? 0 : (rand() & 0xFF) | 1);\ - ph = (~(in) << 2 & 0x100) | (((in) >> 1 & 0x10) ^ BYTE( cz ));\ -} - -#define GET_FLAGS( out )\ -{\ - out = (cz >> 4 & c10);\ - out += ~ph >> 2 & n40;\ - out += (ph ^ cz) << 1 & h20;\ - if ( !BYTE( cz ) )\ - out += z80;\ -} - -#define CC_NZ() ( BYTE( cz )) -#define CC_Z() (!BYTE( cz )) -#define CC_NC() (!(cz & 0x100)) -#define CC_C() ( cz & 0x100 ) - -// Truncation -#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */ -#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */ -#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */ - -{ - Gb_Cpu::cpu_state_t s; - CPU.cpu_state = &s; - memcpy( &s, &CPU.cpu_state_, sizeof s ); - - union { - struct { - #if BLARGG_BIG_ENDIAN - byte b, c, d, e, h, l, flags, a; - #else - byte c, b, e, d, l, h, a, flags; - #endif - } rg; // individual registers - Gb_Cpu::core_regs_t rp; // pairs - - byte r8_ [8]; // indexed registers (use R8 macro due to endian dependence) - BOOST::uint16_t r16 [4]; // indexed pairs - }; - BLARGG_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); - - #if BLARGG_BIG_ENDIAN - #define R8( n ) (r8_ [n]) - #elif BLARGG_LITTLE_ENDIAN - #define R8( n ) (r8_ [(n) ^ 1]) - #else - // Be sure "blargg_endian.h" has been #included in the file that #includes this - #error "Byte order of CPU must be known" - #endif - - rp = CPU.r; - int pc = CPU.r.pc; - int sp = CPU.r.sp; - int ph; - int cz; - SET_FLAGS( rg.flags ); - - int time = s.time; - -loop: - - check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around - check( (unsigned) sp < 0x10000 ); - - byte const* instr = CODE_PAGE( pc ); - int op; - - if ( GB_CPU_OFFSET(~0) == ~0 ) - { - op = instr [pc]; - pc++; - instr += pc; - } - else - { - instr += GB_CPU_OFFSET( pc ); - op = *instr++; - pc++; - } - -#define GET_ADDR() GET_LE16( instr ) - - static byte const instr_times [256*2] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,// 0 - 4,12, 8, 8, 4, 4, 8, 4,12, 8, 8, 8, 4, 4, 8, 4,// 1 - 8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 2 - 8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 3 - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 4 - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 5 - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 6 - 8, 8, 8, 8, 8, 8, 0, 8, 4, 4, 4, 4, 4, 4, 8, 4,// 7 - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 8 - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 9 - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// A - 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// B - 8,12,16,16,12,16, 8,16, 8,16,16, 0,12,24, 8,16,// C - 8,12,16, 0,12,16, 8,16, 8,16,16, 0,12, 0, 8,16,// D - 12,12, 8, 0, 0,16, 8,16,16, 4,16, 0, 0, 0, 8,16,// E - 12,12, 8, 4, 0,16, 8,16,12, 8,16, 4, 0, 0, 8,16,// F - - // CB prefixed - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 0 - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 1 - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 2 - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 3 - 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 4 - 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 5 - 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 6 - 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 7 - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 8 - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 9 - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// A - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// B - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// C - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// D - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// E - 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// F - }; - - if ( time >= 0 ) - goto stop; - - time += instr_times [op]; - - int data; - data = *instr; - s.time = time; - - #ifdef CPU_INSTR_HOOK - { CPU_INSTR_HOOK( (pc-1), (instr-1), rg.a, rp.bc, rp.de, rp.hl, sp ); } - #endif - - switch ( op ) - { - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH_( cond, clocks )\ -{\ - pc++;\ - if ( !(cond) )\ - goto loop;\ - pc = WORD( pc + SBYTE( data ) );\ - time += clocks;\ - goto loop;\ -} - -#define BRANCH( cond ) BRANCH_( cond, 4 ) - -// Most Common - - case 0x20: // JR NZ - BRANCH( CC_NZ() ) - - case 0x21: // LD HL,IMM (common) - rp.hl = GET_ADDR(); - pc += 2; - goto loop; - - case 0x28: // JR Z - BRANCH( CC_Z() ) - - case 0xF2: // LD A,(0xFF00+C) - READ_IO( rg.c, rg.a ); - goto loop; - - case 0xF0: // LD A,(0xFF00+imm) - pc++; - READ_IO( data, rg.a ); - goto loop; - - { - int temp; - case 0x0A: // LD A,(BC) - temp = rp.bc; - goto ld_a_ind_comm; - - case 0x3A: // LD A,(HL-) - temp = rp.hl; - rp.hl = temp - 1; - goto ld_a_ind_comm; - - case 0x1A: // LD A,(DE) - temp = rp.de; - goto ld_a_ind_comm; - - case 0x2A: // LD A,(HL+) (common) - temp = rp.hl; - rp.hl = temp + 1; - goto ld_a_ind_comm; - - case 0xFA: // LD A,IND16 (common) - temp = GET_ADDR(); - pc += 2; - ld_a_ind_comm: - READ_FAST( temp, rg.a ); - goto loop; - } - - { - int temp; - case 0xBE: // CP (HL) - temp = READ_MEM( rp.hl ); - goto cmp_comm; - - case 0xB8: // CP B - case 0xB9: // CP C - case 0xBA: // CP D - case 0xBB: // CP E - case 0xBC: // CP H - case 0xBD: // CP L - case 0xBF: // CP A - temp = R8( op & 7 ); - cmp_comm: - ph = rg.a ^ temp; // N=1 H=* - cz = rg.a - temp; // C=* Z=* - goto loop; - } - - case 0xFE: // CP IMM - pc++; - ph = rg.a ^ data; // N=1 H=* - cz = rg.a - data; // C=* Z=* - goto loop; - - case 0x46: // LD B,(HL) - case 0x4E: // LD C,(HL) - case 0x56: // LD D,(HL) - case 0x5E: // LD E,(HL) - case 0x66: // LD H,(HL) - case 0x6E: // LD L,(HL) - case 0x7E:{// LD A,(HL) - int addr = rp.hl; - READ_FAST( addr, R8( op >> 3 & 7 ) ); - goto loop; - } - - case 0xC4: // CNZ (next-most-common) - pc += 2; - if ( CC_Z() ) - goto loop; - call: - time += 12; - pc -= 2; - case 0xCD: // CALL (most-common) - data = pc + 2; - pc = GET_ADDR(); - push: { - int addr = WORD( sp - 1 ); - WRITE_MEM( addr, (data >> 8) ); - sp = WORD( sp - 2 ); - WRITE_MEM( sp, data ); - goto loop; - } - - case 0xC8: // RET Z (next-most-common) - if ( CC_NZ() ) - goto loop; - ret: - time += 12; - case 0xD9: // RETI - case 0xC9:{// RET (most common) - pc = READ_MEM( sp ); - int addr = sp + 1; - sp = WORD( sp + 2 ); - pc += 0x100 * READ_MEM( addr ); - goto loop; - } - - case 0x00: // NOP - case 0x40: // LD B,B - case 0x49: // LD C,C - case 0x52: // LD D,D - case 0x5B: // LD E,E - case 0x64: // LD H,H - case 0x6D: // LD L,L - case 0x7F: // LD A,A - goto loop; - -// CB Instructions - - case 0xCB: - time += (instr_times + 256) [data]; - pc++; - // now data is the opcode - switch ( data ) { - - case 0x46: // BIT b,(HL) - case 0x4E: - case 0x56: - case 0x5E: - case 0x66: - case 0x6E: - case 0x76: - case 0x7E: { - int addr = rp.hl; - READ_FAST( addr, op ); - goto bit_comm; - } - - case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r - case 0x44: case 0x45: case 0x47: case 0x48: - case 0x49: case 0x4A: case 0x4B: case 0x4C: - case 0x4D: case 0x4F: case 0x50: case 0x51: - case 0x52: case 0x53: case 0x54: case 0x55: - case 0x57: case 0x58: case 0x59: case 0x5A: - case 0x5B: case 0x5C: case 0x5D: case 0x5F: - case 0x60: case 0x61: case 0x62: case 0x63: - case 0x64: case 0x65: case 0x67: case 0x68: - case 0x69: case 0x6A: case 0x6B: case 0x6C: - case 0x6D: case 0x6F: case 0x70: case 0x71: - case 0x72: case 0x73: case 0x74: case 0x75: - case 0x77: case 0x78: case 0x79: case 0x7A: - case 0x7B: case 0x7C: case 0x7D: case 0x7F: - op = R8( data & 7 ); - bit_comm: - ph = op >> (data >> 3 & 7) & 1; - cz = (cz & 0x100) + ph; - ph ^= 0x110; // N=0 H=1 - goto loop; - - case 0x86: // RES b,(HL) - case 0x8E: - case 0x96: - case 0x9E: - case 0xA6: - case 0xAE: - case 0xB6: - case 0xBE: { - int temp = READ_MEM( rp.hl ); - temp &= ~(1 << (data >> 3 & 7)); - WRITE_MEM( rp.hl, temp ); - goto loop; - } - - case 0xC6: // SET b,(HL) - case 0xCE: - case 0xD6: - case 0xDE: - case 0xE6: - case 0xEE: - case 0xF6: - case 0xFE: { - int temp = READ_MEM( rp.hl ); - temp |= 1 << (data >> 3 & 7); - WRITE_MEM( rp.hl, temp ); - goto loop; - } - - case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r - case 0xC4: case 0xC5: case 0xC7: case 0xC8: - case 0xC9: case 0xCA: case 0xCB: case 0xCC: - case 0xCD: case 0xCF: case 0xD0: case 0xD1: - case 0xD2: case 0xD3: case 0xD4: case 0xD5: - case 0xD7: case 0xD8: case 0xD9: case 0xDA: - case 0xDB: case 0xDC: case 0xDD: case 0xDF: - case 0xE0: case 0xE1: case 0xE2: case 0xE3: - case 0xE4: case 0xE5: case 0xE7: case 0xE8: - case 0xE9: case 0xEA: case 0xEB: case 0xEC: - case 0xED: case 0xEF: case 0xF0: case 0xF1: - case 0xF2: case 0xF3: case 0xF4: case 0xF5: - case 0xF7: case 0xF8: case 0xF9: case 0xFA: - case 0xFB: case 0xFC: case 0xFD: case 0xFF: - R8( data & 7 ) |= 1 << (data >> 3 & 7); - goto loop; - - case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r - case 0x84: case 0x85: case 0x87: case 0x88: - case 0x89: case 0x8A: case 0x8B: case 0x8C: - case 0x8D: case 0x8F: case 0x90: case 0x91: - case 0x92: case 0x93: case 0x94: case 0x95: - case 0x97: case 0x98: case 0x99: case 0x9A: - case 0x9B: case 0x9C: case 0x9D: case 0x9F: - case 0xA0: case 0xA1: case 0xA2: case 0xA3: - case 0xA4: case 0xA5: case 0xA7: case 0xA8: - case 0xA9: case 0xAA: case 0xAB: case 0xAC: - case 0xAD: case 0xAF: case 0xB0: case 0xB1: - case 0xB2: case 0xB3: case 0xB4: case 0xB5: - case 0xB7: case 0xB8: case 0xB9: case 0xBA: - case 0xBB: case 0xBC: case 0xBD: case 0xBF: - R8( data & 7 ) &= ~(1 << (data >> 3 & 7)); - goto loop; - - case 0x36: // SWAP (HL) - op = READ_MEM( rp.hl ); - goto swap_comm; - - case 0x30: // SWAP B - case 0x31: // SWAP C - case 0x32: // SWAP D - case 0x33: // SWAP E - case 0x34: // SWAP H - case 0x35: // SWAP L - case 0x37: // SWAP A - op = R8( data & 7 ); - swap_comm: - op = (op >> 4) + (op << 4); - cz = BYTE( op ); - ph = cz + 0x100; - if ( data == 0x36 ) - goto write_hl_op_ff; - R8( data & 7 ) = op; - goto loop; - -// Shift/Rotate - - case 0x26: // SLA (HL) - cz = 0; - case 0x16: // RL (HL) - cz = (cz >> 8 & 1) + (READ_MEM( rp.hl ) << 1); - goto rl_hl_common; - - case 0x06: // RLC (HL) - cz = READ_MEM( rp.hl ); - cz = (cz << 1) + (cz >> 7 & 1); - rl_hl_common: - // Z=* C=* - ph = cz | 0x100; // N=0 H=0 - WRITE_MEM( rp.hl, cz ); - goto loop; - - case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA r - cz = 0; - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL r - cz = (cz >> 8 & 1) + (R8( data & 7 ) << 1); - goto rl_common; - - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC r - cz = R8( data & 7 ); - cz = (cz << 1) + (cz >> 7 & 1); - rl_common: - // Z=* C=* - ph = cz | 0x100; // N=0 H=0 - R8( data & 7 ) = cz; - goto loop; - - case 0x0E: // RRC (HL) - cz = READ_MEM( rp.hl ); - cz += cz << 8 & 0x100; - goto rr_hl_common; - - case 0x2E: // SRA (HL) - cz = READ_MEM( rp.hl ); - cz += cz << 1 & 0x100; - goto rr_hl_common; - - case 0x3E: // SRL (HL) - cz = 0; - case 0x1E: // RR (HL) - cz = (cz & 0x100) + READ_MEM( rp.hl ); - rr_hl_common: - cz = (cz << 8) + (cz >> 1); // Z=* C=* - ph = cz | 0x100; // N=0 H=0 - WRITE_MEM( rp.hl, cz ); - goto loop; - - case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC r - cz = R8( data & 7 ); - cz += cz << 8 & 0x100; - goto rr_common; - - case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA r - cz = R8( data & 7 ); - cz += cz << 1 & 0x100; - goto rr_common; - - case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL r - cz = 0; - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR r - cz = (cz & 0x100) + R8( data & 7 ); - rr_common: - cz = (cz << 8) + (cz >> 1); // Z=* C=* - ph = cz | 0x100; // N=0 H=0 - R8( data & 7 ) = cz; - goto loop; - - } // CB op - assert( false ); // unhandled CB op - - case 0x07: // RLCA - cz = rg.a >> 7; - goto rlc_common; - case 0x17: // RLA - cz = cz >> 8 & 1; - rlc_common: - cz += rg.a << 1; - ph = cz | 0x100; - rg.a = BYTE( cz ); - cz |= 1; - goto loop; - - case 0x0F: // RRCA - ph = rg.a << 8; - goto rrc_common; - case 0x1F: // RRA - ph = cz; - rrc_common: - cz = (rg.a << 8) + 1; // Z=0 C=* - rg.a = ((ph & 0x100) + rg.a) >> 1; - ph = 0x100; // N=0 H=0 - goto loop; - -// Load - - case 0x70: // LD (HL),B - case 0x71: // LD (HL),C - case 0x72: // LD (HL),D - case 0x73: // LD (HL),E - case 0x74: // LD (HL),H - case 0x75: // LD (HL),L - case 0x77: // LD (HL),A - op = R8( op & 7 ); - write_hl_op_ff: - WRITE_MEM( rp.hl, op ); - goto loop; - - case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r - case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: - case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57: - case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F: - case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67: - case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F: - case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: - R8( op >> 3 & 7 ) = R8( op & 7 ); - goto loop; - - case 0x08: // LD IND16,SP - data = GET_ADDR(); - pc += 2; - WRITE_MEM( data, sp ); - data++; - WRITE_MEM( data, (sp >> 8) ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - - case 0x31: // LD SP,IMM - sp = GET_ADDR(); - pc += 2; - goto loop; - - case 0x01: // LD BC,IMM - case 0x11: // LD DE,IMM - r16 [(unsigned) op >> 4] = GET_ADDR(); - pc += 2; - goto loop; - - case 0xE2: // LD (0xFF00+C),A - WRITE_IO( rg.c, rg.a ); - goto loop; - - case 0xE0: // LD (0xFF00+imm),A - pc++; - WRITE_IO( data, rg.a ); - goto loop; - - { - int temp; - case 0x32: // LD (HL-),A - temp = rp.hl; - rp.hl = temp - 1; - goto write_data_rg_a; - - case 0x02: // LD (BC),A - temp = rp.bc; - goto write_data_rg_a; - - case 0x12: // LD (DE),A - temp = rp.de; - goto write_data_rg_a; - - case 0x22: // LD (HL+),A - temp = rp.hl; - rp.hl = temp + 1; - goto write_data_rg_a; - - case 0xEA: // LD IND16,A (common) - temp = GET_ADDR(); - pc += 2; - write_data_rg_a: - WRITE_MEM( temp, rg.a ); - goto loop; - } - - case 0x06: // LD B,IMM - rg.b = data; - pc++; - goto loop; - - case 0x0E: // LD C,IMM - rg.c = data; - pc++; - goto loop; - - case 0x16: // LD D,IMM - rg.d = data; - pc++; - goto loop; - - case 0x1E: // LD E,IMM - rg.e = data; - pc++; - goto loop; - - case 0x26: // LD H,IMM - rg.h = data; - pc++; - goto loop; - - case 0x2E: // LD L,IMM - rg.l = data; - pc++; - goto loop; - - case 0x36: // LD (HL),IMM - WRITE_MEM( rp.hl, data ); - pc++; - goto loop; - - case 0x3E: // LD A,IMM - rg.a = data; - pc++; - goto loop; - -// Increment/decrement - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - r16 [(unsigned) op >> 4]++; - goto loop; - - case 0x33: // INC SP - sp = WORD( sp + 1 ); - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - r16 [(unsigned) op >> 4]--; - goto loop; - - case 0x3B: // DEC SP - sp = WORD( sp - 1 ); - goto loop; - - case 0x34: // INC (HL) - op = rp.hl; - data = READ_MEM( op ); - data++; - WRITE_MEM( op, data ); - goto inc_comm; - - case 0x04: // INC B - case 0x0C: // INC C (common) - case 0x14: // INC D - case 0x1C: // INC E - case 0x24: // INC H - case 0x2C: // INC L - case 0x3C: // INC A - op = op >> 3 & 7; - data = R8( op ) + 1; - R8( op ) = data; - inc_comm: - ph = data - 0x101; // N=0 H=* - cz = (cz & 0x100) + BYTE( data ); // C=- Z=* - goto loop; - - case 0x35: // DEC (HL) - op = rp.hl; - data = READ_MEM( op ); - data--; - WRITE_MEM( op, data ); - goto dec_comm; - - case 0x05: // DEC B - case 0x0D: // DEC C - case 0x15: // DEC D - case 0x1D: // DEC E - case 0x25: // DEC H - case 0x2D: // DEC L - case 0x3D: // DEC A - op = op >> 3 & 7; - data = R8( op ) - 1; - R8( op ) = data; - dec_comm: - ph = data + 1; // N=1 H=* - cz = (cz & 0x100) + BYTE( data ); // C=- Z=* - goto loop; - -// Add 16-bit - - case 0xF8: // LD HL,SP+n - case 0xE8:{// ADD SP,n - pc++; - int t = WORD( sp + SBYTE( data ) ); - cz = ((BYTE( sp ) + data) & 0x100) + 1; // Z=0 C=* - ph = (sp ^ data ^ t) | 0x100; // N=0 H=* - if ( op == 0xF8 ) - { - rp.hl = t; - goto loop; - } - sp = t; - goto loop; - } - - case 0x39: // ADD HL,SP - data = sp; - goto add_hl_comm; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - data = r16 [(unsigned) op >> 4]; - add_hl_comm: - ph = rp.hl ^ data; - data += rp.hl; - rp.hl = WORD( data ); - ph ^= data; - cz = BYTE( cz ) + (data >> 8 & 0x100); // C=* Z=- - ph = ((ph >> 8) ^ cz) | 0x100; // N=0 H=* - goto loop; - - case 0x86: // ADD (HL) - data = READ_MEM( rp.hl ); - goto add_comm; - - case 0x80: // ADD B - case 0x81: // ADD C - case 0x82: // ADD D - case 0x83: // ADD E - case 0x84: // ADD H - case 0x85: // ADD L - case 0x87: // ADD A - data = R8( op & 7 ); - goto add_comm; - - case 0xC6: // ADD IMM - pc++; - add_comm: - ph = (rg.a ^ data) | 0x100; // N=1 H=* - cz = rg.a + data; // C=* Z=* - rg.a = cz; - goto loop; - -// Add/Subtract - - case 0x8E: // ADC (HL) - data = READ_MEM( rp.hl ); - goto adc_comm; - - case 0x88: // ADC B - case 0x89: // ADC C - case 0x8A: // ADC D - case 0x8B: // ADC E - case 0x8C: // ADC H - case 0x8D: // ADC L - case 0x8F: // ADC A - data = R8( op & 7 ); - goto adc_comm; - - case 0xCE: // ADC IMM - pc++; - adc_comm: - ph = (rg.a ^ data) | 0x100; // N=1 H=* - cz = rg.a + data + (cz >> 8 & 1); // C=* Z=* - rg.a = cz; - goto loop; - - case 0x96: // SUB (HL) - data = READ_MEM( rp.hl ); - goto sub_comm; - - case 0x90: // SUB B - case 0x91: // SUB C - case 0x92: // SUB D - case 0x93: // SUB E - case 0x94: // SUB H - case 0x95: // SUB L - case 0x97: // SUB A - data = R8( op & 7 ); - goto sub_comm; - - case 0xD6: // SUB IMM - pc++; - sub_comm: - ph = rg.a ^ data; // N=1 H=* - cz = rg.a - data; // C=* Z=* - rg.a = cz; - goto loop; - - case 0x9E: // SBC (HL) - data = READ_MEM( rp.hl ); - goto sbc_comm; - - case 0x98: // SBC B - case 0x99: // SBC C - case 0x9A: // SBC D - case 0x9B: // SBC E - case 0x9C: // SBC H - case 0x9D: // SBC L - case 0x9F: // SBC A - data = R8( op & 7 ); - goto sbc_comm; - - case 0xDE: // SBC IMM - pc++; - sbc_comm: - ph = rg.a ^ data; // N=1 H=* - cz = rg.a - data - (cz >> 8 & 1); // C=* Z=* - rg.a = cz; - goto loop; - -// Logical - - case 0xA0: // AND B - case 0xA1: // AND C - case 0xA2: // AND D - case 0xA3: // AND E - case 0xA4: // AND H - case 0xA5: // AND L - data = R8( op & 7 ); - goto and_comm; - - case 0xA6: // AND (HL) - data = READ_MEM( rp.hl ); - goto and_comm; - case 0xE6: // AND IMM - pc++; - and_comm: - cz = rg.a & data; // C=0 Z=* - ph = ~cz; // N=0 H=1 - rg.a = cz; - goto loop; - - case 0xA7: // AND A - cz = rg.a; // C=0 Z=* - ph = ~rg.a; // N=0 H=1 - goto loop; - - case 0xB0: // OR B - case 0xB1: // OR C - case 0xB2: // OR D - case 0xB3: // OR E - case 0xB4: // OR H - case 0xB5: // OR L - data = R8( op & 7 ); - goto or_comm; - - case 0xB6: // OR (HL) - data = READ_MEM( rp.hl ); - goto or_comm; - case 0xF6: // OR IMM - pc++; - or_comm: - cz = rg.a | data; // C=0 Z=* - ph = cz | 0x100; // N=0 H=0 - rg.a = cz; - goto loop; - - case 0xB7: // OR A - cz = rg.a; // C=0 Z=* - ph = rg.a + 0x100; // N=0 H=0 - goto loop; - - case 0xA8: // XOR B - case 0xA9: // XOR C - case 0xAA: // XOR D - case 0xAB: // XOR E - case 0xAC: // XOR H - case 0xAD: // XOR L - data = R8( op & 7 ); - goto xor_comm; - - case 0xAE: // XOR (HL) - data = READ_MEM( rp.hl ); - pc--; - case 0xEE: // XOR IMM - pc++; - xor_comm: - cz = rg.a ^ data; // C=0 Z=* - ph = cz + 0x100; // N=0 H=0 - rg.a = cz; - goto loop; - - case 0xAF: // XOR A - rg.a = 0; - cz = 0; // C=0 Z=* - ph = 0x100; // N=0 H=0 - goto loop; - -// Stack - - case 0xF1: // POP AF - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL (common) - data = READ_MEM( sp ); - r16 [op >> 4 & 3] = data + 0x100 * READ_MEM( (sp + 1) ); - sp = WORD( sp + 2 ); - if ( op != 0xF1 ) - goto loop; - - SET_FLAGS( rg.a ); - rg.a = rg.flags; - goto loop; - - case 0xC5: // PUSH BC - data = rp.bc; - goto push; - - case 0xD5: // PUSH DE - data = rp.de; - goto push; - - case 0xE5: // PUSH HL - data = rp.hl; - goto push; - - case 0xF5: // PUSH AF - GET_FLAGS( data ); - data += rg.a << 8; - goto push; - -// Flow control - - case 0xFF: case 0xC7: case 0xCF: case 0xD7: // RST - case 0xDF: case 0xE7: case 0xEF: case 0xF7: - data = pc; - pc = (op & 0x38) + CPU.rst_base; - goto push; - - case 0xCC: // CALL Z - pc += 2; - if ( CC_Z() ) - goto call; - goto loop; - - case 0xD4: // CALL NC - pc += 2; - if ( CC_NC() ) - goto call; - goto loop; - - case 0xDC: // CALL C - pc += 2; - if ( CC_C() ) - goto call; - goto loop; - - case 0xC0: // RET NZ - if ( CC_NZ() ) - goto ret; - goto loop; - - case 0xD0: // RET NC - if ( CC_NC() ) - goto ret; - goto loop; - - case 0xD8: // RET C - if ( CC_C() ) - goto ret; - goto loop; - - case 0x18: // JR - BRANCH_( true, 0 ) - - case 0x30: // JR NC - BRANCH( CC_NC() ) - - case 0x38: // JR C - BRANCH( CC_C() ) - - case 0xE9: // LD PC,HL - pc = rp.hl; - goto loop; - - case 0xC3: // JP (next-most-common) - pc = GET_ADDR(); - goto loop; - - case 0xC2: // JP NZ - pc += 2; - if ( CC_NZ() ) - goto jp_taken; - time -= 4; - goto loop; - - case 0xCA: // JP Z (most common) - pc += 2; - if ( CC_Z() ) - goto jp_taken; - time -= 4; - goto loop; - - jp_taken: - pc -= 2; - pc = GET_ADDR(); - goto loop; - - case 0xD2: // JP NC - pc += 2; - if ( CC_NC() ) - goto jp_taken; - time -= 4; - goto loop; - - case 0xDA: // JP C - pc += 2; - if ( CC_C() ) - goto jp_taken; - time -= 4; - goto loop; - -// Flags - - case 0x2F: // CPL - rg.a = ~rg.a; - ph = BYTE( ~cz ); // N=1 H=1 - goto loop; - - case 0x3F: // CCF - ph = cz | 0x100; // N=0 H=0 - cz ^= 0x100; // C=* Z=- - goto loop; - - case 0x37: // SCF - ph = cz | 0x100; // N=0 H=0 - cz |= 0x100; // C=1 Z=- - goto loop; - - case 0xF3: // DI - goto loop; - - case 0xFB: // EI - goto loop; - - case 0x27:{// DAA - unsigned a = rg.a; - int h = ph ^ cz; - if ( ph & 0x100 ) - { - if ( (h & 0x10) || (a & 0x0F) > 9 ) - a += 6; - - if ( (cz & 0x100) || a > 0x9F ) - a += 0x60; - } - else - { - if ( h & 0x10 ) - a = (a - 6) & 0xFF; - - if ( cz & 0x100 ) - a -= 0x60; - } - cz = (cz & 0x100) | a; // C=- Z=* - rg.a = a; - ph = (ph & 0x100) + BYTE( a ); // N=- H=0 - goto loop; - } - -// Special - - case 0x76: // HALT - case 0x10: // STOP - case 0xD3: case 0xDB: case 0xDD: // Illegal - case 0xE3: case 0xE4: case 0xEB: case 0xEC: case 0xED: // (all freeze CPU) - case 0xF4: case 0xFC: case 0xFD: - goto stop; - } - - // If this fails then an opcode isn't handled above - assert( false ); - -stop: - pc--; - - // copy state back - CPU.cpu_state_.time = time; - CPU.r.pc = pc; - CPU.r.sp = sp; - { - int t; - GET_FLAGS( t ); - rg.flags = t; - } - CPU.cpu_state = &CPU.cpu_state_; - STATIC_CAST(Gb_Cpu::core_regs_t&,CPU.r) = rp; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#if 0 +/* Define these macros in the source file before #including this file. +- Parameters might be expressions, so they are best evaluated only once, +though they NEVER have side-effects, so multiple evaluation is OK. +- Output parameters might be a multiple-assignment expression like "a=x", +so they must NOT be parenthesized. +- Macros "returning" void may use a {} statement block. */ + + // 0 <= addr <= 0xFFFF + page_size + // time functions can be used + int READ_MEM( addr_t ); + void WRITE_MEM( addr_t, int data ); + + // Access of 0xFF00 + offset + // 0 <= offset <= 0xFF + int READ_IO( int offset ); + void WRITE_IO( int offset, int data ); + + // Often-used instructions use this instead of READ_MEM + void READ_FAST( addr_t, int& out ); + +// The following can be used within macros: + + // Current time + cpu_time_t TIME(); +#endif + +/* Copyright (C) 2003-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// Common instructions: +// +// 365880 FA LD A,(nn) +// 355863 20 JR NZ +// 313655 21 LD HL,nn +// 274580 28 JR Z +// 252878 FE CP n +// 230541 7E LD A,(HL) +// 226209 2A LD A,(HL+) +// 217467 CD CALL +// 212034 C9 RET +// 208376 CB CB prefix +// +// 27486 CB 7E BIT 7,(HL) +// 15925 CB 76 BIT 6,(HL) +// 13035 CB 19 RR C +// 11557 CB 7F BIT 7,A +// 10898 CB 37 SWAP A +// 10208 CB 66 BIT 4,(HL) + +// Allows MWCW debugger to step through code properly +#ifdef CPU_BEGIN + CPU_BEGIN +#endif + +#define TIME() s.time + +#define CODE_PAGE( addr ) s.code_map [GB_CPU_PAGE( addr )] +#define READ_CODE( addr ) (CODE_PAGE( addr ) [GB_CPU_OFFSET( addr )]) + +// Flags with hex value for clarity when used as mask. +// Stored in indicated variable during emulation. +int const z80 = 0x80; // cz +int const n40 = 0x40; // ph +int const h20 = 0x20; // ph +int const c10 = 0x10; // cz + +#define SET_FLAGS( in )\ +{\ + cz = ((in) << 4 & 0x100) + (~(in) >> 7 & 1);\ + ph = (~(in) << 2 & 0x100) + ((in) >> 1 & 0x10);\ +} + +// random bits in cz to catch misuse of them +#define SET_FLAGS_DEBUG( in )\ +{\ + cz = ((in) << 4 & 0x100) | (rand() & ~0x1FF) | ((in) & 0x80 ? 0 : (rand() & 0xFF) | 1);\ + ph = (~(in) << 2 & 0x100) | (((in) >> 1 & 0x10) ^ BYTE( cz ));\ +} + +#define GET_FLAGS( out )\ +{\ + out = (cz >> 4 & c10);\ + out += ~ph >> 2 & n40;\ + out += (ph ^ cz) << 1 & h20;\ + if ( !BYTE( cz ) )\ + out += z80;\ +} + +#define CC_NZ() ( BYTE( cz )) +#define CC_Z() (!BYTE( cz )) +#define CC_NC() (!(cz & 0x100)) +#define CC_C() ( cz & 0x100 ) + +// Truncation +#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */ +#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */ +#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */ + +{ + Gb_Cpu::cpu_state_t s; + CPU.cpu_state = &s; + memcpy( &s, &CPU.cpu_state_, sizeof s ); + + union { + struct { + #if BLARGG_BIG_ENDIAN + byte b, c, d, e, h, l, flags, a; + #else + byte c, b, e, d, l, h, a, flags; + #endif + } rg; // individual registers + Gb_Cpu::core_regs_t rp; // pairs + + byte r8_ [8]; // indexed registers (use R8 macro due to endian dependence) + BOOST::uint16_t r16 [4]; // indexed pairs + }; + BLARGG_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); + + #if BLARGG_BIG_ENDIAN + #define R8( n ) (r8_ [n]) + #elif BLARGG_LITTLE_ENDIAN + #define R8( n ) (r8_ [(n) ^ 1]) + #else + // Be sure "blargg_endian.h" has been #included in the file that #includes this + #error "Byte order of CPU must be known" + #endif + + rp = CPU.r; + int pc = CPU.r.pc; + int sp = CPU.r.sp; + int ph; + int cz; + SET_FLAGS( rg.flags ); + + int time = s.time; + +loop: + + check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around + check( (unsigned) sp < 0x10000 ); + + byte const* instr = CODE_PAGE( pc ); + int op; + + if ( GB_CPU_OFFSET(~0) == ~0 ) + { + op = instr [pc]; + pc++; + instr += pc; + } + else + { + instr += GB_CPU_OFFSET( pc ); + op = *instr++; + pc++; + } + +#define GET_ADDR() GET_LE16( instr ) + + static byte const instr_times [256*2] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,// 0 + 4,12, 8, 8, 4, 4, 8, 4,12, 8, 8, 8, 4, 4, 8, 4,// 1 + 8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 2 + 8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 3 + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 4 + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 5 + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 6 + 8, 8, 8, 8, 8, 8, 0, 8, 4, 4, 4, 4, 4, 4, 8, 4,// 7 + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 8 + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 9 + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// A + 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// B + 8,12,16,16,12,16, 8,16, 8,16,16, 0,12,24, 8,16,// C + 8,12,16, 0,12,16, 8,16, 8,16,16, 0,12, 0, 8,16,// D + 12,12, 8, 0, 0,16, 8,16,16, 4,16, 0, 0, 0, 8,16,// E + 12,12, 8, 4, 0,16, 8,16,12, 8,16, 4, 0, 0, 8,16,// F + + // CB prefixed + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 0 + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 1 + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 2 + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 3 + 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 4 + 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 5 + 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 6 + 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 7 + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 8 + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 9 + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// A + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// B + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// C + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// D + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// E + 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// F + }; + + if ( time >= 0 ) + goto stop; + + time += instr_times [op]; + + int data; + data = *instr; + s.time = time; + + #ifdef CPU_INSTR_HOOK + { CPU_INSTR_HOOK( (pc-1), (instr-1), rg.a, rp.bc, rp.de, rp.hl, sp ); } + #endif + + switch ( op ) + { + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH_( cond, clocks )\ +{\ + pc++;\ + if ( !(cond) )\ + goto loop;\ + pc = WORD( pc + SBYTE( data ) );\ + time += clocks;\ + goto loop;\ +} + +#define BRANCH( cond ) BRANCH_( cond, 4 ) + +// Most Common + + case 0x20: // JR NZ + BRANCH( CC_NZ() ) + + case 0x21: // LD HL,IMM (common) + rp.hl = GET_ADDR(); + pc += 2; + goto loop; + + case 0x28: // JR Z + BRANCH( CC_Z() ) + + case 0xF2: // LD A,(0xFF00+C) + READ_IO( rg.c, rg.a ); + goto loop; + + case 0xF0: // LD A,(0xFF00+imm) + pc++; + READ_IO( data, rg.a ); + goto loop; + + { + int temp; + case 0x0A: // LD A,(BC) + temp = rp.bc; + goto ld_a_ind_comm; + + case 0x3A: // LD A,(HL-) + temp = rp.hl; + rp.hl = temp - 1; + goto ld_a_ind_comm; + + case 0x1A: // LD A,(DE) + temp = rp.de; + goto ld_a_ind_comm; + + case 0x2A: // LD A,(HL+) (common) + temp = rp.hl; + rp.hl = temp + 1; + goto ld_a_ind_comm; + + case 0xFA: // LD A,IND16 (common) + temp = GET_ADDR(); + pc += 2; + ld_a_ind_comm: + READ_FAST( temp, rg.a ); + goto loop; + } + + { + int temp; + case 0xBE: // CP (HL) + temp = READ_MEM( rp.hl ); + goto cmp_comm; + + case 0xB8: // CP B + case 0xB9: // CP C + case 0xBA: // CP D + case 0xBB: // CP E + case 0xBC: // CP H + case 0xBD: // CP L + case 0xBF: // CP A + temp = R8( op & 7 ); + cmp_comm: + ph = rg.a ^ temp; // N=1 H=* + cz = rg.a - temp; // C=* Z=* + goto loop; + } + + case 0xFE: // CP IMM + pc++; + ph = rg.a ^ data; // N=1 H=* + cz = rg.a - data; // C=* Z=* + goto loop; + + case 0x46: // LD B,(HL) + case 0x4E: // LD C,(HL) + case 0x56: // LD D,(HL) + case 0x5E: // LD E,(HL) + case 0x66: // LD H,(HL) + case 0x6E: // LD L,(HL) + case 0x7E:{// LD A,(HL) + int addr = rp.hl; + READ_FAST( addr, R8( op >> 3 & 7 ) ); + goto loop; + } + + case 0xC4: // CNZ (next-most-common) + pc += 2; + if ( CC_Z() ) + goto loop; + call: + time += 12; + pc -= 2; + case 0xCD: // CALL (most-common) + data = pc + 2; + pc = GET_ADDR(); + push: { + int addr = WORD( sp - 1 ); + WRITE_MEM( addr, (data >> 8) ); + sp = WORD( sp - 2 ); + WRITE_MEM( sp, data ); + goto loop; + } + + case 0xC8: // RET Z (next-most-common) + if ( CC_NZ() ) + goto loop; + ret: + time += 12; + case 0xD9: // RETI + case 0xC9:{// RET (most common) + pc = READ_MEM( sp ); + int addr = sp + 1; + sp = WORD( sp + 2 ); + pc += 0x100 * READ_MEM( addr ); + goto loop; + } + + case 0x00: // NOP + case 0x40: // LD B,B + case 0x49: // LD C,C + case 0x52: // LD D,D + case 0x5B: // LD E,E + case 0x64: // LD H,H + case 0x6D: // LD L,L + case 0x7F: // LD A,A + goto loop; + +// CB Instructions + + case 0xCB: + time += (instr_times + 256) [data]; + pc++; + // now data is the opcode + switch ( data ) { + + case 0x46: // BIT b,(HL) + case 0x4E: + case 0x56: + case 0x5E: + case 0x66: + case 0x6E: + case 0x76: + case 0x7E: { + int addr = rp.hl; + READ_FAST( addr, op ); + goto bit_comm; + } + + case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r + case 0x44: case 0x45: case 0x47: case 0x48: + case 0x49: case 0x4A: case 0x4B: case 0x4C: + case 0x4D: case 0x4F: case 0x50: case 0x51: + case 0x52: case 0x53: case 0x54: case 0x55: + case 0x57: case 0x58: case 0x59: case 0x5A: + case 0x5B: case 0x5C: case 0x5D: case 0x5F: + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x67: case 0x68: + case 0x69: case 0x6A: case 0x6B: case 0x6C: + case 0x6D: case 0x6F: case 0x70: case 0x71: + case 0x72: case 0x73: case 0x74: case 0x75: + case 0x77: case 0x78: case 0x79: case 0x7A: + case 0x7B: case 0x7C: case 0x7D: case 0x7F: + op = R8( data & 7 ); + bit_comm: + ph = op >> (data >> 3 & 7) & 1; + cz = (cz & 0x100) + ph; + ph ^= 0x110; // N=0 H=1 + goto loop; + + case 0x86: // RES b,(HL) + case 0x8E: + case 0x96: + case 0x9E: + case 0xA6: + case 0xAE: + case 0xB6: + case 0xBE: { + int temp = READ_MEM( rp.hl ); + temp &= ~(1 << (data >> 3 & 7)); + WRITE_MEM( rp.hl, temp ); + goto loop; + } + + case 0xC6: // SET b,(HL) + case 0xCE: + case 0xD6: + case 0xDE: + case 0xE6: + case 0xEE: + case 0xF6: + case 0xFE: { + int temp = READ_MEM( rp.hl ); + temp |= 1 << (data >> 3 & 7); + WRITE_MEM( rp.hl, temp ); + goto loop; + } + + case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r + case 0xC4: case 0xC5: case 0xC7: case 0xC8: + case 0xC9: case 0xCA: case 0xCB: case 0xCC: + case 0xCD: case 0xCF: case 0xD0: case 0xD1: + case 0xD2: case 0xD3: case 0xD4: case 0xD5: + case 0xD7: case 0xD8: case 0xD9: case 0xDA: + case 0xDB: case 0xDC: case 0xDD: case 0xDF: + case 0xE0: case 0xE1: case 0xE2: case 0xE3: + case 0xE4: case 0xE5: case 0xE7: case 0xE8: + case 0xE9: case 0xEA: case 0xEB: case 0xEC: + case 0xED: case 0xEF: case 0xF0: case 0xF1: + case 0xF2: case 0xF3: case 0xF4: case 0xF5: + case 0xF7: case 0xF8: case 0xF9: case 0xFA: + case 0xFB: case 0xFC: case 0xFD: case 0xFF: + R8( data & 7 ) |= 1 << (data >> 3 & 7); + goto loop; + + case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r + case 0x84: case 0x85: case 0x87: case 0x88: + case 0x89: case 0x8A: case 0x8B: case 0x8C: + case 0x8D: case 0x8F: case 0x90: case 0x91: + case 0x92: case 0x93: case 0x94: case 0x95: + case 0x97: case 0x98: case 0x99: case 0x9A: + case 0x9B: case 0x9C: case 0x9D: case 0x9F: + case 0xA0: case 0xA1: case 0xA2: case 0xA3: + case 0xA4: case 0xA5: case 0xA7: case 0xA8: + case 0xA9: case 0xAA: case 0xAB: case 0xAC: + case 0xAD: case 0xAF: case 0xB0: case 0xB1: + case 0xB2: case 0xB3: case 0xB4: case 0xB5: + case 0xB7: case 0xB8: case 0xB9: case 0xBA: + case 0xBB: case 0xBC: case 0xBD: case 0xBF: + R8( data & 7 ) &= ~(1 << (data >> 3 & 7)); + goto loop; + + case 0x36: // SWAP (HL) + op = READ_MEM( rp.hl ); + goto swap_comm; + + case 0x30: // SWAP B + case 0x31: // SWAP C + case 0x32: // SWAP D + case 0x33: // SWAP E + case 0x34: // SWAP H + case 0x35: // SWAP L + case 0x37: // SWAP A + op = R8( data & 7 ); + swap_comm: + op = (op >> 4) + (op << 4); + cz = BYTE( op ); + ph = cz + 0x100; + if ( data == 0x36 ) + goto write_hl_op_ff; + R8( data & 7 ) = op; + goto loop; + +// Shift/Rotate + + case 0x26: // SLA (HL) + cz = 0; + case 0x16: // RL (HL) + cz = (cz >> 8 & 1) + (READ_MEM( rp.hl ) << 1); + goto rl_hl_common; + + case 0x06: // RLC (HL) + cz = READ_MEM( rp.hl ); + cz = (cz << 1) + (cz >> 7 & 1); + rl_hl_common: + // Z=* C=* + ph = cz | 0x100; // N=0 H=0 + WRITE_MEM( rp.hl, cz ); + goto loop; + + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA r + cz = 0; + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL r + cz = (cz >> 8 & 1) + (R8( data & 7 ) << 1); + goto rl_common; + + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC r + cz = R8( data & 7 ); + cz = (cz << 1) + (cz >> 7 & 1); + rl_common: + // Z=* C=* + ph = cz | 0x100; // N=0 H=0 + R8( data & 7 ) = cz; + goto loop; + + case 0x0E: // RRC (HL) + cz = READ_MEM( rp.hl ); + cz += cz << 8 & 0x100; + goto rr_hl_common; + + case 0x2E: // SRA (HL) + cz = READ_MEM( rp.hl ); + cz += cz << 1 & 0x100; + goto rr_hl_common; + + case 0x3E: // SRL (HL) + cz = 0; + case 0x1E: // RR (HL) + cz = (cz & 0x100) + READ_MEM( rp.hl ); + rr_hl_common: + cz = (cz << 8) + (cz >> 1); // Z=* C=* + ph = cz | 0x100; // N=0 H=0 + WRITE_MEM( rp.hl, cz ); + goto loop; + + case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC r + cz = R8( data & 7 ); + cz += cz << 8 & 0x100; + goto rr_common; + + case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA r + cz = R8( data & 7 ); + cz += cz << 1 & 0x100; + goto rr_common; + + case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL r + cz = 0; + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR r + cz = (cz & 0x100) + R8( data & 7 ); + rr_common: + cz = (cz << 8) + (cz >> 1); // Z=* C=* + ph = cz | 0x100; // N=0 H=0 + R8( data & 7 ) = cz; + goto loop; + + } // CB op + assert( false ); // unhandled CB op + + case 0x07: // RLCA + cz = rg.a >> 7; + goto rlc_common; + case 0x17: // RLA + cz = cz >> 8 & 1; + rlc_common: + cz += rg.a << 1; + ph = cz | 0x100; + rg.a = BYTE( cz ); + cz |= 1; + goto loop; + + case 0x0F: // RRCA + ph = rg.a << 8; + goto rrc_common; + case 0x1F: // RRA + ph = cz; + rrc_common: + cz = (rg.a << 8) + 1; // Z=0 C=* + rg.a = ((ph & 0x100) + rg.a) >> 1; + ph = 0x100; // N=0 H=0 + goto loop; + +// Load + + case 0x70: // LD (HL),B + case 0x71: // LD (HL),C + case 0x72: // LD (HL),D + case 0x73: // LD (HL),E + case 0x74: // LD (HL),H + case 0x75: // LD (HL),L + case 0x77: // LD (HL),A + op = R8( op & 7 ); + write_hl_op_ff: + WRITE_MEM( rp.hl, op ); + goto loop; + + case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r + case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: + case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57: + case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F: + case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67: + case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F: + case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: + R8( op >> 3 & 7 ) = R8( op & 7 ); + goto loop; + + case 0x08: // LD IND16,SP + data = GET_ADDR(); + pc += 2; + WRITE_MEM( data, sp ); + data++; + WRITE_MEM( data, (sp >> 8) ); + goto loop; + + case 0xF9: // LD SP,HL + sp = rp.hl; + goto loop; + + case 0x31: // LD SP,IMM + sp = GET_ADDR(); + pc += 2; + goto loop; + + case 0x01: // LD BC,IMM + case 0x11: // LD DE,IMM + r16 [(unsigned) op >> 4] = GET_ADDR(); + pc += 2; + goto loop; + + case 0xE2: // LD (0xFF00+C),A + WRITE_IO( rg.c, rg.a ); + goto loop; + + case 0xE0: // LD (0xFF00+imm),A + pc++; + WRITE_IO( data, rg.a ); + goto loop; + + { + int temp; + case 0x32: // LD (HL-),A + temp = rp.hl; + rp.hl = temp - 1; + goto write_data_rg_a; + + case 0x02: // LD (BC),A + temp = rp.bc; + goto write_data_rg_a; + + case 0x12: // LD (DE),A + temp = rp.de; + goto write_data_rg_a; + + case 0x22: // LD (HL+),A + temp = rp.hl; + rp.hl = temp + 1; + goto write_data_rg_a; + + case 0xEA: // LD IND16,A (common) + temp = GET_ADDR(); + pc += 2; + write_data_rg_a: + WRITE_MEM( temp, rg.a ); + goto loop; + } + + case 0x06: // LD B,IMM + rg.b = data; + pc++; + goto loop; + + case 0x0E: // LD C,IMM + rg.c = data; + pc++; + goto loop; + + case 0x16: // LD D,IMM + rg.d = data; + pc++; + goto loop; + + case 0x1E: // LD E,IMM + rg.e = data; + pc++; + goto loop; + + case 0x26: // LD H,IMM + rg.h = data; + pc++; + goto loop; + + case 0x2E: // LD L,IMM + rg.l = data; + pc++; + goto loop; + + case 0x36: // LD (HL),IMM + WRITE_MEM( rp.hl, data ); + pc++; + goto loop; + + case 0x3E: // LD A,IMM + rg.a = data; + pc++; + goto loop; + +// Increment/decrement + + case 0x03: // INC BC + case 0x13: // INC DE + case 0x23: // INC HL + r16 [(unsigned) op >> 4]++; + goto loop; + + case 0x33: // INC SP + sp = WORD( sp + 1 ); + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + r16 [(unsigned) op >> 4]--; + goto loop; + + case 0x3B: // DEC SP + sp = WORD( sp - 1 ); + goto loop; + + case 0x34: // INC (HL) + op = rp.hl; + data = READ_MEM( op ); + data++; + WRITE_MEM( op, data ); + goto inc_comm; + + case 0x04: // INC B + case 0x0C: // INC C (common) + case 0x14: // INC D + case 0x1C: // INC E + case 0x24: // INC H + case 0x2C: // INC L + case 0x3C: // INC A + op = op >> 3 & 7; + data = R8( op ) + 1; + R8( op ) = data; + inc_comm: + ph = data - 0x101; // N=0 H=* + cz = (cz & 0x100) + BYTE( data ); // C=- Z=* + goto loop; + + case 0x35: // DEC (HL) + op = rp.hl; + data = READ_MEM( op ); + data--; + WRITE_MEM( op, data ); + goto dec_comm; + + case 0x05: // DEC B + case 0x0D: // DEC C + case 0x15: // DEC D + case 0x1D: // DEC E + case 0x25: // DEC H + case 0x2D: // DEC L + case 0x3D: // DEC A + op = op >> 3 & 7; + data = R8( op ) - 1; + R8( op ) = data; + dec_comm: + ph = data + 1; // N=1 H=* + cz = (cz & 0x100) + BYTE( data ); // C=- Z=* + goto loop; + +// Add 16-bit + + case 0xF8: // LD HL,SP+n + case 0xE8:{// ADD SP,n + pc++; + int t = WORD( sp + SBYTE( data ) ); + cz = ((BYTE( sp ) + data) & 0x100) + 1; // Z=0 C=* + ph = (sp ^ data ^ t) | 0x100; // N=0 H=* + if ( op == 0xF8 ) + { + rp.hl = t; + goto loop; + } + sp = t; + goto loop; + } + + case 0x39: // ADD HL,SP + data = sp; + goto add_hl_comm; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + data = r16 [(unsigned) op >> 4]; + add_hl_comm: + ph = rp.hl ^ data; + data += rp.hl; + rp.hl = WORD( data ); + ph ^= data; + cz = BYTE( cz ) + (data >> 8 & 0x100); // C=* Z=- + ph = ((ph >> 8) ^ cz) | 0x100; // N=0 H=* + goto loop; + + case 0x86: // ADD (HL) + data = READ_MEM( rp.hl ); + goto add_comm; + + case 0x80: // ADD B + case 0x81: // ADD C + case 0x82: // ADD D + case 0x83: // ADD E + case 0x84: // ADD H + case 0x85: // ADD L + case 0x87: // ADD A + data = R8( op & 7 ); + goto add_comm; + + case 0xC6: // ADD IMM + pc++; + add_comm: + ph = (rg.a ^ data) | 0x100; // N=1 H=* + cz = rg.a + data; // C=* Z=* + rg.a = cz; + goto loop; + +// Add/Subtract + + case 0x8E: // ADC (HL) + data = READ_MEM( rp.hl ); + goto adc_comm; + + case 0x88: // ADC B + case 0x89: // ADC C + case 0x8A: // ADC D + case 0x8B: // ADC E + case 0x8C: // ADC H + case 0x8D: // ADC L + case 0x8F: // ADC A + data = R8( op & 7 ); + goto adc_comm; + + case 0xCE: // ADC IMM + pc++; + adc_comm: + ph = (rg.a ^ data) | 0x100; // N=1 H=* + cz = rg.a + data + (cz >> 8 & 1); // C=* Z=* + rg.a = cz; + goto loop; + + case 0x96: // SUB (HL) + data = READ_MEM( rp.hl ); + goto sub_comm; + + case 0x90: // SUB B + case 0x91: // SUB C + case 0x92: // SUB D + case 0x93: // SUB E + case 0x94: // SUB H + case 0x95: // SUB L + case 0x97: // SUB A + data = R8( op & 7 ); + goto sub_comm; + + case 0xD6: // SUB IMM + pc++; + sub_comm: + ph = rg.a ^ data; // N=1 H=* + cz = rg.a - data; // C=* Z=* + rg.a = cz; + goto loop; + + case 0x9E: // SBC (HL) + data = READ_MEM( rp.hl ); + goto sbc_comm; + + case 0x98: // SBC B + case 0x99: // SBC C + case 0x9A: // SBC D + case 0x9B: // SBC E + case 0x9C: // SBC H + case 0x9D: // SBC L + case 0x9F: // SBC A + data = R8( op & 7 ); + goto sbc_comm; + + case 0xDE: // SBC IMM + pc++; + sbc_comm: + ph = rg.a ^ data; // N=1 H=* + cz = rg.a - data - (cz >> 8 & 1); // C=* Z=* + rg.a = cz; + goto loop; + +// Logical + + case 0xA0: // AND B + case 0xA1: // AND C + case 0xA2: // AND D + case 0xA3: // AND E + case 0xA4: // AND H + case 0xA5: // AND L + data = R8( op & 7 ); + goto and_comm; + + case 0xA6: // AND (HL) + data = READ_MEM( rp.hl ); + goto and_comm; + case 0xE6: // AND IMM + pc++; + and_comm: + cz = rg.a & data; // C=0 Z=* + ph = ~cz; // N=0 H=1 + rg.a = cz; + goto loop; + + case 0xA7: // AND A + cz = rg.a; // C=0 Z=* + ph = ~rg.a; // N=0 H=1 + goto loop; + + case 0xB0: // OR B + case 0xB1: // OR C + case 0xB2: // OR D + case 0xB3: // OR E + case 0xB4: // OR H + case 0xB5: // OR L + data = R8( op & 7 ); + goto or_comm; + + case 0xB6: // OR (HL) + data = READ_MEM( rp.hl ); + goto or_comm; + case 0xF6: // OR IMM + pc++; + or_comm: + cz = rg.a | data; // C=0 Z=* + ph = cz | 0x100; // N=0 H=0 + rg.a = cz; + goto loop; + + case 0xB7: // OR A + cz = rg.a; // C=0 Z=* + ph = rg.a + 0x100; // N=0 H=0 + goto loop; + + case 0xA8: // XOR B + case 0xA9: // XOR C + case 0xAA: // XOR D + case 0xAB: // XOR E + case 0xAC: // XOR H + case 0xAD: // XOR L + data = R8( op & 7 ); + goto xor_comm; + + case 0xAE: // XOR (HL) + data = READ_MEM( rp.hl ); + pc--; + case 0xEE: // XOR IMM + pc++; + xor_comm: + cz = rg.a ^ data; // C=0 Z=* + ph = cz + 0x100; // N=0 H=0 + rg.a = cz; + goto loop; + + case 0xAF: // XOR A + rg.a = 0; + cz = 0; // C=0 Z=* + ph = 0x100; // N=0 H=0 + goto loop; + +// Stack + + case 0xF1: // POP AF + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL (common) + data = READ_MEM( sp ); + r16 [op >> 4 & 3] = data + 0x100 * READ_MEM( (sp + 1) ); + sp = WORD( sp + 2 ); + if ( op != 0xF1 ) + goto loop; + + SET_FLAGS( rg.a ); + rg.a = rg.flags; + goto loop; + + case 0xC5: // PUSH BC + data = rp.bc; + goto push; + + case 0xD5: // PUSH DE + data = rp.de; + goto push; + + case 0xE5: // PUSH HL + data = rp.hl; + goto push; + + case 0xF5: // PUSH AF + GET_FLAGS( data ); + data += rg.a << 8; + goto push; + +// Flow control + + case 0xFF: case 0xC7: case 0xCF: case 0xD7: // RST + case 0xDF: case 0xE7: case 0xEF: case 0xF7: + data = pc; + pc = (op & 0x38) + CPU.rst_base; + goto push; + + case 0xCC: // CALL Z + pc += 2; + if ( CC_Z() ) + goto call; + goto loop; + + case 0xD4: // CALL NC + pc += 2; + if ( CC_NC() ) + goto call; + goto loop; + + case 0xDC: // CALL C + pc += 2; + if ( CC_C() ) + goto call; + goto loop; + + case 0xC0: // RET NZ + if ( CC_NZ() ) + goto ret; + goto loop; + + case 0xD0: // RET NC + if ( CC_NC() ) + goto ret; + goto loop; + + case 0xD8: // RET C + if ( CC_C() ) + goto ret; + goto loop; + + case 0x18: // JR + BRANCH_( true, 0 ) + + case 0x30: // JR NC + BRANCH( CC_NC() ) + + case 0x38: // JR C + BRANCH( CC_C() ) + + case 0xE9: // LD PC,HL + pc = rp.hl; + goto loop; + + case 0xC3: // JP (next-most-common) + pc = GET_ADDR(); + goto loop; + + case 0xC2: // JP NZ + pc += 2; + if ( CC_NZ() ) + goto jp_taken; + time -= 4; + goto loop; + + case 0xCA: // JP Z (most common) + pc += 2; + if ( CC_Z() ) + goto jp_taken; + time -= 4; + goto loop; + + jp_taken: + pc -= 2; + pc = GET_ADDR(); + goto loop; + + case 0xD2: // JP NC + pc += 2; + if ( CC_NC() ) + goto jp_taken; + time -= 4; + goto loop; + + case 0xDA: // JP C + pc += 2; + if ( CC_C() ) + goto jp_taken; + time -= 4; + goto loop; + +// Flags + + case 0x2F: // CPL + rg.a = ~rg.a; + ph = BYTE( ~cz ); // N=1 H=1 + goto loop; + + case 0x3F: // CCF + ph = cz | 0x100; // N=0 H=0 + cz ^= 0x100; // C=* Z=- + goto loop; + + case 0x37: // SCF + ph = cz | 0x100; // N=0 H=0 + cz |= 0x100; // C=1 Z=- + goto loop; + + case 0xF3: // DI + goto loop; + + case 0xFB: // EI + goto loop; + + case 0x27:{// DAA + unsigned a = rg.a; + int h = ph ^ cz; + if ( ph & 0x100 ) + { + if ( (h & 0x10) || (a & 0x0F) > 9 ) + a += 6; + + if ( (cz & 0x100) || a > 0x9F ) + a += 0x60; + } + else + { + if ( h & 0x10 ) + a = (a - 6) & 0xFF; + + if ( cz & 0x100 ) + a -= 0x60; + } + cz = (cz & 0x100) | a; // C=- Z=* + rg.a = a; + ph = (ph & 0x100) + BYTE( a ); // N=- H=0 + goto loop; + } + +// Special + + case 0x76: // HALT + case 0x10: // STOP + case 0xD3: case 0xDB: case 0xDD: // Illegal + case 0xE3: case 0xE4: case 0xEB: case 0xEC: case 0xED: // (all freeze CPU) + case 0xF4: case 0xFC: case 0xFD: + goto stop; + } + + // If this fails then an opcode isn't handled above + assert( false ); + +stop: + pc--; + + // copy state back + CPU.cpu_state_.time = time; + CPU.r.pc = pc; + CPU.r.sp = sp; + { + int t; + GET_FLAGS( t ); + rg.flags = t; + } + CPU.cpu_state = &CPU.cpu_state_; + STATIC_CAST(Gb_Cpu::core_regs_t&,CPU.r) = rp; +} diff --git a/Frameworks/GME/gme/Gb_Oscs.cpp b/Frameworks/GME/gme/Gb_Oscs.cpp index 08e06e31b..a1b681051 100644 --- a/Frameworks/GME/gme/Gb_Oscs.cpp +++ b/Frameworks/GME/gme/Gb_Oscs.cpp @@ -1,712 +1,712 @@ -// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ - -#include "Gb_Apu.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games -bool const cgb_05 = false; // enables CGB-05 zombie behavior - -int const trigger_mask = 0x80; -int const length_enabled = 0x40; - -void Gb_Osc::reset() -{ - output = NULL; - last_amp = 0; - delay = 0; - phase = 0; - enabled = false; -} - -inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) -{ - output->set_modified(); - int delta = new_amp - last_amp; - if ( delta ) - { - last_amp = new_amp; - fast_synth->offset( time, delta, output ); - } -} - -// Units - -void Gb_Osc::clock_length() -{ - if ( (regs [4] & length_enabled) && length_ctr ) - { - if ( --length_ctr <= 0 ) - enabled = false; - } -} - -inline int Gb_Env::reload_env_timer() -{ - int raw = regs [2] & 7; - env_delay = (raw ? raw : 8); - return raw; -} - -void Gb_Env::clock_envelope() -{ - if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) - { - int v = volume + (regs [2] & 0x08 ? +1 : -1); - if ( 0 <= v && v <= 15 ) - volume = v; - else - env_enabled = false; - } -} - -inline void Gb_Sweep_Square::reload_sweep_timer() -{ - sweep_delay = (regs [0] & period_mask) >> 4; - if ( !sweep_delay ) - sweep_delay = 8; -} - -void Gb_Sweep_Square::calc_sweep( bool update ) -{ - int const shift = regs [0] & shift_mask; - int const delta = sweep_freq >> shift; - sweep_neg = (regs [0] & 0x08) != 0; - int const freq = sweep_freq + (sweep_neg ? -delta : delta); - - if ( freq > 0x7FF ) - { - enabled = false; - } - else if ( shift && update ) - { - sweep_freq = freq; - - regs [3] = freq & 0xFF; - regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); - } -} - -void Gb_Sweep_Square::clock_sweep() -{ - if ( --sweep_delay <= 0 ) - { - reload_sweep_timer(); - if ( sweep_enabled && (regs [0] & period_mask) ) - { - calc_sweep( true ); - calc_sweep( false ); - } - } -} - -int Gb_Wave::access( int addr ) const -{ - if ( enabled ) - { - addr = phase & (bank_size - 1); - if ( mode == Gb_Apu::mode_dmg ) - { - addr++; - if ( delay > clk_mul ) - return -1; // can only access within narrow time window while playing - } - addr >>= 1; - } - return addr & 0x0F; -} - -// write_register - -int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) -{ - int data = regs [4]; - - if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr ) - { - if ( (data & length_enabled) || cgb_02 ) - length_ctr--; - } - - if ( data & trigger_mask ) - { - enabled = true; - if ( !length_ctr ) - { - length_ctr = max_len; - if ( (frame_phase & 1) && (data & length_enabled) ) - length_ctr--; - } - } - - if ( !length_ctr ) - enabled = false; - - return data & trigger_mask; -} - -inline void Gb_Env::zombie_volume( int old, int data ) -{ - int v = volume; - if ( mode == Gb_Apu::mode_agb || cgb_05 ) - { - // CGB-05 behavior, very close to AGB behavior as well - if ( (old ^ data) & 8 ) - { - if ( !(old & 8) ) - { - v++; - if ( old & 7 ) - v++; - } - - v = 16 - v; - } - else if ( (old & 0x0F) == 8 ) - { - v++; - } - } - else - { - // CGB-04&02 behavior, very close to MGB behavior as well - if ( !(old & 7) && env_enabled ) - v++; - else if ( !(old & 8) ) - v += 2; - - if ( (old ^ data) & 8 ) - v = 16 - v; - } - volume = v & 0x0F; -} - -bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) -{ - int const max_len = 64; - - switch ( reg ) - { - case 1: - length_ctr = max_len - (data & (max_len - 1)); - break; - - case 2: - if ( !dac_enabled() ) - enabled = false; - - zombie_volume( old, data ); - - if ( (data & 7) && env_delay == 8 ) - { - env_delay = 1; - clock_envelope(); // TODO: really happens at next length clock - } - break; - - case 4: - if ( write_trig( frame_phase, max_len, old ) ) - { - volume = regs [2] >> 4; - reload_env_timer(); - env_enabled = true; - if ( frame_phase == 7 ) - env_delay++; - if ( !dac_enabled() ) - enabled = false; - return true; - } - } - return false; -} - -bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) -{ - bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); - if ( result ) - delay = (delay & (4 * clk_mul - 1)) + period(); - return result; -} - -inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) -{ - if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) - { - phase = 0x7FFF; - delay += 8 * clk_mul; - } -} - -inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) -{ - if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) - enabled = false; // sweep negate disabled after used - - if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) - { - sweep_freq = frequency(); - sweep_neg = false; - reload_sweep_timer(); - sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0; - if ( regs [0] & shift_mask ) - calc_sweep( false ); - } -} - -void Gb_Wave::corrupt_wave() -{ - int pos = ((phase + 1) & (bank_size - 1)) >> 1; - if ( pos < 4 ) - wave_ram [0] = wave_ram [pos]; - else - for ( int i = 4; --i >= 0; ) - wave_ram [i] = wave_ram [(pos & ~3) + i]; -} - -inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) -{ - int const max_len = 256; - - switch ( reg ) - { - case 0: - if ( !dac_enabled() ) - enabled = false; - break; - - case 1: - length_ctr = max_len - data; - break; - - case 4: - bool was_enabled = enabled; - if ( write_trig( frame_phase, max_len, old_data ) ) - { - if ( !dac_enabled() ) - enabled = false; - else if ( mode == Gb_Apu::mode_dmg && was_enabled && - (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul ) - corrupt_wave(); - - phase = 0; - delay = period() + 6 * clk_mul; - } - } -} - -void Gb_Apu::write_osc( int reg, int old_data, int data ) -{ - int index = (reg * 3 + 3) >> 4; // avoids divide - assert( index == reg / 5 ); - reg -= index * 5; - switch ( index ) - { - case 0: square1.write_register( frame_phase, reg, old_data, data ); break; - case 1: square2.write_register( frame_phase, reg, old_data, data ); break; - case 2: wave .write_register( frame_phase, reg, old_data, data ); break; - case 3: noise .write_register( frame_phase, reg, old_data, data ); break; - } -} - -// Synthesis - -void Gb_Square::run( blip_time_t time, blip_time_t end_time ) -{ - // Calc duty and phase - static byte const duty_offsets [4] = { 1, 1, 3, 7 }; - static byte const duties [4] = { 1, 2, 4, 6 }; - int const duty_code = regs [1] >> 6; - int duty_offset = duty_offsets [duty_code]; - int duty = duties [duty_code]; - if ( mode == Gb_Apu::mode_agb ) - { - // AGB uses inverted duty - duty_offset -= duty; - duty = 8 - duty; - } - int ph = (this->phase + duty_offset) & 7; - - // Determine what will be generated - int vol = 0; - Blip_Buffer* const out = this->output; - if ( out ) - { - int amp = dac_off_amp; - if ( dac_enabled() ) - { - if ( enabled ) - vol = this->volume; - - amp = -dac_bias; - if ( mode == Gb_Apu::mode_agb ) - amp = -(vol >> 1); - - // Play inaudible frequencies as constant amplitude - if ( frequency() >= 0x7FA && delay < 32 * clk_mul ) - { - amp += (vol * duty) >> 3; - vol = 0; - } - - if ( ph < duty ) - { - amp += vol; - vol = -vol; - } - } - update_amp( time, amp ); - } - - // Generate wave - time += delay; - if ( time < end_time ) - { - int const per = this->period(); - if ( !vol ) - { - #if GB_APU_FAST - time = end_time; - #else - // Maintain phase when not playing - int count = (end_time - time + per - 1) / per; - ph += count; // will be masked below - time += (blip_time_t) count * per; - #endif - } - else - { - // Output amplitude transitions - int delta = vol; - do - { - ph = (ph + 1) & 7; - if ( ph == 0 || ph == duty ) - { - norm_synth->offset_inline( time, delta, out ); - delta = -delta; - } - time += per; - } - while ( time < end_time ); - - if ( delta != vol ) - last_amp -= delta; - } - this->phase = (ph - duty_offset) & 7; - } - delay = time - end_time; -} - -#if !GB_APU_FAST -// Quickly runs LFSR for a large number of clocks. For use when noise is generating -// no sound. -static unsigned run_lfsr( unsigned s, unsigned mask, int count ) -{ - bool const optimized = true; // set to false to use only unoptimized loop in middle - - // optimization used in several places: - // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n) - - if ( mask == 0x4000 && optimized ) - { - if ( count >= 32767 ) - count %= 32767; - - // Convert from Fibonacci to Galois configuration, - // shifted left 1 bit - s ^= (s & 1) * 0x8000; - - // Each iteration is equivalent to clocking LFSR 255 times - while ( (count -= 255) > 0 ) - s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); - count += 255; - - // Each iteration is equivalent to clocking LFSR 15 times - // (interesting similarity to single clocking below) - while ( (count -= 15) > 0 ) - s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); - count += 15; - - // Remaining singles - while ( --count >= 0 ) - s = ((s & 2) * (3 << 13)) ^ (s >> 1); - - // Convert back to Fibonacci configuration - s &= 0x7FFF; - } - else if ( count < 8 || !optimized ) - { - // won't fully replace upper 8 bits, so have to do the unoptimized way - while ( --count >= 0 ) - s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2)); - } - else - { - if ( count > 127 ) - { - count %= 127; - if ( !count ) - count = 127; // must run at least once - } - - // Need to keep one extra bit of history - s = s << 1 & 0xFF; - - // Convert from Fibonacci to Galois configuration, - // shifted left 2 bits - s ^= (s & 2) * 0x80; - - // Each iteration is equivalent to clocking LFSR 7 times - // (interesting similarity to single clocking below) - while ( (count -= 7) > 0 ) - s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); - count += 7; - - // Remaining singles - while ( --count >= 0 ) - s = ((s & 4) * (3 << 5)) ^ (s >> 1); - - // Convert back to Fibonacci configuration and - // repeat last 8 bits above significant 7 - s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); - } - - return s; -} -#endif - -void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) -{ - // Determine what will be generated - int vol = 0; - Blip_Buffer* const out = this->output; - if ( out ) - { - int amp = dac_off_amp; - if ( dac_enabled() ) - { - if ( enabled ) - vol = this->volume; - - amp = -dac_bias; - if ( mode == Gb_Apu::mode_agb ) - amp = -(vol >> 1); - - if ( !(phase & 1) ) - { - amp += vol; - vol = -vol; - } - } - - // AGB negates final output - if ( mode == Gb_Apu::mode_agb ) - { - vol = -vol; - amp = -amp; - } - - update_amp( time, amp ); - } - - // Run timer and calculate time of next LFSR clock - static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; - int const period1 = period1s [regs [3] & 7] * clk_mul; - - #if GB_APU_FAST - time += delay; - #else - { - int extra = (end_time - time) - delay; - int const per2 = this->period2(); - time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; - - int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); - divider = (divider - count) & period2_mask; - delay = count * period1 - extra; - } - #endif - - // Generate wave - if ( time < end_time ) - { - unsigned const mask = this->lfsr_mask(); - unsigned bits = this->phase; - - int per = period2( period1 * 8 ); - #if GB_APU_FAST - // Noise can be THE biggest time hog; adjust as necessary - int const min_period = 24; - if ( per < min_period ) - per = min_period; - #endif - if ( period2_index() >= 0xE ) - { - time = end_time; - } - else if ( !vol ) - { - #if GB_APU_FAST - time = end_time; - #else - // Maintain phase when not playing - int count = (end_time - time + per - 1) / per; - time += (blip_time_t) count * per; - bits = run_lfsr( bits, ~mask, count ); - #endif - } - else - { - Blip_Synth_Fast const* const synth = fast_synth; // cache - - // Output amplitude transitions - int delta = -vol; - do - { - unsigned changed = bits + 1; - bits = bits >> 1 & mask; - if ( changed & 2 ) - { - bits |= ~mask; - delta = -delta; - synth->offset_inline( time, delta, out ); - } - time += per; - } - while ( time < end_time ); - - if ( delta == vol ) - last_amp += delta; - } - this->phase = bits; - } - - #if GB_APU_FAST - delay = time - end_time; - #endif -} - -void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) -{ - // Calc volume -#if GB_APU_NO_AGB - static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 }; - int const volume_idx = regs [2] >> 5 & 3; - int const volume_shift = shifts [volume_idx]; - int const volume_mul = 1; -#else - static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; - int const volume_shift = 2 + 4; - int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB - int const volume_mul = volumes [volume_idx]; -#endif - - // Determine what will be generated - int playing = false; - Blip_Buffer* const out = this->output; - if ( out ) - { - int amp = dac_off_amp; - if ( dac_enabled() ) - { - // Play inaudible frequencies as constant amplitude - amp = 8 << 4; // really depends on average of all samples in wave - - // if delay is larger, constant amplitude won't start yet - if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) - { - if ( volume_mul && volume_shift != 4+4 ) - playing = (int) enabled; - - amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; - } - - amp = ((amp * volume_mul) >> volume_shift) - dac_bias; - } - update_amp( time, amp ); - } - - // Generate wave - time += delay; - if ( time < end_time ) - { - byte const* wave = this->wave_ram; - - // wave size and bank - #if GB_APU_NO_AGB - int const wave_mask = 0x1F; - int const swap_banks = 0; - #else - int const size20_mask = 0x20; - int const flags = regs [0] & agb_mask; - int const wave_mask = (flags & size20_mask) | 0x1F; - int swap_banks = 0; - if ( flags & bank40_mask ) - { - swap_banks = flags & size20_mask; - wave += bank_size/2 - (swap_banks >> 1); - } - #endif - - int ph = this->phase ^ swap_banks; - ph = (ph + 1) & wave_mask; // pre-advance - - int const per = this->period(); - if ( !playing ) - { - #if GB_APU_FAST - time = end_time; - #else - // Maintain phase when not playing - int count = (end_time - time + per - 1) / per; - ph += count; // will be masked below - time += (blip_time_t) count * per; - #endif - } - else - { - Blip_Synth_Fast const* const synth = fast_synth; // cache - - // Output amplitude transitions - int lamp = this->last_amp + dac_bias; - do - { - // Extract nibble - int nibble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; - ph = (ph + 1) & wave_mask; - - // Scale by volume - int amp = (nibble * volume_mul) >> volume_shift; - - int delta = amp - lamp; - if ( delta ) - { - lamp = amp; - synth->offset_inline( time, delta, out ); - } - time += per; - } - while ( time < end_time ); - this->last_amp = lamp - dac_bias; - } - ph = (ph - 1) & wave_mask; // undo pre-advance and mask position - - // Keep track of last byte read - if ( enabled ) - sample_buf = wave [ph >> 1]; - - this->phase = ph ^ swap_banks; // undo swapped banks - } - delay = time - end_time; -} +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games +bool const cgb_05 = false; // enables CGB-05 zombie behavior + +int const trigger_mask = 0x80; +int const length_enabled = 0x40; + +void Gb_Osc::reset() +{ + output = NULL; + last_amp = 0; + delay = 0; + phase = 0; + enabled = false; +} + +inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) +{ + output->set_modified(); + int delta = new_amp - last_amp; + if ( delta ) + { + last_amp = new_amp; + fast_synth->offset( time, delta, output ); + } +} + +// Units + +void Gb_Osc::clock_length() +{ + if ( (regs [4] & length_enabled) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } +} + +inline int Gb_Env::reload_env_timer() +{ + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; +} + +void Gb_Env::clock_envelope() +{ + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } +} + +inline void Gb_Sweep_Square::reload_sweep_timer() +{ + sweep_delay = (regs [0] & period_mask) >> 4; + if ( !sweep_delay ) + sweep_delay = 8; +} + +void Gb_Sweep_Square::calc_sweep( bool update ) +{ + int const shift = regs [0] & shift_mask; + int const delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + int const freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + { + enabled = false; + } + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } +} + +void Gb_Sweep_Square::clock_sweep() +{ + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & period_mask) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } +} + +int Gb_Wave::access( int addr ) const +{ + if ( enabled ) + { + addr = phase & (bank_size - 1); + if ( mode == Gb_Apu::mode_dmg ) + { + addr++; + if ( delay > clk_mul ) + return -1; // can only access within narrow time window while playing + } + addr >>= 1; + } + return addr & 0x0F; +} + +// write_register + +int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) +{ + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr ) + { + if ( (data & length_enabled) || cgb_02 ) + length_ctr--; + } + + if ( data & trigger_mask ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & length_enabled) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & trigger_mask; +} + +inline void Gb_Env::zombie_volume( int old, int data ) +{ + int v = volume; + if ( mode == Gb_Apu::mode_agb || cgb_05 ) + { + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + { + v++; + } + } + else + { + // CGB-04&02 behavior, very close to MGB behavior as well + if ( !(old & 7) && env_enabled ) + v++; + else if ( !(old & 8) ) + v += 2; + + if ( (old ^ data) & 8 ) + v = 16 - v; + } + volume = v & 0x0F; +} + +bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) +{ + int const max_len = 64; + + switch ( reg ) + { + case 1: + length_ctr = max_len - (data & (max_len - 1)); + break; + + case 2: + if ( !dac_enabled() ) + enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } + break; + + case 4: + if ( write_trig( frame_phase, max_len, old ) ) + { + volume = regs [2] >> 4; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !dac_enabled() ) + enabled = false; + return true; + } + } + return false; +} + +bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) +{ + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (4 * clk_mul - 1)) + period(); + return result; +} + +inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) +{ + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) + { + phase = 0x7FFF; + delay += 8 * clk_mul; + } +} + +inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) +{ + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used + + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) + { + sweep_freq = frequency(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0; + if ( regs [0] & shift_mask ) + calc_sweep( false ); + } +} + +void Gb_Wave::corrupt_wave() +{ + int pos = ((phase + 1) & (bank_size - 1)) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; +} + +inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) +{ + int const max_len = 256; + + switch ( reg ) + { + case 0: + if ( !dac_enabled() ) + enabled = false; + break; + + case 1: + length_ctr = max_len - data; + break; + + case 4: + bool was_enabled = enabled; + if ( write_trig( frame_phase, max_len, old_data ) ) + { + if ( !dac_enabled() ) + enabled = false; + else if ( mode == Gb_Apu::mode_dmg && was_enabled && + (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul ) + corrupt_wave(); + + phase = 0; + delay = period() + 6 * clk_mul; + } + } +} + +void Gb_Apu::write_osc( int reg, int old_data, int data ) +{ + int index = (reg * 3 + 3) >> 4; // avoids divide + assert( index == reg / 5 ); + reg -= index * 5; + switch ( index ) + { + case 0: square1.write_register( frame_phase, reg, old_data, data ); break; + case 1: square2.write_register( frame_phase, reg, old_data, data ); break; + case 2: wave .write_register( frame_phase, reg, old_data, data ); break; + case 3: noise .write_register( frame_phase, reg, old_data, data ); break; + } +} + +// Synthesis + +void Gb_Square::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc duty and phase + static byte const duty_offsets [4] = { 1, 1, 3, 7 }; + static byte const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int duty_offset = duty_offsets [duty_code]; + int duty = duties [duty_code]; + if ( mode == Gb_Apu::mode_agb ) + { + // AGB uses inverted duty + duty_offset -= duty; + duty = 8 - duty; + } + int ph = (this->phase + duty_offset) & 7; + + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + // Play inaudible frequencies as constant amplitude + if ( frequency() >= 0x7FA && delay < 32 * clk_mul ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + int const per = this->period(); + if ( !vol ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + #endif + } + else + { + // Output amplitude transitions + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + norm_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + this->phase = (ph - duty_offset) & 7; + } + delay = time - end_time; +} + +#if !GB_APU_FAST +// Quickly runs LFSR for a large number of clocks. For use when noise is generating +// no sound. +static unsigned run_lfsr( unsigned s, unsigned mask, int count ) +{ + bool const optimized = true; // set to false to use only unoptimized loop in middle + + // optimization used in several places: + // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n) + + if ( mask == 0x4000 && optimized ) + { + if ( count >= 32767 ) + count %= 32767; + + // Convert from Fibonacci to Galois configuration, + // shifted left 1 bit + s ^= (s & 1) * 0x8000; + + // Each iteration is equivalent to clocking LFSR 255 times + while ( (count -= 255) > 0 ) + s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); + count += 255; + + // Each iteration is equivalent to clocking LFSR 15 times + // (interesting similarity to single clocking below) + while ( (count -= 15) > 0 ) + s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); + count += 15; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 2) * (3 << 13)) ^ (s >> 1); + + // Convert back to Fibonacci configuration + s &= 0x7FFF; + } + else if ( count < 8 || !optimized ) + { + // won't fully replace upper 8 bits, so have to do the unoptimized way + while ( --count >= 0 ) + s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2)); + } + else + { + if ( count > 127 ) + { + count %= 127; + if ( !count ) + count = 127; // must run at least once + } + + // Need to keep one extra bit of history + s = s << 1 & 0xFF; + + // Convert from Fibonacci to Galois configuration, + // shifted left 2 bits + s ^= (s & 2) * 0x80; + + // Each iteration is equivalent to clocking LFSR 7 times + // (interesting similarity to single clocking below) + while ( (count -= 7) > 0 ) + s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); + count += 7; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 4) * (3 << 5)) ^ (s >> 1); + + // Convert back to Fibonacci configuration and + // repeat last 8 bits above significant 7 + s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); + } + + return s; +} +#endif + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + if ( !(phase & 1) ) + { + amp += vol; + vol = -vol; + } + } + + // AGB negates final output + if ( mode == Gb_Apu::mode_agb ) + { + vol = -vol; + amp = -amp; + } + + update_amp( time, amp ); + } + + // Run timer and calculate time of next LFSR clock + static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * clk_mul; + + #if GB_APU_FAST + time += delay; + #else + { + int extra = (end_time - time) - delay; + int const per2 = this->period2(); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & period2_mask; + delay = count * period1 - extra; + } + #endif + + // Generate wave + if ( time < end_time ) + { + unsigned const mask = this->lfsr_mask(); + unsigned bits = this->phase; + + int per = period2( period1 * 8 ); + #if GB_APU_FAST + // Noise can be THE biggest time hog; adjust as necessary + int const min_period = 24; + if ( per < min_period ) + per = min_period; + #endif + if ( period2_index() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + time += (blip_time_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + #endif + } + else + { + Blip_Synth_Fast const* const synth = fast_synth; // cache + + // Output amplitude transitions + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + this->phase = bits; + } + + #if GB_APU_FAST + delay = time - end_time; + #endif +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc volume +#if GB_APU_NO_AGB + static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 }; + int const volume_idx = regs [2] >> 5 & 3; + int const volume_shift = shifts [volume_idx]; + int const volume_mul = 1; +#else + static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_shift = 2 + 4; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB + int const volume_mul = volumes [volume_idx]; +#endif + + // Determine what will be generated + int playing = false; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + // Play inaudible frequencies as constant amplitude + amp = 8 << 4; // really depends on average of all samples in wave + + // if delay is larger, constant amplitude won't start yet + if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) + { + if ( volume_mul && volume_shift != 4+4 ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> volume_shift) - dac_bias; + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + byte const* wave = this->wave_ram; + + // wave size and bank + #if GB_APU_NO_AGB + int const wave_mask = 0x1F; + int const swap_banks = 0; + #else + int const size20_mask = 0x20; + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & size20_mask) | 0x1F; + int swap_banks = 0; + if ( flags & bank40_mask ) + { + swap_banks = flags & size20_mask; + wave += bank_size/2 - (swap_banks >> 1); + } + #endif + + int ph = this->phase ^ swap_banks; + ph = (ph + 1) & wave_mask; // pre-advance + + int const per = this->period(); + if ( !playing ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + #endif + } + else + { + Blip_Synth_Fast const* const synth = fast_synth; // cache + + // Output amplitude transitions + int lamp = this->last_amp + dac_bias; + do + { + // Extract nibble + int nibble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + // Scale by volume + int amp = (nibble * volume_mul) >> volume_shift; + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + this->last_amp = lamp - dac_bias; + } + ph = (ph - 1) & wave_mask; // undo pre-advance and mask position + + // Keep track of last byte read + if ( enabled ) + sample_buf = wave [ph >> 1]; + + this->phase = ph ^ swap_banks; // undo swapped banks + } + delay = time - end_time; +} diff --git a/Frameworks/GME/gme/Gb_Oscs.h b/Frameworks/GME/gme/Gb_Oscs.h index cbbb1f3ee..1151b0a24 100644 --- a/Frameworks/GME/gme/Gb_Oscs.h +++ b/Frameworks/GME/gme/Gb_Oscs.h @@ -1,188 +1,188 @@ -// Private oscillators used by Gb_Apu - -// Gb_Snd_Emu $vers -#ifndef GB_OSCS_H -#define GB_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -#ifndef GB_APU_OVERCLOCK - #define GB_APU_OVERCLOCK 1 -#endif - -#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1) - #error "GB_APU_OVERCLOCK must be a power of 2" -#endif - -class Gb_Osc { -protected: - - // 11-bit frequency in NRx3 and NRx4 - int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } - - void update_amp( blip_time_t, int new_amp ); - int write_trig( int frame_phase, int max_len, int old_data ); -public: - - enum { clk_mul = GB_APU_OVERCLOCK }; - enum { dac_bias = 7 }; - - Blip_Buffer* outputs [4];// NULL, right, left, center - Blip_Buffer* output; // where to output sound - BOOST::uint8_t* regs; // osc's 5 registers - int mode; // mode_dmg, mode_cgb, mode_agb - int dac_off_amp;// amplitude when DAC is off - int last_amp; // current amplitude in Blip_Buffer - Blip_Synth_Norm const* norm_synth; - Blip_Synth_Fast const* fast_synth; - - int delay; // clocks until frequency timer expires - int length_ctr; // length counter - unsigned phase; // waveform phase (or equivalent) - bool enabled; // internal enabled flag - - void clock_length(); - void reset(); -}; - -class Gb_Env : public Gb_Osc { -public: - int env_delay; - int volume; - bool env_enabled; - - void clock_envelope(); - bool write_register( int frame_phase, int reg, int old_data, int data ); - - void reset() - { - env_delay = 0; - volume = 0; - Gb_Osc::reset(); - } -protected: - // Non-zero if DAC is enabled - int dac_enabled() const { return regs [2] & 0xF8; } -private: - void zombie_volume( int old, int data ); - int reload_env_timer(); -}; - -class Gb_Square : public Gb_Env { -public: - bool write_register( int frame_phase, int reg, int old_data, int data ); - void run( blip_time_t, blip_time_t ); - - void reset() - { - Gb_Env::reset(); - delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger) - } -private: - // Frequency timer period - int period() const { return (2048 - frequency()) * (4 * clk_mul); } -}; - -class Gb_Sweep_Square : public Gb_Square { -public: - int sweep_freq; - int sweep_delay; - bool sweep_enabled; - bool sweep_neg; - - void clock_sweep(); - void write_register( int frame_phase, int reg, int old_data, int data ); - - void reset() - { - sweep_freq = 0; - sweep_delay = 0; - sweep_enabled = false; - sweep_neg = false; - Gb_Square::reset(); - } -private: - enum { period_mask = 0x70 }; - enum { shift_mask = 0x07 }; - - void calc_sweep( bool update ); - void reload_sweep_timer(); -}; - -class Gb_Noise : public Gb_Env { -public: - int divider; // noise has more complex frequency divider setup - - void run( blip_time_t, blip_time_t ); - void write_register( int frame_phase, int reg, int old_data, int data ); - - void reset() - { - divider = 0; - Gb_Env::reset(); - delay = 4 * clk_mul; // TODO: remove? - } - -private: - enum { period2_mask = 0x1FFFF }; - - int period2_index() const { return regs [3] >> 4; } - int period2( int base = 8 ) const { return base << period2_index(); } - unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } -}; - -class Gb_Wave : public Gb_Osc { -public: - int sample_buf; // last wave RAM byte read (hardware has this as well) - - void write_register( int frame_phase, int reg, int old_data, int data ); - void run( blip_time_t, blip_time_t ); - - // Reads/writes wave RAM - int read( int addr ) const; - void write( int addr, int data ); - - void reset() - { - sample_buf = 0; - Gb_Osc::reset(); - } - -private: - enum { bank40_mask = 0x40 }; - enum { bank_size = 32 }; - - int agb_mask; // 0xFF if AGB features enabled, 0 otherwise - BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU - - friend class Gb_Apu; - - // Frequency timer period - int period() const { return (2048 - frequency()) * (2 * clk_mul); } - - // Non-zero if DAC is enabled - int dac_enabled() const { return regs [0] & 0x80; } - - void corrupt_wave(); - - BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } - - // Wave index that would be accessed, or -1 if no access would occur - int access( int addr ) const; -}; - -inline int Gb_Wave::read( int addr ) const -{ - int index = access( addr ); - return (index < 0 ? 0xFF : wave_bank() [index]); -} - -inline void Gb_Wave::write( int addr, int data ) -{ - int index = access( addr ); - if ( index >= 0 ) - wave_bank() [index] = data;; -} - -#endif +// Private oscillators used by Gb_Apu + +// Gb_Snd_Emu $vers +#ifndef GB_OSCS_H +#define GB_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +#ifndef GB_APU_OVERCLOCK + #define GB_APU_OVERCLOCK 1 +#endif + +#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1) + #error "GB_APU_OVERCLOCK must be a power of 2" +#endif + +class Gb_Osc { +protected: + + // 11-bit frequency in NRx3 and NRx4 + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } + + void update_amp( blip_time_t, int new_amp ); + int write_trig( int frame_phase, int max_len, int old_data ); +public: + + enum { clk_mul = GB_APU_OVERCLOCK }; + enum { dac_bias = 7 }; + + Blip_Buffer* outputs [4];// NULL, right, left, center + Blip_Buffer* output; // where to output sound + BOOST::uint8_t* regs; // osc's 5 registers + int mode; // mode_dmg, mode_cgb, mode_agb + int dac_off_amp;// amplitude when DAC is off + int last_amp; // current amplitude in Blip_Buffer + Blip_Synth_Norm const* norm_synth; + Blip_Synth_Fast const* fast_synth; + + int delay; // clocks until frequency timer expires + int length_ctr; // length counter + unsigned phase; // waveform phase (or equivalent) + bool enabled; // internal enabled flag + + void clock_length(); + void reset(); +}; + +class Gb_Env : public Gb_Osc { +public: + int env_delay; + int volume; + bool env_enabled; + + void clock_envelope(); + bool write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + env_delay = 0; + volume = 0; + Gb_Osc::reset(); + } +protected: + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [2] & 0xF8; } +private: + void zombie_volume( int old, int data ); + int reload_env_timer(); +}; + +class Gb_Square : public Gb_Env { +public: + bool write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + void reset() + { + Gb_Env::reset(); + delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger) + } +private: + // Frequency timer period + int period() const { return (2048 - frequency()) * (4 * clk_mul); } +}; + +class Gb_Sweep_Square : public Gb_Square { +public: + int sweep_freq; + int sweep_delay; + bool sweep_enabled; + bool sweep_neg; + + void clock_sweep(); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + sweep_freq = 0; + sweep_delay = 0; + sweep_enabled = false; + sweep_neg = false; + Gb_Square::reset(); + } +private: + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; + + void calc_sweep( bool update ); + void reload_sweep_timer(); +}; + +class Gb_Noise : public Gb_Env { +public: + int divider; // noise has more complex frequency divider setup + + void run( blip_time_t, blip_time_t ); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + divider = 0; + Gb_Env::reset(); + delay = 4 * clk_mul; // TODO: remove? + } + +private: + enum { period2_mask = 0x1FFFF }; + + int period2_index() const { return regs [3] >> 4; } + int period2( int base = 8 ) const { return base << period2_index(); } + unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } +}; + +class Gb_Wave : public Gb_Osc { +public: + int sample_buf; // last wave RAM byte read (hardware has this as well) + + void write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + // Reads/writes wave RAM + int read( int addr ) const; + void write( int addr, int data ); + + void reset() + { + sample_buf = 0; + Gb_Osc::reset(); + } + +private: + enum { bank40_mask = 0x40 }; + enum { bank_size = 32 }; + + int agb_mask; // 0xFF if AGB features enabled, 0 otherwise + BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU + + friend class Gb_Apu; + + // Frequency timer period + int period() const { return (2048 - frequency()) * (2 * clk_mul); } + + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [0] & 0x80; } + + void corrupt_wave(); + + BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } + + // Wave index that would be accessed, or -1 if no access would occur + int access( int addr ) const; +}; + +inline int Gb_Wave::read( int addr ) const +{ + int index = access( addr ); + return (index < 0 ? 0xFF : wave_bank() [index]); +} + +inline void Gb_Wave::write( int addr, int data ) +{ + int index = access( addr ); + if ( index >= 0 ) + wave_bank() [index] = data;; +} + +#endif diff --git a/Frameworks/GME/gme/Gbs_Core.cpp b/Frameworks/GME/gme/Gbs_Core.cpp index 5703db69b..3fcfd8305 100644 --- a/Frameworks/GME/gme/Gbs_Core.cpp +++ b/Frameworks/GME/gme/Gbs_Core.cpp @@ -1,208 +1,208 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Gbs_Core.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const tempo_unit = 16; -int const idle_addr = 0xF00D; -int const bank_size = 0x4000; - -Gbs_Core::Gbs_Core() : rom( bank_size ) -{ - tempo = tempo_unit; - assert( offsetof (header_t,copyright [32]) == header_t::size ); -} - -Gbs_Core::~Gbs_Core() { } - -void Gbs_Core::unload() -{ - header_.timer_mode = 0; // set_tempo() reads this - rom.clear(); - Gme_Loader::unload(); -} - -bool Gbs_Core::header_t::valid_tag() const -{ - return 0 == memcmp( tag, "GBS", 3 ); -} - -blargg_err_t Gbs_Core::load_( Data_Reader& in ) -{ - RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) ); - - if ( !header_.valid_tag() ) - return blargg_err_file_type; - - if ( header_.vers != 1 ) - set_warning( "Unknown file version" ); - - if ( header_.timer_mode & 0x78 ) - set_warning( "Invalid timer mode" ); - - addr_t load_addr = get_le16( header_.load_addr ); - if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || - load_addr < 0x400 ) - set_warning( "Invalid load/init/play address" ); - - cpu.rst_base = load_addr; - rom.set_addr( load_addr ); - - return blargg_ok; -} - -void Gbs_Core::set_bank( int n ) -{ - addr_t addr = rom.mask_addr( n * bank_size ); - if ( addr == 0 && rom.size() > bank_size ) - addr = bank_size; // MBC1&2 behavior, bank 0 acts like bank 1 - cpu.map_code( bank_size, bank_size, rom.at_addr( addr ) ); -} - -void Gbs_Core::update_timer() -{ - play_period_ = 70224 / tempo_unit; // 59.73 Hz - - if ( header_.timer_mode & 0x04 ) - { - // Using custom rate - static byte const rates [4] = { 6, 0, 2, 4 }; - // TODO: emulate double speed CPU mode rather than halving timer rate - int double_speed = header_.timer_mode >> 7; - int shift = rates [ram [hi_page + 7] & 3] - double_speed; - play_period_ = (256 - ram [hi_page + 6]) << shift; - } - - play_period_ *= tempo; -} - -void Gbs_Core::set_tempo( double t ) -{ - tempo = (int) (tempo_unit / t + 0.5); - apu_.set_tempo( t ); - update_timer(); -} - -// Jumps to routine, given pointer to address in file header. Pushes idle_addr -// as return address, NOT old PC. -void Gbs_Core::jsr_then_stop( byte const addr [] ) -{ - check( cpu.r.sp == get_le16( header_.stack_ptr ) ); - cpu.r.pc = get_le16( addr ); - write_mem( --cpu.r.sp, idle_addr >> 8 ); - write_mem( --cpu.r.sp, idle_addr ); -} - -blargg_err_t Gbs_Core::start_track( int track, Gb_Apu::mode_t mode ) -{ - // Reset APU to state expected by most rips - static byte const sound_data [] = { - 0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled - 0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled - 0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave DAC disabled - 0x00, 0xFF, 0x00, 0x00, 0xB8, // noise DAC disabled - 0x77, 0xFF, 0x80, // max volume, all chans in center, power on - }; - apu_.reset( mode ); - apu_.write_register( 0, 0xFF26, 0x80 ); // power on - for ( int i = 0; i < (int) sizeof sound_data; i++ ) - apu_.write_register( 0, i + apu_.io_addr, sound_data [i] ); - apu_.end_frame( 1 ); // necessary to get click out of the way - - // Init memory and I/O registers - memset( ram, 0, 0x4000 ); - memset( ram + 0x4000, 0xFF, 0x1F80 ); - memset( ram + 0x5F80, 0, sizeof ram - 0x5F80 ); - ram [hi_page] = 0; // joypad reads back as 0 - ram [idle_addr - ram_addr] = 0xED; // illegal instruction - ram [hi_page + 6] = header_.timer_modulo; - ram [hi_page + 7] = header_.timer_mode; - - // Map memory - cpu.reset( rom.unmapped() ); - cpu.map_code( ram_addr, 0x10000 - ram_addr, ram ); - cpu.map_code( 0, bank_size, rom.at_addr( 0 ) ); - set_bank( rom.size() > bank_size ); - - // CPU registers, timing - update_timer(); - next_play = play_period_; - cpu.r.fa = track; - cpu.r.sp = get_le16( header_.stack_ptr ); - jsr_then_stop( header_.init_addr ); - - return blargg_ok; -} - -blargg_err_t Gbs_Core::run_until( int end ) -{ - end_time = end; - cpu.set_time( cpu.time() - end ); - while ( true ) - { - run_cpu(); - if ( cpu.time() >= 0 ) - break; - - if ( cpu.r.pc == idle_addr ) - { - if ( next_play > end_time ) - { - cpu.set_time( 0 ); - break; - } - - if ( cpu.time() < next_play - end_time ) - cpu.set_time( next_play - end_time ); - next_play += play_period_; - jsr_then_stop( header_.play_addr ); - } - else if ( cpu.r.pc > 0xFFFF ) - { - dprintf( "PC wrapped around\n" ); - cpu.r.pc &= 0xFFFF; - } - else - { - set_warning( "Emulation error (illegal/unsupported instruction)" ); - dprintf( "Bad opcode $%02X at $%04X\n", - (int) *cpu.get_code( cpu.r.pc ), (int) cpu.r.pc ); - cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF; - cpu.set_time( cpu.time() + 6 ); - } - } - - return blargg_ok; -} - -blargg_err_t Gbs_Core::end_frame( int end ) -{ - RETURN_ERR( run_until( end ) ); - - next_play -= end; - if ( next_play < 0 ) // happens when play routine takes too long - { - #if !GBS_IGNORE_STARVED_PLAY - check( false ); - #endif - next_play = 0; - } - - apu_.end_frame( end ); - - return blargg_ok; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gbs_Core.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const tempo_unit = 16; +int const idle_addr = 0xF00D; +int const bank_size = 0x4000; + +Gbs_Core::Gbs_Core() : rom( bank_size ) +{ + tempo = tempo_unit; + assert( offsetof (header_t,copyright [32]) == header_t::size ); +} + +Gbs_Core::~Gbs_Core() { } + +void Gbs_Core::unload() +{ + header_.timer_mode = 0; // set_tempo() reads this + rom.clear(); + Gme_Loader::unload(); +} + +bool Gbs_Core::header_t::valid_tag() const +{ + return 0 == memcmp( tag, "GBS", 3 ); +} + +blargg_err_t Gbs_Core::load_( Data_Reader& in ) +{ + RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) ); + + if ( !header_.valid_tag() ) + return blargg_err_file_type; + + if ( header_.vers != 1 ) + set_warning( "Unknown file version" ); + + if ( header_.timer_mode & 0x78 ) + set_warning( "Invalid timer mode" ); + + addr_t load_addr = get_le16( header_.load_addr ); + if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || + load_addr < 0x400 ) + set_warning( "Invalid load/init/play address" ); + + cpu.rst_base = load_addr; + rom.set_addr( load_addr ); + + return blargg_ok; +} + +void Gbs_Core::set_bank( int n ) +{ + addr_t addr = rom.mask_addr( n * bank_size ); + if ( addr == 0 && rom.size() > bank_size ) + addr = bank_size; // MBC1&2 behavior, bank 0 acts like bank 1 + cpu.map_code( bank_size, bank_size, rom.at_addr( addr ) ); +} + +void Gbs_Core::update_timer() +{ + play_period_ = 70224 / tempo_unit; // 59.73 Hz + + if ( header_.timer_mode & 0x04 ) + { + // Using custom rate + static byte const rates [4] = { 6, 0, 2, 4 }; + // TODO: emulate double speed CPU mode rather than halving timer rate + int double_speed = header_.timer_mode >> 7; + int shift = rates [ram [hi_page + 7] & 3] - double_speed; + play_period_ = (256 - ram [hi_page + 6]) << shift; + } + + play_period_ *= tempo; +} + +void Gbs_Core::set_tempo( double t ) +{ + tempo = (int) (tempo_unit / t + 0.5); + apu_.set_tempo( t ); + update_timer(); +} + +// Jumps to routine, given pointer to address in file header. Pushes idle_addr +// as return address, NOT old PC. +void Gbs_Core::jsr_then_stop( byte const addr [] ) +{ + check( cpu.r.sp == get_le16( header_.stack_ptr ) ); + cpu.r.pc = get_le16( addr ); + write_mem( --cpu.r.sp, idle_addr >> 8 ); + write_mem( --cpu.r.sp, idle_addr ); +} + +blargg_err_t Gbs_Core::start_track( int track, Gb_Apu::mode_t mode ) +{ + // Reset APU to state expected by most rips + static byte const sound_data [] = { + 0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled + 0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled + 0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave DAC disabled + 0x00, 0xFF, 0x00, 0x00, 0xB8, // noise DAC disabled + 0x77, 0xFF, 0x80, // max volume, all chans in center, power on + }; + apu_.reset( mode ); + apu_.write_register( 0, 0xFF26, 0x80 ); // power on + for ( int i = 0; i < (int) sizeof sound_data; i++ ) + apu_.write_register( 0, i + apu_.io_addr, sound_data [i] ); + apu_.end_frame( 1 ); // necessary to get click out of the way + + // Init memory and I/O registers + memset( ram, 0, 0x4000 ); + memset( ram + 0x4000, 0xFF, 0x1F80 ); + memset( ram + 0x5F80, 0, sizeof ram - 0x5F80 ); + ram [hi_page] = 0; // joypad reads back as 0 + ram [idle_addr - ram_addr] = 0xED; // illegal instruction + ram [hi_page + 6] = header_.timer_modulo; + ram [hi_page + 7] = header_.timer_mode; + + // Map memory + cpu.reset( rom.unmapped() ); + cpu.map_code( ram_addr, 0x10000 - ram_addr, ram ); + cpu.map_code( 0, bank_size, rom.at_addr( 0 ) ); + set_bank( rom.size() > bank_size ); + + // CPU registers, timing + update_timer(); + next_play = play_period_; + cpu.r.fa = track; + cpu.r.sp = get_le16( header_.stack_ptr ); + jsr_then_stop( header_.init_addr ); + + return blargg_ok; +} + +blargg_err_t Gbs_Core::run_until( int end ) +{ + end_time = end; + cpu.set_time( cpu.time() - end ); + while ( true ) + { + run_cpu(); + if ( cpu.time() >= 0 ) + break; + + if ( cpu.r.pc == idle_addr ) + { + if ( next_play > end_time ) + { + cpu.set_time( 0 ); + break; + } + + if ( cpu.time() < next_play - end_time ) + cpu.set_time( next_play - end_time ); + next_play += play_period_; + jsr_then_stop( header_.play_addr ); + } + else if ( cpu.r.pc > 0xFFFF ) + { + dprintf( "PC wrapped around\n" ); + cpu.r.pc &= 0xFFFF; + } + else + { + set_warning( "Emulation error (illegal/unsupported instruction)" ); + dprintf( "Bad opcode $%02X at $%04X\n", + (int) *cpu.get_code( cpu.r.pc ), (int) cpu.r.pc ); + cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF; + cpu.set_time( cpu.time() + 6 ); + } + } + + return blargg_ok; +} + +blargg_err_t Gbs_Core::end_frame( int end ) +{ + RETURN_ERR( run_until( end ) ); + + next_play -= end; + if ( next_play < 0 ) // happens when play routine takes too long + { + #if !GBS_IGNORE_STARVED_PLAY + check( false ); + #endif + next_play = 0; + } + + apu_.end_frame( end ); + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Gbs_Core.h b/Frameworks/GME/gme/Gbs_Core.h index 833f807e6..273495604 100644 --- a/Frameworks/GME/gme/Gbs_Core.h +++ b/Frameworks/GME/gme/Gbs_Core.h @@ -1,109 +1,109 @@ -// Nintendo Game Boy GBS music file emulator core - -// Game_Music_Emu $vers -#ifndef GBS_CORE_H -#define GBS_CORE_H - -#include "Gme_Loader.h" -#include "Rom_Data.h" -#include "Gb_Cpu.h" -#include "Gb_Apu.h" - -class Gbs_Core : public Gme_Loader { -public: - - // GBS file header - struct header_t - { - enum { size = 112 }; - - char tag [ 3]; - byte vers; - byte track_count; - byte first_track; - byte load_addr [ 2]; - byte init_addr [ 2]; - byte play_addr [ 2]; - byte stack_ptr [ 2]; - byte timer_modulo; - byte timer_mode; - char game [32]; // strings can be 32 chars, NOT terminated - char author [32]; - char copyright [32]; - - // True if header has valid file signature - bool valid_tag() const; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - // Sound chip - Gb_Apu& apu() { return apu_; } - - // ROM data - Rom_Data const& rom_() const { return rom; } - - // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. - void set_tempo( double ); - - // Starts track, where 0 is the first. Uses specified APU mode. - blargg_err_t start_track( int, Gb_Apu::mode_t = Gb_Apu::mode_cgb ); - - // Ends time frame at time t - typedef int time_t; // clock count - blargg_err_t end_frame( time_t t ); - - // Clocks between calls to play routine - time_t play_period() const { return play_period_; } - -protected: - typedef int addr_t; - - // Current time - time_t time() const { return cpu.time() + end_time; } - - // Runs emulator to time t - blargg_err_t run_until( time_t t ); - - // Runs CPU until time becomes >= 0 - void run_cpu(); - - // Reads/writes memory and I/O - int read_mem( addr_t ); - void write_mem( addr_t, int ); - -// Implementation -public: - Gbs_Core(); - ~Gbs_Core(); - virtual void unload(); - -protected: - virtual blargg_err_t load_( Data_Reader& ); - -private: - enum { ram_addr = 0xA000 }; - enum { io_base = 0xFF00 }; - enum { hi_page = io_base - ram_addr }; - - Rom_Data rom; - int tempo; - time_t end_time; - time_t play_period_; - time_t next_play; - header_t header_; - Gb_Cpu cpu; - Gb_Apu apu_; - byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; - - void update_timer(); - void jsr_then_stop( byte const [] ); - void set_bank( int n ); - void write_io_inline( int offset, int data, int base ); - void write_io_( int offset, int data ); - int read_io( int offset ); - void write_io( int offset, int data ); -}; - -#endif +// Nintendo Game Boy GBS music file emulator core + +// Game_Music_Emu $vers +#ifndef GBS_CORE_H +#define GBS_CORE_H + +#include "Gme_Loader.h" +#include "Rom_Data.h" +#include "Gb_Cpu.h" +#include "Gb_Apu.h" + +class Gbs_Core : public Gme_Loader { +public: + + // GBS file header + struct header_t + { + enum { size = 112 }; + + char tag [ 3]; + byte vers; + byte track_count; + byte first_track; + byte load_addr [ 2]; + byte init_addr [ 2]; + byte play_addr [ 2]; + byte stack_ptr [ 2]; + byte timer_modulo; + byte timer_mode; + char game [32]; // strings can be 32 chars, NOT terminated + char author [32]; + char copyright [32]; + + // True if header has valid file signature + bool valid_tag() const; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + // Sound chip + Gb_Apu& apu() { return apu_; } + + // ROM data + Rom_Data const& rom_() const { return rom; } + + // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. + void set_tempo( double ); + + // Starts track, where 0 is the first. Uses specified APU mode. + blargg_err_t start_track( int, Gb_Apu::mode_t = Gb_Apu::mode_cgb ); + + // Ends time frame at time t + typedef int time_t; // clock count + blargg_err_t end_frame( time_t t ); + + // Clocks between calls to play routine + time_t play_period() const { return play_period_; } + +protected: + typedef int addr_t; + + // Current time + time_t time() const { return cpu.time() + end_time; } + + // Runs emulator to time t + blargg_err_t run_until( time_t t ); + + // Runs CPU until time becomes >= 0 + void run_cpu(); + + // Reads/writes memory and I/O + int read_mem( addr_t ); + void write_mem( addr_t, int ); + +// Implementation +public: + Gbs_Core(); + ~Gbs_Core(); + virtual void unload(); + +protected: + virtual blargg_err_t load_( Data_Reader& ); + +private: + enum { ram_addr = 0xA000 }; + enum { io_base = 0xFF00 }; + enum { hi_page = io_base - ram_addr }; + + Rom_Data rom; + int tempo; + time_t end_time; + time_t play_period_; + time_t next_play; + header_t header_; + Gb_Cpu cpu; + Gb_Apu apu_; + byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; + + void update_timer(); + void jsr_then_stop( byte const [] ); + void set_bank( int n ); + void write_io_inline( int offset, int data, int base ); + void write_io_( int offset, int data ); + int read_io( int offset ); + void write_io( int offset, int data ); +}; + +#endif diff --git a/Frameworks/GME/gme/Gbs_Cpu.cpp b/Frameworks/GME/gme/Gbs_Cpu.cpp index 4f5751aa0..6a5f99881 100644 --- a/Frameworks/GME/gme/Gbs_Cpu.cpp +++ b/Frameworks/GME/gme/Gbs_Cpu.cpp @@ -1,134 +1,134 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Gbs_Core.h" - -#include "blargg_endian.h" - -//#include "gb_cpu_log.h" - -/* Copyright (C) 2003-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifndef LOG_MEM - #define LOG_MEM( addr, str, data ) data -#endif - -int Gbs_Core::read_mem( addr_t addr ) -{ - int result = *cpu.get_code( addr ); - if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size ) - result = apu_.read_register( time(), addr ); - -#ifndef NDEBUG - else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) - dprintf( "Unmapped read $%04X\n", (unsigned) addr ); - else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 && addr != 0xFF70 && addr != 0xFF05 ) - dprintf( "Unmapped read $%04X\n", (unsigned) addr ); -#endif - - return LOG_MEM( addr, ">", result ); -} - -inline void Gbs_Core::write_io_inline( int offset, int data, int base ) -{ - if ( (unsigned) (offset - (apu_.io_addr - base)) < apu_.io_size ) - apu_.write_register( time(), offset + base, data & 0xFF ); - else if ( (unsigned) (offset - (0xFF06 - base)) < 2 ) - update_timer(); - else if ( offset == io_base - base ) - ram [base - ram_addr + offset] = 0; // keep joypad return value 0 - else - ram [base - ram_addr + offset] = 0xFF; - - //if ( offset == 0xFFFF - base ) - // dprintf( "Wrote interrupt mask\n" ); -} - -void Gbs_Core::write_mem( addr_t addr, int data ) -{ - (void) LOG_MEM( addr, "<", data ); - - int offset = addr - ram_addr; - if ( (unsigned) offset < 0x10000 - ram_addr ) - { - ram [offset] = data; - - offset -= 0xE000 - ram_addr; - if ( (unsigned) offset < 0x1F80 ) - write_io_inline( offset, data, 0xE000 ); - } - else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 ) - { - set_bank( data & 0xFF ); - } -#ifndef NDEBUG - else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) - { - dprintf( "Unmapped write $%04X\n", (unsigned) addr ); - } -#endif -} - -void Gbs_Core::write_io_( int offset, int data ) -{ - write_io_inline( offset, data, io_base ); -} - -inline void Gbs_Core::write_io( int offset, int data ) -{ - (void) LOG_MEM( offset + io_base, "<", data ); - - ram [io_base - ram_addr + offset] = data; - if ( (unsigned) offset < 0x80 ) - write_io_( offset, data ); -} - -int Gbs_Core::read_io( int offset ) -{ - int const io_base = 0xFF00; - int result = ram [io_base - ram_addr + offset]; - - if ( (unsigned) (offset - (apu_.io_addr - io_base)) < apu_.io_size ) - { - result = apu_.read_register( time(), offset + io_base ); - (void) LOG_MEM( offset + io_base, ">", result ); - } - else - { - check( result == read_mem( offset + io_base ) ); - } - return result; -} - -#define READ_FAST( addr, out ) \ -{\ - out = READ_CODE( addr );\ - if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )\ - out = LOG_MEM( addr, ">", apu_.read_register( TIME() + end_time, addr ) );\ - else\ - check( out == read_mem( addr ) );\ -} - -#define READ_MEM( addr ) read_mem( addr ) -#define WRITE_MEM( addr, data ) write_mem( addr, data ) - -#define WRITE_IO( addr, data ) write_io( addr, data ) -#define READ_IO( addr, out ) out = read_io( addr ) - -#define CPU cpu - -#define CPU_BEGIN \ -void Gbs_Core::run_cpu()\ -{ - #include "Gb_Cpu_run.h" -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gbs_Core.h" + +#include "blargg_endian.h" + +//#include "gb_cpu_log.h" + +/* Copyright (C) 2003-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifndef LOG_MEM + #define LOG_MEM( addr, str, data ) data +#endif + +int Gbs_Core::read_mem( addr_t addr ) +{ + int result = *cpu.get_code( addr ); + if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size ) + result = apu_.read_register( time(), addr ); + +#ifndef NDEBUG + else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) + dprintf( "Unmapped read $%04X\n", (unsigned) addr ); + else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 && addr != 0xFF70 && addr != 0xFF05 ) + dprintf( "Unmapped read $%04X\n", (unsigned) addr ); +#endif + + return LOG_MEM( addr, ">", result ); +} + +inline void Gbs_Core::write_io_inline( int offset, int data, int base ) +{ + if ( (unsigned) (offset - (apu_.io_addr - base)) < apu_.io_size ) + apu_.write_register( time(), offset + base, data & 0xFF ); + else if ( (unsigned) (offset - (0xFF06 - base)) < 2 ) + update_timer(); + else if ( offset == io_base - base ) + ram [base - ram_addr + offset] = 0; // keep joypad return value 0 + else + ram [base - ram_addr + offset] = 0xFF; + + //if ( offset == 0xFFFF - base ) + // dprintf( "Wrote interrupt mask\n" ); +} + +void Gbs_Core::write_mem( addr_t addr, int data ) +{ + (void) LOG_MEM( addr, "<", data ); + + int offset = addr - ram_addr; + if ( (unsigned) offset < 0x10000 - ram_addr ) + { + ram [offset] = data; + + offset -= 0xE000 - ram_addr; + if ( (unsigned) offset < 0x1F80 ) + write_io_inline( offset, data, 0xE000 ); + } + else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 ) + { + set_bank( data & 0xFF ); + } +#ifndef NDEBUG + else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) + { + dprintf( "Unmapped write $%04X\n", (unsigned) addr ); + } +#endif +} + +void Gbs_Core::write_io_( int offset, int data ) +{ + write_io_inline( offset, data, io_base ); +} + +inline void Gbs_Core::write_io( int offset, int data ) +{ + (void) LOG_MEM( offset + io_base, "<", data ); + + ram [io_base - ram_addr + offset] = data; + if ( (unsigned) offset < 0x80 ) + write_io_( offset, data ); +} + +int Gbs_Core::read_io( int offset ) +{ + int const io_base = 0xFF00; + int result = ram [io_base - ram_addr + offset]; + + if ( (unsigned) (offset - (apu_.io_addr - io_base)) < apu_.io_size ) + { + result = apu_.read_register( time(), offset + io_base ); + (void) LOG_MEM( offset + io_base, ">", result ); + } + else + { + check( result == read_mem( offset + io_base ) ); + } + return result; +} + +#define READ_FAST( addr, out ) \ +{\ + out = READ_CODE( addr );\ + if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )\ + out = LOG_MEM( addr, ">", apu_.read_register( TIME() + end_time, addr ) );\ + else\ + check( out == read_mem( addr ) );\ +} + +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) write_mem( addr, data ) + +#define WRITE_IO( addr, data ) write_io( addr, data ) +#define READ_IO( addr, out ) out = read_io( addr ) + +#define CPU cpu + +#define CPU_BEGIN \ +void Gbs_Core::run_cpu()\ +{ + #include "Gb_Cpu_run.h" +} diff --git a/Frameworks/GME/gme/Gbs_Emu.cpp b/Frameworks/GME/gme/Gbs_Emu.cpp index 5ed815140..219cea77e 100644 --- a/Frameworks/GME/gme/Gbs_Emu.cpp +++ b/Frameworks/GME/gme/Gbs_Emu.cpp @@ -1,167 +1,167 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Gbs_Emu.h" - -/* Copyright (C) 2003-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000, 0,0,0,0,0,0,0,0 }; -Gbs_Emu::equalizer_t const Gbs_Emu::cgb_eq = { 0.0, 300, 0,0,0,0,0,0,0,0 }; -Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 30, 0,0,0,0,0,0,0,0 }; // DMG - -Gbs_Emu::Gbs_Emu() -{ - sound_hardware = sound_gbs; - enable_clicking( false ); - set_type( gme_gbs_type ); - set_silence_lookahead( 6 ); - set_max_initial_silence( 21 ); - set_gain( 1.2 ); - - // kind of midway between headphones and speaker - static equalizer_t const eq = { -1.0, 120, 0,0,0,0,0,0,0,0 }; - set_equalizer( eq ); -} - -Gbs_Emu::~Gbs_Emu() { } - -void Gbs_Emu::unload() -{ - core_.unload(); - Music_Emu::unload(); -} - -// Track info - -static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) -{ - GME_COPY_FIELD( h, out, game ); - GME_COPY_FIELD( h, out, author ); - GME_COPY_FIELD( h, out, copyright ); -} - -static void hash_gbs_file( Gbs_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) -{ - out.hash_( &h.vers, sizeof(h.vers) ); - out.hash_( &h.track_count, sizeof(h.track_count) ); - out.hash_( &h.first_track, sizeof(h.first_track) ); - out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); - out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); - out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); - out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) ); - out.hash_( &h.timer_modulo, sizeof(h.timer_modulo) ); - out.hash_( &h.timer_mode, sizeof(h.timer_mode) ); - out.hash_( data, data_size ); -} - -blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const -{ - copy_gbs_fields( header(), out ); - return blargg_ok; -} - -struct Gbs_File : Gme_Info_ -{ - Gbs_Emu::header_t const* h; - - Gbs_File() { set_type( gme_gbs_type ); } - - blargg_err_t load_mem_( byte const begin [], int size ) - { - h = ( Gbs_Emu::header_t * ) begin; - - set_track_count( h->track_count ); - if ( !h->valid_tag() ) - return blargg_err_file_type; - - return blargg_ok; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_gbs_fields( Gbs_Emu::header_t( *h ), out ); - return blargg_ok; - } - - blargg_err_t hash_( Hash_Function& out ) const - { - hash_gbs_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); - return blargg_ok; - } -}; - -static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } -static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } - -gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }}; - -// Setup - -blargg_err_t Gbs_Emu::load_( Data_Reader& in ) -{ - RETURN_ERR( core_.load( in ) ); - set_warning( core_.warning() ); - set_track_count( header().track_count ); - set_voice_count( Gb_Apu::osc_count ); - core_.apu().volume( gain() ); - - static const char* const names [Gb_Apu::osc_count] = { - "Square 1", "Square 2", "Wave", "Noise" - }; - set_voice_names( names ); - - static int const types [Gb_Apu::osc_count] = { - wave_type+1, wave_type+2, wave_type+3, mixed_type+1 - }; - set_voice_types( types ); - - return setup_buffer( 4194304 ); -} - -void Gbs_Emu::update_eq( blip_eq_t const& eq ) -{ - core_.apu().treble_eq( eq ); -} - -void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - core_.apu().set_output( i, c, l, r ); -} - -void Gbs_Emu::set_tempo_( double t ) -{ - core_.set_tempo( t ); -} - -blargg_err_t Gbs_Emu::start_track_( int track ) -{ - sound_t mode = sound_hardware; - if ( mode == sound_gbs ) - mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg; - - RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) ); - - // clear buffer AFTER track is started, eliminating initial click - return Classic_Emu::start_track_( track ); -} - -blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) -{ - return core_.end_frame( duration ); -} - -blargg_err_t Gbs_Emu::hash_( Hash_Function& out ) const -{ - hash_gbs_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); - return blargg_ok; +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gbs_Emu.h" + +/* Copyright (C) 2003-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000, 0,0,0,0,0,0,0,0 }; +Gbs_Emu::equalizer_t const Gbs_Emu::cgb_eq = { 0.0, 300, 0,0,0,0,0,0,0,0 }; +Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 30, 0,0,0,0,0,0,0,0 }; // DMG + +Gbs_Emu::Gbs_Emu() +{ + sound_hardware = sound_gbs; + enable_clicking( false ); + set_type( gme_gbs_type ); + set_silence_lookahead( 6 ); + set_max_initial_silence( 21 ); + set_gain( 1.2 ); + + // kind of midway between headphones and speaker + static equalizer_t const eq = { -1.0, 120, 0,0,0,0,0,0,0,0 }; + set_equalizer( eq ); +} + +Gbs_Emu::~Gbs_Emu() { } + +void Gbs_Emu::unload() +{ + core_.unload(); + Music_Emu::unload(); +} + +// Track info + +static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) +{ + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, author ); + GME_COPY_FIELD( h, out, copyright ); +} + +static void hash_gbs_file( Gbs_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.track_count, sizeof(h.track_count) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) ); + out.hash_( &h.timer_modulo, sizeof(h.timer_modulo) ); + out.hash_( &h.timer_mode, sizeof(h.timer_mode) ); + out.hash_( data, data_size ); +} + +blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const +{ + copy_gbs_fields( header(), out ); + return blargg_ok; +} + +struct Gbs_File : Gme_Info_ +{ + Gbs_Emu::header_t const* h; + + Gbs_File() { set_type( gme_gbs_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( Gbs_Emu::header_t * ) begin; + + set_track_count( h->track_count ); + if ( !h->valid_tag() ) + return blargg_err_file_type; + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_gbs_fields( Gbs_Emu::header_t( *h ), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_gbs_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } +static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } + +gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }}; + +// Setup + +blargg_err_t Gbs_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core_.load( in ) ); + set_warning( core_.warning() ); + set_track_count( header().track_count ); + set_voice_count( Gb_Apu::osc_count ); + core_.apu().volume( gain() ); + + static const char* const names [Gb_Apu::osc_count] = { + "Square 1", "Square 2", "Wave", "Noise" + }; + set_voice_names( names ); + + static int const types [Gb_Apu::osc_count] = { + wave_type+1, wave_type+2, wave_type+3, mixed_type+1 + }; + set_voice_types( types ); + + return setup_buffer( 4194304 ); +} + +void Gbs_Emu::update_eq( blip_eq_t const& eq ) +{ + core_.apu().treble_eq( eq ); +} + +void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + core_.apu().set_output( i, c, l, r ); +} + +void Gbs_Emu::set_tempo_( double t ) +{ + core_.set_tempo( t ); +} + +blargg_err_t Gbs_Emu::start_track_( int track ) +{ + sound_t mode = sound_hardware; + if ( mode == sound_gbs ) + mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg; + + RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) ); + + // clear buffer AFTER track is started, eliminating initial click + return Classic_Emu::start_track_( track ); +} + +blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) +{ + return core_.end_frame( duration ); +} + +blargg_err_t Gbs_Emu::hash_( Hash_Function& out ) const +{ + hash_gbs_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); + return blargg_ok; } \ No newline at end of file diff --git a/Frameworks/GME/gme/Gbs_Emu.h b/Frameworks/GME/gme/Gbs_Emu.h index b070582b7..008fb5201 100644 --- a/Frameworks/GME/gme/Gbs_Emu.h +++ b/Frameworks/GME/gme/Gbs_Emu.h @@ -1,63 +1,63 @@ -// Nintendo Game Boy GBS music file emulator - -// Game_Music_Emu $vers -#ifndef GBS_EMU_H -#define GBS_EMU_H - -#include "Classic_Emu.h" -#include "Gbs_Core.h" - -class Gbs_Emu : public Classic_Emu { -public: - // Equalizer profiles for Game Boy speaker and headphones - static equalizer_t const handheld_eq; - static equalizer_t const headphones_eq; - static equalizer_t const cgb_eq; // Game Boy Color headphones have less bass - - // GBS file header (see Gbs_Core.h) - typedef Gbs_Core::header_t header_t; - - // Header for currently loaded file - header_t const& header() const { return core_.header(); } - - // Selects which sound hardware to use. AGB hardware is cleaner than the - // others. Doesn't take effect until next start_track(). - enum sound_t { - sound_dmg = Gb_Apu::mode_dmg, // Game Boy monochrome - sound_cgb = Gb_Apu::mode_cgb, // Game Boy Color - sound_agb = Gb_Apu::mode_agb, // Game Boy Advance - sound_gbs // Use DMG/CGB based on GBS (default) - }; - void set_sound( sound_t s ) { sound_hardware = s; } - - // If true, makes APU more accurate, which results in more clicking. - void enable_clicking( bool enable = true ) { core_.apu().reduce_clicks( !enable ); } - - static gme_type_t static_type() { return gme_gbs_type; } - - Gbs_Core& core() { return core_; } - - blargg_err_t hash_( Hash_Function& ) const; - -// Internal -public: - Gbs_Emu(); - ~Gbs_Emu(); - -protected: - // Overrides - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t load_( Data_Reader& ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - virtual void unload(); - -private: - sound_t sound_hardware; - Gbs_Core core_; -}; - -#endif +// Nintendo Game Boy GBS music file emulator + +// Game_Music_Emu $vers +#ifndef GBS_EMU_H +#define GBS_EMU_H + +#include "Classic_Emu.h" +#include "Gbs_Core.h" + +class Gbs_Emu : public Classic_Emu { +public: + // Equalizer profiles for Game Boy speaker and headphones + static equalizer_t const handheld_eq; + static equalizer_t const headphones_eq; + static equalizer_t const cgb_eq; // Game Boy Color headphones have less bass + + // GBS file header (see Gbs_Core.h) + typedef Gbs_Core::header_t header_t; + + // Header for currently loaded file + header_t const& header() const { return core_.header(); } + + // Selects which sound hardware to use. AGB hardware is cleaner than the + // others. Doesn't take effect until next start_track(). + enum sound_t { + sound_dmg = Gb_Apu::mode_dmg, // Game Boy monochrome + sound_cgb = Gb_Apu::mode_cgb, // Game Boy Color + sound_agb = Gb_Apu::mode_agb, // Game Boy Advance + sound_gbs // Use DMG/CGB based on GBS (default) + }; + void set_sound( sound_t s ) { sound_hardware = s; } + + // If true, makes APU more accurate, which results in more clicking. + void enable_clicking( bool enable = true ) { core_.apu().reduce_clicks( !enable ); } + + static gme_type_t static_type() { return gme_gbs_type; } + + Gbs_Core& core() { return core_; } + + blargg_err_t hash_( Hash_Function& ) const; + +// Internal +public: + Gbs_Emu(); + ~Gbs_Emu(); + +protected: + // Overrides + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual void unload(); + +private: + sound_t sound_hardware; + Gbs_Core core_; +}; + +#endif diff --git a/Frameworks/GME/gme/Gme_Loader.cpp b/Frameworks/GME/gme/Gme_Loader.cpp index 8f340a138..70fe1f78c 100644 --- a/Frameworks/GME/gme/Gme_Loader.cpp +++ b/Frameworks/GME/gme/Gme_Loader.cpp @@ -1,86 +1,86 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Gme_Loader.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void Gme_Loader::unload() -{ - file_begin_ = NULL; - file_end_ = NULL; - file_data.clear(); -} - -Gme_Loader::Gme_Loader() -{ - warning_ = NULL; - Gme_Loader::unload(); - blargg_verify_byte_order(); // used by most emulator types, so save them the trouble -} - -Gme_Loader::~Gme_Loader() { } - -blargg_err_t Gme_Loader::load_mem_( byte const data [], int size ) -{ - require( data != file_data.begin() ); // load_mem_() or load_() must be overridden - Mem_File_Reader in( data, size ); - return load_( in ); -} - -inline blargg_err_t Gme_Loader::load_mem_wrapper( byte const data [], int size ) -{ - file_begin_ = data; - file_end_ = data + size; - return load_mem_( data, size ); -} - -blargg_err_t Gme_Loader::load_( Data_Reader& in ) -{ - RETURN_ERR( file_data.resize( in.remain() ) ); - RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); - return load_mem_wrapper( file_data.begin(), file_data.size() ); -} - -blargg_err_t Gme_Loader::post_load_( blargg_err_t err ) -{ - if ( err ) - { - unload(); - return err; - } - - return post_load(); -} - -blargg_err_t Gme_Loader::load_mem( void const* in, long size ) -{ - pre_load(); - return post_load_( load_mem_wrapper( (byte const*) in, (int) size ) ); -} - -blargg_err_t Gme_Loader::load( Data_Reader& in ) -{ - pre_load(); - return post_load_( load_( in ) ); -} - -blargg_err_t Gme_Loader::load_file( const char path [] ) -{ - pre_load(); - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - return post_load_( load_( in ) ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gme_Loader.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Gme_Loader::unload() +{ + file_begin_ = NULL; + file_end_ = NULL; + file_data.clear(); +} + +Gme_Loader::Gme_Loader() +{ + warning_ = NULL; + Gme_Loader::unload(); + blargg_verify_byte_order(); // used by most emulator types, so save them the trouble +} + +Gme_Loader::~Gme_Loader() { } + +blargg_err_t Gme_Loader::load_mem_( byte const data [], int size ) +{ + require( data != file_data.begin() ); // load_mem_() or load_() must be overridden + Mem_File_Reader in( data, size ); + return load_( in ); +} + +inline blargg_err_t Gme_Loader::load_mem_wrapper( byte const data [], int size ) +{ + file_begin_ = data; + file_end_ = data + size; + return load_mem_( data, size ); +} + +blargg_err_t Gme_Loader::load_( Data_Reader& in ) +{ + RETURN_ERR( file_data.resize( in.remain() ) ); + RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); + return load_mem_wrapper( file_data.begin(), file_data.size() ); +} + +blargg_err_t Gme_Loader::post_load_( blargg_err_t err ) +{ + if ( err ) + { + unload(); + return err; + } + + return post_load(); +} + +blargg_err_t Gme_Loader::load_mem( void const* in, long size ) +{ + pre_load(); + return post_load_( load_mem_wrapper( (byte const*) in, (int) size ) ); +} + +blargg_err_t Gme_Loader::load( Data_Reader& in ) +{ + pre_load(); + return post_load_( load_( in ) ); +} + +blargg_err_t Gme_Loader::load_file( const char path [] ) +{ + pre_load(); + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + return post_load_( load_( in ) ); +} diff --git a/Frameworks/GME/gme/Gme_Loader.h b/Frameworks/GME/gme/Gme_Loader.h index e59431c9a..81c2a0677 100644 --- a/Frameworks/GME/gme/Gme_Loader.h +++ b/Frameworks/GME/gme/Gme_Loader.h @@ -1,92 +1,92 @@ -// Common interface for loading file data from various sources - -// Game_Music_Emu $vers -#ifndef GME_LOADER_H -#define GME_LOADER_H - -#include "blargg_common.h" -#include "Data_Reader.h" - -class Gme_Loader { -public: - - // Each loads game music data from a file and returns an error if - // file is wrong type or is seriously corrupt. Minor problems are - // reported using warning(). - - // Loads from file on disk - blargg_err_t load_file( const char path [] ); - - // Loads from custom data source (see Data_Reader.h) - blargg_err_t load( Data_Reader& ); - - // Loads from file already read into memory. Object might keep pointer to - // data; if it does, you MUST NOT free it until you're done with the file. - blargg_err_t load_mem( void const* data, long size ); - - // Most recent warning string, or NULL if none. Clears current warning after - // returning. - const char* warning(); - - // Unloads file from memory - virtual void unload(); - - virtual ~Gme_Loader(); - -protected: - typedef BOOST::uint8_t byte; - - // File data in memory, or 0 if data was loaded with load_() - byte const* file_begin() const { return file_begin_; } - byte const* file_end() const { return file_end_; } - int file_size() const { return (int) (file_end_ - file_begin_); } - - // Sets warning string - void set_warning( const char s [] ) { warning_ = s; } - - // At least one must be overridden - virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() - virtual blargg_err_t load_mem_( byte const data [], int size ); // use data in memory - - // Optionally overridden - virtual void pre_load() { unload(); } // called before load_()/load_mem_() - virtual blargg_err_t post_load() { return blargg_ok; } // called after load_()/load_mem_() succeeds - -private: - // noncopyable - Gme_Loader( const Gme_Loader& ); - Gme_Loader& operator = ( const Gme_Loader& ); - -// Implementation -public: - Gme_Loader(); - BLARGG_DISABLE_NOTHROW - - blargg_vector file_data; // used only when loading from file to load_mem_() - byte const* file_begin_; - byte const* file_end_; - const char* warning_; - - blargg_err_t load_mem_wrapper( byte const [], int ); - blargg_err_t post_load_( blargg_err_t err ); -}; - -// Files are read with GME_FILE_READER. Default supports gzip if zlib is available. -#ifndef GME_FILE_READER - #ifdef HAVE_ZLIB_H - #define GME_FILE_READER Gzip_File_Reader - #else - #define GME_FILE_READER Std_File_Reader - #endif -#elif defined (GME_FILE_READER_INCLUDE) - #include GME_FILE_READER_INCLUDE -#endif - -inline const char* Gme_Loader::warning() -{ - const char* s = warning_; - warning_ = NULL; - return s; -} - -#endif +// Common interface for loading file data from various sources + +// Game_Music_Emu $vers +#ifndef GME_LOADER_H +#define GME_LOADER_H + +#include "blargg_common.h" +#include "Data_Reader.h" + +class Gme_Loader { +public: + + // Each loads game music data from a file and returns an error if + // file is wrong type or is seriously corrupt. Minor problems are + // reported using warning(). + + // Loads from file on disk + blargg_err_t load_file( const char path [] ); + + // Loads from custom data source (see Data_Reader.h) + blargg_err_t load( Data_Reader& ); + + // Loads from file already read into memory. Object might keep pointer to + // data; if it does, you MUST NOT free it until you're done with the file. + blargg_err_t load_mem( void const* data, long size ); + + // Most recent warning string, or NULL if none. Clears current warning after + // returning. + const char* warning(); + + // Unloads file from memory + virtual void unload(); + + virtual ~Gme_Loader(); + +protected: + typedef BOOST::uint8_t byte; + + // File data in memory, or 0 if data was loaded with load_() + byte const* file_begin() const { return file_begin_; } + byte const* file_end() const { return file_end_; } + int file_size() const { return (int) (file_end_ - file_begin_); } + + // Sets warning string + void set_warning( const char s [] ) { warning_ = s; } + + // At least one must be overridden + virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() + virtual blargg_err_t load_mem_( byte const data [], int size ); // use data in memory + + // Optionally overridden + virtual void pre_load() { unload(); } // called before load_()/load_mem_() + virtual blargg_err_t post_load() { return blargg_ok; } // called after load_()/load_mem_() succeeds + +private: + // noncopyable + Gme_Loader( const Gme_Loader& ); + Gme_Loader& operator = ( const Gme_Loader& ); + +// Implementation +public: + Gme_Loader(); + BLARGG_DISABLE_NOTHROW + + blargg_vector file_data; // used only when loading from file to load_mem_() + byte const* file_begin_; + byte const* file_end_; + const char* warning_; + + blargg_err_t load_mem_wrapper( byte const [], int ); + blargg_err_t post_load_( blargg_err_t err ); +}; + +// Files are read with GME_FILE_READER. Default supports gzip if zlib is available. +#ifndef GME_FILE_READER + #ifdef HAVE_ZLIB_H + #define GME_FILE_READER Gzip_File_Reader + #else + #define GME_FILE_READER Std_File_Reader + #endif +#elif defined (GME_FILE_READER_INCLUDE) + #include GME_FILE_READER_INCLUDE +#endif + +inline const char* Gme_Loader::warning() +{ + const char* s = warning_; + warning_ = NULL; + return s; +} + +#endif diff --git a/Frameworks/GME/gme/Hes_Apu.cpp b/Frameworks/GME/gme/Hes_Apu.cpp index edbbf1f80..a8d749185 100644 --- a/Frameworks/GME/gme/Hes_Apu.cpp +++ b/Frameworks/GME/gme/Hes_Apu.cpp @@ -1,361 +1,361 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Hes_Apu.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -bool const center_waves = true; // reduces asymmetry and clamping when starting notes - -Hes_Apu::Hes_Apu() -{ - for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) - { - osc--; - osc->output [0] = NULL; - osc->output [1] = NULL; - osc->outputs [0] = NULL; - osc->outputs [1] = NULL; - osc->outputs [2] = NULL; - } - - reset(); -} - -void Hes_Apu::reset() -{ - latch = 0; - balance = 0xFF; - - for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) - { - osc--; - memset( osc, 0, offsetof (Osc,output) ); - osc->lfsr = 0; - osc->control = 0x40; - osc->balance = 0xFF; - } - - // Only last two oscs support noise - oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR - oscs [osc_count - 1].lfsr = 0x200C3; -} - -void Hes_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) - require( !center || (center && !left && !right) || (center && left && right) ); - require( (unsigned) i < osc_count ); // fails if you pass invalid osc index - - if ( !center || !left || !right ) - { - left = center; - right = center; - } - - Osc& o = oscs [i]; - o.outputs [0] = center; - o.outputs [1] = left; - o.outputs [2] = right; - balance_changed( o ); -} - -void Hes_Apu::run_osc( Blip_Synth_Fast& syn, Osc& o, blip_time_t end_time ) -{ - int vol0 = o.volume [0]; - int vol1 = o.volume [1]; - int dac = o.dac; - - Blip_Buffer* out0 = o.output [0]; // cache often-used values - Blip_Buffer* out1 = o.output [1]; - if ( !(o.control & 0x80) ) - out0 = NULL; - - if ( out0 ) - { - // Update amplitudes - if ( out1 ) - { - int delta = dac * vol1 - o.last_amp [1]; - if ( delta ) - { - syn.offset( o.last_time, delta, out1 ); - out1->set_modified(); - } - } - int delta = dac * vol0 - o.last_amp [0]; - if ( delta ) - { - syn.offset( o.last_time, delta, out0 ); - out0->set_modified(); - } - - // Don't generate if silent - if ( !(vol0 | vol1) ) - out0 = NULL; - } - - // Generate noise - int noise = 0; - if ( o.lfsr ) - { - noise = o.noise & 0x80; - - blip_time_t time = o.last_time + o.noise_delay; - if ( time < end_time ) - { - int period = (~o.noise & 0x1F) * 128; - if ( !period ) - period = 64; - - if ( noise && out0 ) - { - unsigned lfsr = o.lfsr; - do - { - int new_dac = -(lfsr & 1); - lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); - - int delta = (new_dac &= 0x1F) - dac; - if ( delta ) - { - dac = new_dac; - syn.offset( time, delta * vol0, out0 ); - if ( out1 ) - syn.offset( time, delta * vol1, out1 ); - } - time += period; - } - while ( time < end_time ); - - if ( !lfsr ) - { - lfsr = 1; - check( false ); - } - o.lfsr = lfsr; - - out0->set_modified(); - if ( out1 ) - out1->set_modified(); - } - else - { - // Maintain phase when silent - int count = (end_time - time + period - 1) / period; - time += count * period; - - // not worth it - //while ( count-- ) - // o.lfsr = (o.lfsr >> 1) ^ (0x30061 * (o.lfsr & 1)); - } - } - o.noise_delay = time - end_time; - } - - // Generate wave - blip_time_t time = o.last_time + o.delay; - if ( time < end_time ) - { - int phase = (o.phase + 1) & 0x1F; // pre-advance for optimal inner loop - int period = o.period * 2; - - if ( period >= 14 && out0 && !((o.control & 0x40) | noise) ) - { - do - { - int new_dac = o.wave [phase]; - phase = (phase + 1) & 0x1F; - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - syn.offset( time, delta * vol0, out0 ); - if ( out1 ) - syn.offset( time, delta * vol1, out1 ); - } - time += period; - } - while ( time < end_time ); - out0->set_modified(); - if ( out1 ) - out1->set_modified(); - } - else - { - // Maintain phase when silent - int count = end_time - time; - if ( !period ) - period = 1; - count = (count + period - 1) / period; - - phase += count; // phase will be masked below - time += count * period; - } - - // TODO: Find whether phase increments even when both volumes are zero. - // CAN'T simply check for out0 being non-NULL, since it could be NULL - // if channel is muted in player, but still has non-zero volume. - // City Hunter breaks when this check is removed. - if ( !(o.control & 0x40) && (vol0 | vol1) ) - o.phase = (phase - 1) & 0x1F; // undo pre-advance - } - o.delay = time - end_time; - check( o.delay >= 0 ); - - o.last_time = end_time; - o.dac = dac; - o.last_amp [0] = dac * vol0; - o.last_amp [1] = dac * vol1; -} - -void Hes_Apu::balance_changed( Osc& osc ) -{ - static short const log_table [32] = { // ~1.5 db per step - #define ENTRY( factor ) short (factor * amp_range / 31.0 + 0.5) - ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ), - ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ), - ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ), - ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ), - ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ), - ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ), - ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ), - ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ), - #undef ENTRY - }; - - int vol = (osc.control & 0x1F) - 0x1E * 2; - - int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E); - if ( left < 0 ) left = 0; - - int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); - if ( right < 0 ) right = 0; - - // optimizing for the common case of being centered also allows easy - // panning using Effects_Buffer - - // Separate balance into center volume and additional on either left or right - osc.output [0] = osc.outputs [0]; // center - osc.output [1] = osc.outputs [2]; // right - int base = log_table [left ]; - int side = log_table [right] - base; - if ( side < 0 ) - { - base += side; - side = -side; - osc.output [1] = osc.outputs [1]; // left - } - - // Optimize when output is far left, center, or far right - if ( !base || osc.output [0] == osc.output [1] ) - { - base += side; - side = 0; - osc.output [0] = osc.output [1]; - osc.output [1] = NULL; - osc.last_amp [1] = 0; - } - - if ( center_waves ) - { - // TODO: this can leave a non-zero level in a buffer (minor) - osc.last_amp [0] += (base - osc.volume [0]) * 16; - osc.last_amp [1] += (side - osc.volume [1]) * 16; - } - - osc.volume [0] = base; - osc.volume [1] = side; -} - -void Hes_Apu::write_data( blip_time_t time, int addr, int data ) -{ - if ( addr == 0x800 ) - { - latch = data & 7; - } - else if ( addr == 0x801 ) - { - if ( balance != data ) - { - balance = data; - - for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) - { - osc--; - run_osc( synth, *osc, time ); - balance_changed( *oscs ); - } - } - } - else if ( latch < osc_count ) - { - Osc& osc = oscs [latch]; - run_osc( synth, osc, time ); - switch ( addr ) - { - case 0x802: - osc.period = (osc.period & 0xF00) | data; - break; - - case 0x803: - osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8); - break; - - case 0x804: - if ( osc.control & 0x40 & ~data ) - osc.phase = 0; - osc.control = data; - balance_changed( osc ); - break; - - case 0x805: - osc.balance = data; - balance_changed( osc ); - break; - - case 0x806: - data &= 0x1F; - if ( !(osc.control & 0x40) ) - { - osc.wave [osc.phase] = data; - osc.phase = (osc.phase + 1) & 0x1F; - } - else if ( osc.control & 0x80 ) - { - osc.dac = data; - } - break; - - case 0x807: - osc.noise = data; - break; - - case 0x809: - if ( !(data & 0x80) && (data & 0x03) != 0 ) - dprintf( "HES LFO not supported\n" ); - } - } -} - -void Hes_Apu::end_frame( blip_time_t end_time ) -{ - for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) - { - osc--; - if ( end_time > osc->last_time ) - run_osc( synth, *osc, end_time ); - osc->last_time -= end_time; - check( osc->last_time >= 0 ); - } -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Hes_Apu.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +bool const center_waves = true; // reduces asymmetry and clamping when starting notes + +Hes_Apu::Hes_Apu() +{ + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) + { + osc--; + osc->output [0] = NULL; + osc->output [1] = NULL; + osc->outputs [0] = NULL; + osc->outputs [1] = NULL; + osc->outputs [2] = NULL; + } + + reset(); +} + +void Hes_Apu::reset() +{ + latch = 0; + balance = 0xFF; + + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) + { + osc--; + memset( osc, 0, offsetof (Osc,output) ); + osc->lfsr = 0; + osc->control = 0x40; + osc->balance = 0xFF; + } + + // Only last two oscs support noise + oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR + oscs [osc_count - 1].lfsr = 0x200C3; +} + +void Hes_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Osc& o = oscs [i]; + o.outputs [0] = center; + o.outputs [1] = left; + o.outputs [2] = right; + balance_changed( o ); +} + +void Hes_Apu::run_osc( Blip_Synth_Fast& syn, Osc& o, blip_time_t end_time ) +{ + int vol0 = o.volume [0]; + int vol1 = o.volume [1]; + int dac = o.dac; + + Blip_Buffer* out0 = o.output [0]; // cache often-used values + Blip_Buffer* out1 = o.output [1]; + if ( !(o.control & 0x80) ) + out0 = NULL; + + if ( out0 ) + { + // Update amplitudes + if ( out1 ) + { + int delta = dac * vol1 - o.last_amp [1]; + if ( delta ) + { + syn.offset( o.last_time, delta, out1 ); + out1->set_modified(); + } + } + int delta = dac * vol0 - o.last_amp [0]; + if ( delta ) + { + syn.offset( o.last_time, delta, out0 ); + out0->set_modified(); + } + + // Don't generate if silent + if ( !(vol0 | vol1) ) + out0 = NULL; + } + + // Generate noise + int noise = 0; + if ( o.lfsr ) + { + noise = o.noise & 0x80; + + blip_time_t time = o.last_time + o.noise_delay; + if ( time < end_time ) + { + int period = (~o.noise & 0x1F) * 128; + if ( !period ) + period = 64; + + if ( noise && out0 ) + { + unsigned lfsr = o.lfsr; + do + { + int new_dac = -(lfsr & 1); + lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); + + int delta = (new_dac &= 0x1F) - dac; + if ( delta ) + { + dac = new_dac; + syn.offset( time, delta * vol0, out0 ); + if ( out1 ) + syn.offset( time, delta * vol1, out1 ); + } + time += period; + } + while ( time < end_time ); + + if ( !lfsr ) + { + lfsr = 1; + check( false ); + } + o.lfsr = lfsr; + + out0->set_modified(); + if ( out1 ) + out1->set_modified(); + } + else + { + // Maintain phase when silent + int count = (end_time - time + period - 1) / period; + time += count * period; + + // not worth it + //while ( count-- ) + // o.lfsr = (o.lfsr >> 1) ^ (0x30061 * (o.lfsr & 1)); + } + } + o.noise_delay = time - end_time; + } + + // Generate wave + blip_time_t time = o.last_time + o.delay; + if ( time < end_time ) + { + int phase = (o.phase + 1) & 0x1F; // pre-advance for optimal inner loop + int period = o.period * 2; + + if ( period >= 14 && out0 && !((o.control & 0x40) | noise) ) + { + do + { + int new_dac = o.wave [phase]; + phase = (phase + 1) & 0x1F; + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + syn.offset( time, delta * vol0, out0 ); + if ( out1 ) + syn.offset( time, delta * vol1, out1 ); + } + time += period; + } + while ( time < end_time ); + out0->set_modified(); + if ( out1 ) + out1->set_modified(); + } + else + { + // Maintain phase when silent + int count = end_time - time; + if ( !period ) + period = 1; + count = (count + period - 1) / period; + + phase += count; // phase will be masked below + time += count * period; + } + + // TODO: Find whether phase increments even when both volumes are zero. + // CAN'T simply check for out0 being non-NULL, since it could be NULL + // if channel is muted in player, but still has non-zero volume. + // City Hunter breaks when this check is removed. + if ( !(o.control & 0x40) && (vol0 | vol1) ) + o.phase = (phase - 1) & 0x1F; // undo pre-advance + } + o.delay = time - end_time; + check( o.delay >= 0 ); + + o.last_time = end_time; + o.dac = dac; + o.last_amp [0] = dac * vol0; + o.last_amp [1] = dac * vol1; +} + +void Hes_Apu::balance_changed( Osc& osc ) +{ + static short const log_table [32] = { // ~1.5 db per step + #define ENTRY( factor ) short (factor * amp_range / 31.0 + 0.5) + ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ), + ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ), + ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ), + ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ), + ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ), + ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ), + ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ), + ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ), + #undef ENTRY + }; + + int vol = (osc.control & 0x1F) - 0x1E * 2; + + int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E); + if ( left < 0 ) left = 0; + + int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); + if ( right < 0 ) right = 0; + + // optimizing for the common case of being centered also allows easy + // panning using Effects_Buffer + + // Separate balance into center volume and additional on either left or right + osc.output [0] = osc.outputs [0]; // center + osc.output [1] = osc.outputs [2]; // right + int base = log_table [left ]; + int side = log_table [right] - base; + if ( side < 0 ) + { + base += side; + side = -side; + osc.output [1] = osc.outputs [1]; // left + } + + // Optimize when output is far left, center, or far right + if ( !base || osc.output [0] == osc.output [1] ) + { + base += side; + side = 0; + osc.output [0] = osc.output [1]; + osc.output [1] = NULL; + osc.last_amp [1] = 0; + } + + if ( center_waves ) + { + // TODO: this can leave a non-zero level in a buffer (minor) + osc.last_amp [0] += (base - osc.volume [0]) * 16; + osc.last_amp [1] += (side - osc.volume [1]) * 16; + } + + osc.volume [0] = base; + osc.volume [1] = side; +} + +void Hes_Apu::write_data( blip_time_t time, int addr, int data ) +{ + if ( addr == 0x800 ) + { + latch = data & 7; + } + else if ( addr == 0x801 ) + { + if ( balance != data ) + { + balance = data; + + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) + { + osc--; + run_osc( synth, *osc, time ); + balance_changed( *oscs ); + } + } + } + else if ( latch < osc_count ) + { + Osc& osc = oscs [latch]; + run_osc( synth, osc, time ); + switch ( addr ) + { + case 0x802: + osc.period = (osc.period & 0xF00) | data; + break; + + case 0x803: + osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8); + break; + + case 0x804: + if ( osc.control & 0x40 & ~data ) + osc.phase = 0; + osc.control = data; + balance_changed( osc ); + break; + + case 0x805: + osc.balance = data; + balance_changed( osc ); + break; + + case 0x806: + data &= 0x1F; + if ( !(osc.control & 0x40) ) + { + osc.wave [osc.phase] = data; + osc.phase = (osc.phase + 1) & 0x1F; + } + else if ( osc.control & 0x80 ) + { + osc.dac = data; + } + break; + + case 0x807: + osc.noise = data; + break; + + case 0x809: + if ( !(data & 0x80) && (data & 0x03) != 0 ) + dprintf( "HES LFO not supported\n" ); + } + } +} + +void Hes_Apu::end_frame( blip_time_t end_time ) +{ + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) + { + osc--; + if ( end_time > osc->last_time ) + run_osc( synth, *osc, end_time ); + osc->last_time -= end_time; + check( osc->last_time >= 0 ); + } +} diff --git a/Frameworks/GME/gme/Hes_Apu.h b/Frameworks/GME/gme/Hes_Apu.h index e6f099cef..e0eb0cd2b 100644 --- a/Frameworks/GME/gme/Hes_Apu.h +++ b/Frameworks/GME/gme/Hes_Apu.h @@ -1,87 +1,87 @@ -// Turbo Grafx 16 (PC Engine) PSG sound chip emulator - -// Game_Music_Emu $vers -#ifndef HES_APU_H -#define HES_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Hes_Apu { -public: -// Basics - - // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, - // output is mono. - void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Emulates to time t, then writes data to addr - void write_data( blip_time_t t, int addr, int data ); - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Resets sound chip - void reset(); - - // Same as set_output(), but for a particular channel - enum { osc_count = 6 }; // 0 <= chan < osc_count - void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Sets treble equalization - void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - - // Sets overall volume, where 1.0 is normal - void volume( double v ) { synth.volume( 1.8 / osc_count / amp_range * v ); } - - // Registers are at io_addr to io_addr+io_size-1 - enum { io_addr = 0x0800 }; - enum { io_size = 10 }; - -// Implementation -public: - Hes_Apu(); - typedef BOOST::uint8_t byte; - -private: - enum { amp_range = 0x8000 }; - struct Osc - { - byte wave [32]; - int delay; - int period; - int phase; - - int noise_delay; - byte noise; - unsigned lfsr; - - byte control; - byte balance; - byte dac; - short volume [2]; - int last_amp [2]; - - blip_time_t last_time; - Blip_Buffer* output [2]; - Blip_Buffer* outputs [3]; - }; - Osc oscs [osc_count]; - int latch; - int balance; - Blip_Synth_Fast synth; - - void balance_changed( Osc& ); - static void run_osc( Blip_Synth_Fast&, Osc&, blip_time_t ); -}; - -inline void Hes_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - for ( int i = osc_count; --i >= 0; ) - set_output( i, c, l, r ); -} - -#endif +// Turbo Grafx 16 (PC Engine) PSG sound chip emulator + +// Game_Music_Emu $vers +#ifndef HES_APU_H +#define HES_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Hes_Apu { +public: +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip + void reset(); + + // Same as set_output(), but for a particular channel + enum { osc_count = 6 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth.volume( 1.8 / osc_count / amp_range * v ); } + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0x0800 }; + enum { io_size = 10 }; + +// Implementation +public: + Hes_Apu(); + typedef BOOST::uint8_t byte; + +private: + enum { amp_range = 0x8000 }; + struct Osc + { + byte wave [32]; + int delay; + int period; + int phase; + + int noise_delay; + byte noise; + unsigned lfsr; + + byte control; + byte balance; + byte dac; + short volume [2]; + int last_amp [2]; + + blip_time_t last_time; + Blip_Buffer* output [2]; + Blip_Buffer* outputs [3]; + }; + Osc oscs [osc_count]; + int latch; + int balance; + Blip_Synth_Fast synth; + + void balance_changed( Osc& ); + static void run_osc( Blip_Synth_Fast&, Osc&, blip_time_t ); +}; + +inline void Hes_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} + +#endif diff --git a/Frameworks/GME/gme/Hes_Apu_Adpcm.h b/Frameworks/GME/gme/Hes_Apu_Adpcm.h index 6abd80869..30b16ab3a 100644 --- a/Frameworks/GME/gme/Hes_Apu_Adpcm.h +++ b/Frameworks/GME/gme/Hes_Apu_Adpcm.h @@ -1,94 +1,94 @@ -// Turbo Grafx 16 (PC Engine) ADPCM sound chip emulator - -// Game_Music_Emu $vers -#ifndef HES_APU_ADPCM_H -#define HES_APU_ADPCM_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Hes_Apu_Adpcm { -public: -// Basics - - // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, - // output is mono. - void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Emulates to time t, then writes data to addr - void write_data( blip_time_t t, int addr, int data ); - - // Emulates to time t, then reads from addr - int read_data( blip_time_t t, int addr ); - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Resets sound chip - void reset(); - - // Same as set_output(), but for a particular channel - enum { osc_count = 1 }; // 0 <= chan < osc_count - void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Sets treble equalization - void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - - // Sets overall volume, where 1.0 is normal - void volume( double v ) { synth.volume( 0.6 / osc_count / amp_range * v ); } - - // Registers are at io_addr to io_addr+io_size-1 - enum { io_addr = 0x1800 }; - enum { io_size = 0x400 }; - -// Implementation -public: - Hes_Apu_Adpcm(); - typedef BOOST::uint8_t byte; - -private: - enum { amp_range = 2048 }; - - struct State - { - byte pcmbuf [0x10000]; - byte port [0x10]; - int ad_sample; - int ad_ref_index; - bool ad_low_nibble; - int freq; - unsigned short addr; - unsigned short writeptr; - unsigned short readptr; - unsigned short playptr; - byte playflag; - byte repeatflag; - int length; - int playlength; - int playedsamplecount; - int volume; - int fadetimer; - int fadecount; - }; - State state; - Blip_Synth_Fast synth; - - Blip_Buffer* output; - blip_time_t last_time; - double next_timer; - int last_amp; - - void run_until( blip_time_t ); - - int adpcm_decode( int ); -}; - -inline void Hes_Apu_Adpcm::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - set_output( 0, c, l, r ); -} - -#endif +// Turbo Grafx 16 (PC Engine) ADPCM sound chip emulator + +// Game_Music_Emu $vers +#ifndef HES_APU_ADPCM_H +#define HES_APU_ADPCM_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Hes_Apu_Adpcm { +public: +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); + + // Emulates to time t, then reads from addr + int read_data( blip_time_t t, int addr ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip + void reset(); + + // Same as set_output(), but for a particular channel + enum { osc_count = 1 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth.volume( 0.6 / osc_count / amp_range * v ); } + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0x1800 }; + enum { io_size = 0x400 }; + +// Implementation +public: + Hes_Apu_Adpcm(); + typedef BOOST::uint8_t byte; + +private: + enum { amp_range = 2048 }; + + struct State + { + byte pcmbuf [0x10000]; + byte port [0x10]; + int ad_sample; + int ad_ref_index; + bool ad_low_nibble; + int freq; + unsigned short addr; + unsigned short writeptr; + unsigned short readptr; + unsigned short playptr; + byte playflag; + byte repeatflag; + int length; + int playlength; + int playedsamplecount; + int volume; + int fadetimer; + int fadecount; + }; + State state; + Blip_Synth_Fast synth; + + Blip_Buffer* output; + blip_time_t last_time; + double next_timer; + int last_amp; + + void run_until( blip_time_t ); + + int adpcm_decode( int ); +}; + +inline void Hes_Apu_Adpcm::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + set_output( 0, c, l, r ); +} + +#endif diff --git a/Frameworks/GME/gme/Hes_Core.cpp b/Frameworks/GME/gme/Hes_Core.cpp index 273787ca9..061712870 100644 --- a/Frameworks/GME/gme/Hes_Core.cpp +++ b/Frameworks/GME/gme/Hes_Core.cpp @@ -1,408 +1,408 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Hes_Core.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const timer_mask = 0x04; -int const vdp_mask = 0x02; -int const i_flag_mask = 0x04; -int const unmapped = 0xFF; - -int const period_60hz = 262 * 455; // scanlines * clocks per scanline - -Hes_Core::Hes_Core() : rom( Hes_Cpu::page_size ) -{ - timer.raw_load = 0; -} - -Hes_Core::~Hes_Core() { } - -void Hes_Core::unload() -{ - rom.clear(); - Gme_Loader::unload(); -} - -bool Hes_Core::header_t::valid_tag() const -{ - return 0 == memcmp( tag, "HESM", 4 ); -} - -blargg_err_t Hes_Core::load_( Data_Reader& in ) -{ - assert( offsetof (header_t,unused [4]) == header_t::size ); - RETURN_ERR( rom.load( in, header_t::size, &header_, unmapped ) ); - - if ( !header_.valid_tag() ) - return blargg_err_file_type; - - if ( header_.vers != 0 ) - set_warning( "Unknown file version" ); - - if ( memcmp( header_.data_tag, "DATA", 4 ) ) - set_warning( "Data header missing" ); - - if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) - set_warning( "Unknown header data" ); - - // File spec supports multiple blocks, but I haven't found any, and - // many files have bad sizes in the only block, so it's simpler to - // just try to load the damn data as best as possible. - - int addr = get_le32( header_.addr ); - int size = get_le32( header_.data_size ); - int const rom_max = 0x100000; - if ( (unsigned) addr >= (unsigned) rom_max ) - { - set_warning( "Invalid address" ); - addr &= rom_max - 1; - } - if ( (unsigned) (addr + size) > (unsigned) rom_max ) - set_warning( "Invalid size" ); - - if ( size != rom.file_size() ) - { - if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) - set_warning( "Multiple DATA not supported" ); - else if ( size < rom.file_size() ) - set_warning( "Extra file data" ); - else - set_warning( "Missing file data" ); - } - - rom.set_addr( addr ); - - return blargg_ok; -} - -void Hes_Core::recalc_timer_load() -{ - timer.load = timer.raw_load * timer_base + 1; -} - -void Hes_Core::set_tempo( double t ) -{ - play_period = (time_t) (period_60hz / t); - timer_base = (int) (1024 / t); - recalc_timer_load(); -} - -blargg_err_t Hes_Core::start_track( int track ) -{ - memset( ram, 0, sizeof ram ); // some HES music relies on zero fill - memset( sgx, 0, sizeof sgx ); - - apu_.reset(); - adpcm_.reset(); - cpu.reset(); - - for ( int i = 0; i < (int) sizeof header_.banks; i++ ) - set_mmr( i, header_.banks [i] ); - set_mmr( cpu.page_count, 0xFF ); // unmapped beyond end of address space - - irq.disables = timer_mask | vdp_mask; - irq.timer = cpu.future_time; - irq.vdp = cpu.future_time; - - timer.enabled = false; - timer.raw_load = 0x80; - timer.count = timer.load; - timer.fired = false; - timer.last_time = 0; - - vdp.latch = 0; - vdp.control = 0; - vdp.next_vbl = 0; - - ram [0x1FF] = (idle_addr - 1) >> 8; - ram [0x1FE] = (idle_addr - 1) & 0xFF; - cpu.r.sp = 0xFD; - cpu.r.pc = get_le16( header_.init_addr ); - cpu.r.a = track; - - recalc_timer_load(); - - return blargg_ok; -} - -// Hardware - -void Hes_Core::run_until( time_t present ) -{ - while ( vdp.next_vbl < present ) - vdp.next_vbl += play_period; - - time_t elapsed = present - timer.last_time; - if ( elapsed > 0 ) - { - if ( timer.enabled ) - { - timer.count -= elapsed; - if ( timer.count <= 0 ) - timer.count += timer.load; - } - timer.last_time = present; - } -} - -void Hes_Core::write_vdp( int addr, int data ) -{ - switch ( addr ) - { - case 0: - vdp.latch = data & 0x1F; - break; - - case 2: - if ( vdp.latch == 5 ) - { - if ( data & 0x04 ) - set_warning( "Scanline interrupt unsupported" ); - run_until( cpu.time() ); - vdp.control = data; - irq_changed(); - } - else - { - dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data ); - } - break; - - case 3: - dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); - break; - } -} - -void Hes_Core::write_mem_( addr_t addr, int data ) -{ - time_t time = cpu.time(); - if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size ) - { - // Avoid going way past end when a long block xfer is writing to I/O space. - // Not a problem for other registers below because they don't write to - // Blip_Buffer. - time_t t = min( time, cpu.end_time() + 8 ); - apu_.write_data( t, addr, data ); - return; - } - if ( (unsigned) (addr - adpcm_.io_addr) < adpcm_.io_size ) - { - time_t t = min( time, cpu.end_time() + 6 ); - adpcm_.write_data( t, addr, data ); - return; - } - - switch ( addr ) - { - case 0x0000: - case 0x0002: - case 0x0003: - write_vdp( addr, data ); - return; - - case 0x0C00: { - run_until( time ); - timer.raw_load = (data & 0x7F) + 1; - recalc_timer_load(); - timer.count = timer.load; - break; - } - - case 0x0C01: - data &= 1; - if ( timer.enabled == data ) - return; - run_until( time ); - timer.enabled = data; - if ( data ) - timer.count = timer.load; - break; - - case 0x1402: - run_until( time ); - irq.disables = data; - if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values - dprintf( "Int mask: $%02X\n", data ); - break; - - case 0x1403: - run_until( time ); - if ( timer.enabled ) - timer.count = timer.load; - timer.fired = false; - break; - -#ifndef NDEBUG - case 0x1000: // I/O port - case 0x0402: // palette - case 0x0403: - case 0x0404: - case 0x0405: - return; - - default: - dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); - return; -#endif - } - - irq_changed(); -} - -int Hes_Core::read_mem_( addr_t addr ) -{ - time_t time = cpu.time(); - addr &= cpu.page_size - 1; - switch ( addr ) - { - case 0x0000: - if ( irq.vdp > time ) - return 0; - irq.vdp = cpu.future_time; - run_until( time ); - irq_changed(); - return 0x20; - - case 0x0002: - case 0x0003: - dprintf( "VDP read not supported: %d\n", addr ); - return 0; - - case 0x0C01: - //return timer.enabled; // TODO: remove? - case 0x0C00: - run_until( time ); - dprintf( "Timer count read\n" ); - return (unsigned) (timer.count - 1) / timer_base; - - case 0x1402: - return irq.disables; - - case 0x1403: - { - int status = 0; - if ( irq.timer <= time ) status |= timer_mask; - if ( irq.vdp <= time ) status |= vdp_mask; - return status; - } - - case 0x180A: - case 0x180B: - case 0x180C: - case 0x180D: - return adpcm_.read_data( time, addr ); - - #ifndef NDEBUG - case 0x1000: // I/O port - //case 0x180C: // CD-ROM - //case 0x180D: - break; - - default: - dprintf( "unmapped read $%04X\n", addr ); - #endif - } - - return unmapped; -} - -void Hes_Core::irq_changed() -{ - time_t present = cpu.time(); - - if ( irq.timer > present ) - { - irq.timer = cpu.future_time; - if ( timer.enabled && !timer.fired ) - irq.timer = present + timer.count; - } - - if ( irq.vdp > present ) - { - irq.vdp = cpu.future_time; - if ( vdp.control & 0x08 ) - irq.vdp = vdp.next_vbl; - } - - time_t time = cpu.future_time; - if ( !(irq.disables & timer_mask) ) time = irq.timer; - if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); - - cpu.set_irq_time( time ); -} - -int Hes_Core::cpu_done() -{ - check( cpu.time() >= cpu.end_time() || - (!(cpu.r.flags & i_flag_mask) && cpu.time() >= cpu.irq_time()) ); - - if ( !(cpu.r.flags & i_flag_mask) ) - { - time_t present = cpu.time(); - - if ( irq.timer <= present && !(irq.disables & timer_mask) ) - { - timer.fired = true; - irq.timer = cpu.future_time; - irq_changed(); // overkill, but not worth writing custom code - return 0x0A; - } - - if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) - { - // work around for bugs with music not acknowledging VDP - //run_until( present ); - //irq.vdp = cpu.future_time; - //irq_changed(); - return 0x08; - } - } - return -1; -} - -static void adjust_time( Hes_Core::time_t& time, Hes_Core::time_t delta ) -{ - if ( time < Hes_Cpu::future_time ) - { - time -= delta; - if ( time < 0 ) - time = 0; - } -} - -blargg_err_t Hes_Core::end_frame( time_t duration ) -{ - if ( run_cpu( duration ) ) - set_warning( "Emulation error (illegal instruction)" ); - - check( cpu.time() >= duration ); - //check( time() - duration < 20 ); // Txx instruction could cause going way over - - run_until( duration ); - - // end time frame - timer.last_time -= duration; - vdp.next_vbl -= duration; - cpu.end_frame( duration ); - ::adjust_time( irq.timer, duration ); - ::adjust_time( irq.vdp, duration ); - apu_.end_frame( duration ); - adpcm_.end_frame( duration ); - - return blargg_ok; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Hes_Core.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const timer_mask = 0x04; +int const vdp_mask = 0x02; +int const i_flag_mask = 0x04; +int const unmapped = 0xFF; + +int const period_60hz = 262 * 455; // scanlines * clocks per scanline + +Hes_Core::Hes_Core() : rom( Hes_Cpu::page_size ) +{ + timer.raw_load = 0; +} + +Hes_Core::~Hes_Core() { } + +void Hes_Core::unload() +{ + rom.clear(); + Gme_Loader::unload(); +} + +bool Hes_Core::header_t::valid_tag() const +{ + return 0 == memcmp( tag, "HESM", 4 ); +} + +blargg_err_t Hes_Core::load_( Data_Reader& in ) +{ + assert( offsetof (header_t,unused [4]) == header_t::size ); + RETURN_ERR( rom.load( in, header_t::size, &header_, unmapped ) ); + + if ( !header_.valid_tag() ) + return blargg_err_file_type; + + if ( header_.vers != 0 ) + set_warning( "Unknown file version" ); + + if ( memcmp( header_.data_tag, "DATA", 4 ) ) + set_warning( "Data header missing" ); + + if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) + set_warning( "Unknown header data" ); + + // File spec supports multiple blocks, but I haven't found any, and + // many files have bad sizes in the only block, so it's simpler to + // just try to load the damn data as best as possible. + + int addr = get_le32( header_.addr ); + int size = get_le32( header_.data_size ); + int const rom_max = 0x100000; + if ( (unsigned) addr >= (unsigned) rom_max ) + { + set_warning( "Invalid address" ); + addr &= rom_max - 1; + } + if ( (unsigned) (addr + size) > (unsigned) rom_max ) + set_warning( "Invalid size" ); + + if ( size != rom.file_size() ) + { + if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) + set_warning( "Multiple DATA not supported" ); + else if ( size < rom.file_size() ) + set_warning( "Extra file data" ); + else + set_warning( "Missing file data" ); + } + + rom.set_addr( addr ); + + return blargg_ok; +} + +void Hes_Core::recalc_timer_load() +{ + timer.load = timer.raw_load * timer_base + 1; +} + +void Hes_Core::set_tempo( double t ) +{ + play_period = (time_t) (period_60hz / t); + timer_base = (int) (1024 / t); + recalc_timer_load(); +} + +blargg_err_t Hes_Core::start_track( int track ) +{ + memset( ram, 0, sizeof ram ); // some HES music relies on zero fill + memset( sgx, 0, sizeof sgx ); + + apu_.reset(); + adpcm_.reset(); + cpu.reset(); + + for ( int i = 0; i < (int) sizeof header_.banks; i++ ) + set_mmr( i, header_.banks [i] ); + set_mmr( cpu.page_count, 0xFF ); // unmapped beyond end of address space + + irq.disables = timer_mask | vdp_mask; + irq.timer = cpu.future_time; + irq.vdp = cpu.future_time; + + timer.enabled = false; + timer.raw_load = 0x80; + timer.count = timer.load; + timer.fired = false; + timer.last_time = 0; + + vdp.latch = 0; + vdp.control = 0; + vdp.next_vbl = 0; + + ram [0x1FF] = (idle_addr - 1) >> 8; + ram [0x1FE] = (idle_addr - 1) & 0xFF; + cpu.r.sp = 0xFD; + cpu.r.pc = get_le16( header_.init_addr ); + cpu.r.a = track; + + recalc_timer_load(); + + return blargg_ok; +} + +// Hardware + +void Hes_Core::run_until( time_t present ) +{ + while ( vdp.next_vbl < present ) + vdp.next_vbl += play_period; + + time_t elapsed = present - timer.last_time; + if ( elapsed > 0 ) + { + if ( timer.enabled ) + { + timer.count -= elapsed; + if ( timer.count <= 0 ) + timer.count += timer.load; + } + timer.last_time = present; + } +} + +void Hes_Core::write_vdp( int addr, int data ) +{ + switch ( addr ) + { + case 0: + vdp.latch = data & 0x1F; + break; + + case 2: + if ( vdp.latch == 5 ) + { + if ( data & 0x04 ) + set_warning( "Scanline interrupt unsupported" ); + run_until( cpu.time() ); + vdp.control = data; + irq_changed(); + } + else + { + dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data ); + } + break; + + case 3: + dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); + break; + } +} + +void Hes_Core::write_mem_( addr_t addr, int data ) +{ + time_t time = cpu.time(); + if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size ) + { + // Avoid going way past end when a long block xfer is writing to I/O space. + // Not a problem for other registers below because they don't write to + // Blip_Buffer. + time_t t = min( time, cpu.end_time() + 8 ); + apu_.write_data( t, addr, data ); + return; + } + if ( (unsigned) (addr - adpcm_.io_addr) < adpcm_.io_size ) + { + time_t t = min( time, cpu.end_time() + 6 ); + adpcm_.write_data( t, addr, data ); + return; + } + + switch ( addr ) + { + case 0x0000: + case 0x0002: + case 0x0003: + write_vdp( addr, data ); + return; + + case 0x0C00: { + run_until( time ); + timer.raw_load = (data & 0x7F) + 1; + recalc_timer_load(); + timer.count = timer.load; + break; + } + + case 0x0C01: + data &= 1; + if ( timer.enabled == data ) + return; + run_until( time ); + timer.enabled = data; + if ( data ) + timer.count = timer.load; + break; + + case 0x1402: + run_until( time ); + irq.disables = data; + if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values + dprintf( "Int mask: $%02X\n", data ); + break; + + case 0x1403: + run_until( time ); + if ( timer.enabled ) + timer.count = timer.load; + timer.fired = false; + break; + +#ifndef NDEBUG + case 0x1000: // I/O port + case 0x0402: // palette + case 0x0403: + case 0x0404: + case 0x0405: + return; + + default: + dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); + return; +#endif + } + + irq_changed(); +} + +int Hes_Core::read_mem_( addr_t addr ) +{ + time_t time = cpu.time(); + addr &= cpu.page_size - 1; + switch ( addr ) + { + case 0x0000: + if ( irq.vdp > time ) + return 0; + irq.vdp = cpu.future_time; + run_until( time ); + irq_changed(); + return 0x20; + + case 0x0002: + case 0x0003: + dprintf( "VDP read not supported: %d\n", addr ); + return 0; + + case 0x0C01: + //return timer.enabled; // TODO: remove? + case 0x0C00: + run_until( time ); + dprintf( "Timer count read\n" ); + return (unsigned) (timer.count - 1) / timer_base; + + case 0x1402: + return irq.disables; + + case 0x1403: + { + int status = 0; + if ( irq.timer <= time ) status |= timer_mask; + if ( irq.vdp <= time ) status |= vdp_mask; + return status; + } + + case 0x180A: + case 0x180B: + case 0x180C: + case 0x180D: + return adpcm_.read_data( time, addr ); + + #ifndef NDEBUG + case 0x1000: // I/O port + //case 0x180C: // CD-ROM + //case 0x180D: + break; + + default: + dprintf( "unmapped read $%04X\n", addr ); + #endif + } + + return unmapped; +} + +void Hes_Core::irq_changed() +{ + time_t present = cpu.time(); + + if ( irq.timer > present ) + { + irq.timer = cpu.future_time; + if ( timer.enabled && !timer.fired ) + irq.timer = present + timer.count; + } + + if ( irq.vdp > present ) + { + irq.vdp = cpu.future_time; + if ( vdp.control & 0x08 ) + irq.vdp = vdp.next_vbl; + } + + time_t time = cpu.future_time; + if ( !(irq.disables & timer_mask) ) time = irq.timer; + if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); + + cpu.set_irq_time( time ); +} + +int Hes_Core::cpu_done() +{ + check( cpu.time() >= cpu.end_time() || + (!(cpu.r.flags & i_flag_mask) && cpu.time() >= cpu.irq_time()) ); + + if ( !(cpu.r.flags & i_flag_mask) ) + { + time_t present = cpu.time(); + + if ( irq.timer <= present && !(irq.disables & timer_mask) ) + { + timer.fired = true; + irq.timer = cpu.future_time; + irq_changed(); // overkill, but not worth writing custom code + return 0x0A; + } + + if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) + { + // work around for bugs with music not acknowledging VDP + //run_until( present ); + //irq.vdp = cpu.future_time; + //irq_changed(); + return 0x08; + } + } + return -1; +} + +static void adjust_time( Hes_Core::time_t& time, Hes_Core::time_t delta ) +{ + if ( time < Hes_Cpu::future_time ) + { + time -= delta; + if ( time < 0 ) + time = 0; + } +} + +blargg_err_t Hes_Core::end_frame( time_t duration ) +{ + if ( run_cpu( duration ) ) + set_warning( "Emulation error (illegal instruction)" ); + + check( cpu.time() >= duration ); + //check( time() - duration < 20 ); // Txx instruction could cause going way over + + run_until( duration ); + + // end time frame + timer.last_time -= duration; + vdp.next_vbl -= duration; + cpu.end_frame( duration ); + ::adjust_time( irq.timer, duration ); + ::adjust_time( irq.vdp, duration ); + apu_.end_frame( duration ); + adpcm_.end_frame( duration ); + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Hes_Core.h b/Frameworks/GME/gme/Hes_Core.h index 80b0a5fd5..5c9afd9c7 100644 --- a/Frameworks/GME/gme/Hes_Core.h +++ b/Frameworks/GME/gme/Hes_Core.h @@ -1,120 +1,120 @@ -// TurboGrafx-16/PC Engine HES music file emulator core - -// Game_Music_Emu $vers -#ifndef HES_CORE_H -#define HES_CORE_H - -#include "Gme_Loader.h" -#include "Rom_Data.h" -#include "Hes_Apu.h" -#include "Hes_Apu_Adpcm.h" -#include "Hes_Cpu.h" - -class Hes_Core : public Gme_Loader { -public: - - // HES file header - enum { info_offset = 0x20 }; - struct header_t - { - enum { size = 0x20 }; - - byte tag [4]; - byte vers; - byte first_track; - byte init_addr [2]; - byte banks [8]; - byte data_tag [4]; - byte data_size [4]; - byte addr [4]; - byte unused [4]; - - // True if header has valid file signature - bool valid_tag() const; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - // Pointer to ROM data, for getting track information from - byte const* data() const { return rom.begin(); } - int data_size() const { return rom.file_size(); } - - // Adjusts rate play routine is called at, where 1.0 is normal. - // Can be changed while track is playing. - void set_tempo( double ); - - // Sound chip - Hes_Apu& apu() { return apu_; } - - Hes_Apu_Adpcm& adpcm() { return adpcm_; } - - // Starts track - blargg_err_t start_track( int ); - - // Ends time frame at time t - typedef int time_t; - blargg_err_t end_frame( time_t ); - -// Implementation -public: - Hes_Core(); - ~Hes_Core(); - virtual void unload(); - -protected: - virtual blargg_err_t load_( Data_Reader& ); - -private: - enum { idle_addr = 0x1FFF }; - - typedef int addr_t; - Hes_Cpu cpu; - Rom_Data rom; - header_t header_; - time_t play_period; - int timer_base; - - struct { - time_t last_time; - int count; - int load; - int raw_load; - byte enabled; - byte fired; - } timer; - - struct { - time_t next_vbl; - byte latch; - byte control; - } vdp; - - struct { - time_t timer; - time_t vdp; - byte disables; - } irq; - - void recalc_timer_load(); - - // large items - byte* write_pages [Hes_Cpu::page_count + 1]; // 0 if unmapped or I/O space - Hes_Apu apu_; - Hes_Apu_Adpcm adpcm_; - byte ram [Hes_Cpu::page_size]; - byte sgx [3 * Hes_Cpu::page_size + Hes_Cpu::cpu_padding]; - - void irq_changed(); - void run_until( time_t ); - bool run_cpu( time_t end ); - int read_mem_( addr_t ); - int read_mem( addr_t ); - void write_mem_( addr_t, int data ); - void write_mem( addr_t, int ); - void write_vdp( int addr, int data ); - void set_mmr( int reg, int bank ); - int cpu_done(); -}; - -#endif +// TurboGrafx-16/PC Engine HES music file emulator core + +// Game_Music_Emu $vers +#ifndef HES_CORE_H +#define HES_CORE_H + +#include "Gme_Loader.h" +#include "Rom_Data.h" +#include "Hes_Apu.h" +#include "Hes_Apu_Adpcm.h" +#include "Hes_Cpu.h" + +class Hes_Core : public Gme_Loader { +public: + + // HES file header + enum { info_offset = 0x20 }; + struct header_t + { + enum { size = 0x20 }; + + byte tag [4]; + byte vers; + byte first_track; + byte init_addr [2]; + byte banks [8]; + byte data_tag [4]; + byte data_size [4]; + byte addr [4]; + byte unused [4]; + + // True if header has valid file signature + bool valid_tag() const; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + // Pointer to ROM data, for getting track information from + byte const* data() const { return rom.begin(); } + int data_size() const { return rom.file_size(); } + + // Adjusts rate play routine is called at, where 1.0 is normal. + // Can be changed while track is playing. + void set_tempo( double ); + + // Sound chip + Hes_Apu& apu() { return apu_; } + + Hes_Apu_Adpcm& adpcm() { return adpcm_; } + + // Starts track + blargg_err_t start_track( int ); + + // Ends time frame at time t + typedef int time_t; + blargg_err_t end_frame( time_t ); + +// Implementation +public: + Hes_Core(); + ~Hes_Core(); + virtual void unload(); + +protected: + virtual blargg_err_t load_( Data_Reader& ); + +private: + enum { idle_addr = 0x1FFF }; + + typedef int addr_t; + Hes_Cpu cpu; + Rom_Data rom; + header_t header_; + time_t play_period; + int timer_base; + + struct { + time_t last_time; + int count; + int load; + int raw_load; + byte enabled; + byte fired; + } timer; + + struct { + time_t next_vbl; + byte latch; + byte control; + } vdp; + + struct { + time_t timer; + time_t vdp; + byte disables; + } irq; + + void recalc_timer_load(); + + // large items + byte* write_pages [Hes_Cpu::page_count + 1]; // 0 if unmapped or I/O space + Hes_Apu apu_; + Hes_Apu_Adpcm adpcm_; + byte ram [Hes_Cpu::page_size]; + byte sgx [3 * Hes_Cpu::page_size + Hes_Cpu::cpu_padding]; + + void irq_changed(); + void run_until( time_t ); + bool run_cpu( time_t end ); + int read_mem_( addr_t ); + int read_mem( addr_t ); + void write_mem_( addr_t, int data ); + void write_mem( addr_t, int ); + void write_vdp( int addr, int data ); + void set_mmr( int reg, int bank ); + int cpu_done(); +}; + +#endif diff --git a/Frameworks/GME/gme/Hes_Cpu.cpp b/Frameworks/GME/gme/Hes_Cpu.cpp index 5f70ad45f..d246647f4 100644 --- a/Frameworks/GME/gme/Hes_Cpu.cpp +++ b/Frameworks/GME/gme/Hes_Cpu.cpp @@ -1,123 +1,123 @@ -// $package. http://www.slack.net/~ant/ - -#include "Hes_Cpu.h" - -#include "blargg_endian.h" -#include "Hes_Core.h" - -//#include "hes_cpu_log.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#define PAGE HES_CPU_PAGE - -int Hes_Core::read_mem( addr_t addr ) -{ - check( addr < 0x10000 ); - int result = *cpu.get_code( addr ); - if ( cpu.mmr [PAGE( addr )] == 0xFF ) - result = read_mem_( addr ); - return result; -} - -void Hes_Core::write_mem( addr_t addr, int data ) -{ - check( addr < 0x10000 ); - byte* out = write_pages [PAGE( addr )]; - if ( out ) - out [addr & (cpu.page_size - 1)] = data; - else if ( cpu.mmr [PAGE( addr )] == 0xFF ) - write_mem_( addr, data ); -} - -void Hes_Core::set_mmr( int page, int bank ) -{ - write_pages [page] = 0; - byte* data = rom.at_addr( bank * cpu.page_size ); - if ( bank >= 0x80 ) - { - data = 0; - switch ( bank ) - { - case 0xF8: - data = ram; - break; - - case 0xF9: - case 0xFA: - case 0xFB: - data = &sgx [(bank - 0xF9) * cpu.page_size]; - break; - - default: - if ( bank != 0xFF ) - dprintf( "Unmapped bank $%02X\n", bank ); - data = rom.unmapped(); - goto end; - } - - write_pages [page] = data; - } -end: - cpu.set_mmr( page, bank, data ); -} - -#define READ_FAST( addr, out ) \ -{\ - out = READ_CODE( addr );\ - if ( CPU.mmr [PAGE( addr )] == 0xFF )\ - {\ - FLUSH_TIME();\ - out = read_mem_( addr );\ - CACHE_TIME();\ - }\ -} - -#define WRITE_FAST( addr, data ) \ -{\ - int page = PAGE( addr );\ - byte* out = write_pages [page];\ - addr &= CPU.page_size - 1;\ - if ( out )\ - {\ - out [addr] = data;\ - }\ - else if ( CPU.mmr [page] == 0xFF )\ - {\ - FLUSH_TIME();\ - write_mem_( addr, data );\ - CACHE_TIME();\ - }\ -} - -#define READ_LOW( addr ) (ram [addr]) -#define WRITE_LOW( addr, data ) (ram [addr] = data) -#define READ_MEM( addr ) read_mem( addr ) -#define WRITE_MEM( addr, data ) write_mem( addr, data ) -#define WRITE_VDP( addr, data ) write_vdp( addr, data ) -#define CPU_DONE( result_out ) { FLUSH_TIME(); result_out = cpu_done(); CACHE_TIME(); } -#define SET_MMR( reg, bank ) set_mmr( reg, bank ) - -#define CPU cpu -#define IDLE_ADDR idle_addr - -#define CPU_BEGIN \ -bool Hes_Core::run_cpu( time_t end_time )\ -{\ - cpu.set_end_time( end_time ); - - #include "Hes_Cpu_run.h" - - return illegal_encountered; -} +// $package. http://www.slack.net/~ant/ + +#include "Hes_Cpu.h" + +#include "blargg_endian.h" +#include "Hes_Core.h" + +//#include "hes_cpu_log.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define PAGE HES_CPU_PAGE + +int Hes_Core::read_mem( addr_t addr ) +{ + check( addr < 0x10000 ); + int result = *cpu.get_code( addr ); + if ( cpu.mmr [PAGE( addr )] == 0xFF ) + result = read_mem_( addr ); + return result; +} + +void Hes_Core::write_mem( addr_t addr, int data ) +{ + check( addr < 0x10000 ); + byte* out = write_pages [PAGE( addr )]; + if ( out ) + out [addr & (cpu.page_size - 1)] = data; + else if ( cpu.mmr [PAGE( addr )] == 0xFF ) + write_mem_( addr, data ); +} + +void Hes_Core::set_mmr( int page, int bank ) +{ + write_pages [page] = 0; + byte* data = rom.at_addr( bank * cpu.page_size ); + if ( bank >= 0x80 ) + { + data = 0; + switch ( bank ) + { + case 0xF8: + data = ram; + break; + + case 0xF9: + case 0xFA: + case 0xFB: + data = &sgx [(bank - 0xF9) * cpu.page_size]; + break; + + default: + if ( bank != 0xFF ) + dprintf( "Unmapped bank $%02X\n", bank ); + data = rom.unmapped(); + goto end; + } + + write_pages [page] = data; + } +end: + cpu.set_mmr( page, bank, data ); +} + +#define READ_FAST( addr, out ) \ +{\ + out = READ_CODE( addr );\ + if ( CPU.mmr [PAGE( addr )] == 0xFF )\ + {\ + FLUSH_TIME();\ + out = read_mem_( addr );\ + CACHE_TIME();\ + }\ +} + +#define WRITE_FAST( addr, data ) \ +{\ + int page = PAGE( addr );\ + byte* out = write_pages [page];\ + addr &= CPU.page_size - 1;\ + if ( out )\ + {\ + out [addr] = data;\ + }\ + else if ( CPU.mmr [page] == 0xFF )\ + {\ + FLUSH_TIME();\ + write_mem_( addr, data );\ + CACHE_TIME();\ + }\ +} + +#define READ_LOW( addr ) (ram [addr]) +#define WRITE_LOW( addr, data ) (ram [addr] = data) +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) write_mem( addr, data ) +#define WRITE_VDP( addr, data ) write_vdp( addr, data ) +#define CPU_DONE( result_out ) { FLUSH_TIME(); result_out = cpu_done(); CACHE_TIME(); } +#define SET_MMR( reg, bank ) set_mmr( reg, bank ) + +#define CPU cpu +#define IDLE_ADDR idle_addr + +#define CPU_BEGIN \ +bool Hes_Core::run_cpu( time_t end_time )\ +{\ + cpu.set_end_time( end_time ); + + #include "Hes_Cpu_run.h" + + return illegal_encountered; +} diff --git a/Frameworks/GME/gme/Hes_Cpu.h b/Frameworks/GME/gme/Hes_Cpu.h index b182390cc..3e831afd1 100644 --- a/Frameworks/GME/gme/Hes_Cpu.h +++ b/Frameworks/GME/gme/Hes_Cpu.h @@ -1,139 +1,139 @@ -// PC Engine CPU emulator for use with HES music files - -// $package -#ifndef HES_CPU_H -#define HES_CPU_H - -#include "blargg_common.h" - -class Hes_Cpu { -public: - typedef BOOST::uint8_t byte; - typedef int time_t; - typedef int addr_t; - enum { future_time = INT_MAX/2 + 1 }; - - void reset(); - - enum { page_bits = 13 }; - enum { page_size = 1 << page_bits }; - enum { page_count = 0x10000 / page_size }; - void set_mmr( int reg, int bank, void const* code ); - - byte const* get_code( addr_t ); - - // NOT kept updated during emulation. - struct registers_t { - BOOST::uint16_t pc; - byte a; - byte x; - byte y; - byte flags; - byte sp; - }; - registers_t r; - - // page mapping registers - byte mmr [page_count + 1]; - - // Time of beginning of next instruction to be executed - time_t time() const { return cpu_state->time + cpu_state->base; } - void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } - void adjust_time( int delta ) { cpu_state->time += delta; } - - // Clocks past end (negative if before) - int time_past_end() const { return cpu_state->time; } - - // Time of next IRQ - time_t irq_time() const { return irq_time_; } - void set_irq_time( time_t ); - - // Emulation stops once time >= end_time - time_t end_time() const { return end_time_; } - void set_end_time( time_t ); - - // Subtracts t from all times - void end_frame( time_t t ); - - // Can read this many bytes past end of a page - enum { cpu_padding = 8 }; - -private: - // noncopyable - Hes_Cpu( const Hes_Cpu& ); - Hes_Cpu& operator = ( const Hes_Cpu& ); - - -// Implementation -public: - Hes_Cpu() { cpu_state = &cpu_state_; } - enum { irq_inhibit_mask = 0x04 }; - - struct cpu_state_t { - byte const* code_map [page_count + 1]; - time_t base; - int time; - }; - cpu_state_t* cpu_state; // points to cpu_state_ or a local copy - cpu_state_t cpu_state_; - time_t irq_time_; - time_t end_time_; - -private: - void set_code_page( int, void const* ); - inline void update_end_time( time_t end, time_t irq ); -}; - -#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> Hes_Cpu::page_bits) - -#if BLARGG_NONPORTABLE - #define HES_CPU_OFFSET( addr ) (addr) -#else - #define HES_CPU_OFFSET( addr ) ((addr) & (Hes_Cpu::page_size - 1)) -#endif - -inline BOOST::uint8_t const* Hes_Cpu::get_code( addr_t addr ) -{ - return cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr ); -} - -inline void Hes_Cpu::update_end_time( time_t end, time_t irq ) -{ - if ( end > irq && !(r.flags & irq_inhibit_mask) ) - end = irq; - - cpu_state->time += cpu_state->base - end; - cpu_state->base = end; -} - -inline void Hes_Cpu::set_irq_time( time_t t ) -{ - irq_time_ = t; - update_end_time( end_time_, t ); -} - -inline void Hes_Cpu::set_end_time( time_t t ) -{ - end_time_ = t; - update_end_time( t, irq_time_ ); -} - -inline void Hes_Cpu::end_frame( time_t t ) -{ - assert( cpu_state == &cpu_state_ ); - cpu_state_.base -= t; - if ( irq_time_ < future_time ) irq_time_ -= t; - if ( end_time_ < future_time ) end_time_ -= t; -} - -inline void Hes_Cpu::set_mmr( int reg, int bank, void const* code ) -{ - assert( (unsigned) reg <= page_count ); // allow page past end to be set - assert( (unsigned) bank < 0x100 ); - mmr [reg] = bank; - byte const* p = STATIC_CAST(byte const*,code) - HES_CPU_OFFSET( reg << page_bits ); - cpu_state->code_map [reg] = p; - cpu_state_.code_map [reg] = p; -} - -#endif +// PC Engine CPU emulator for use with HES music files + +// $package +#ifndef HES_CPU_H +#define HES_CPU_H + +#include "blargg_common.h" + +class Hes_Cpu { +public: + typedef BOOST::uint8_t byte; + typedef int time_t; + typedef int addr_t; + enum { future_time = INT_MAX/2 + 1 }; + + void reset(); + + enum { page_bits = 13 }; + enum { page_size = 1 << page_bits }; + enum { page_count = 0x10000 / page_size }; + void set_mmr( int reg, int bank, void const* code ); + + byte const* get_code( addr_t ); + + // NOT kept updated during emulation. + struct registers_t { + BOOST::uint16_t pc; + byte a; + byte x; + byte y; + byte flags; + byte sp; + }; + registers_t r; + + // page mapping registers + byte mmr [page_count + 1]; + + // Time of beginning of next instruction to be executed + time_t time() const { return cpu_state->time + cpu_state->base; } + void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } + void adjust_time( int delta ) { cpu_state->time += delta; } + + // Clocks past end (negative if before) + int time_past_end() const { return cpu_state->time; } + + // Time of next IRQ + time_t irq_time() const { return irq_time_; } + void set_irq_time( time_t ); + + // Emulation stops once time >= end_time + time_t end_time() const { return end_time_; } + void set_end_time( time_t ); + + // Subtracts t from all times + void end_frame( time_t t ); + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + +private: + // noncopyable + Hes_Cpu( const Hes_Cpu& ); + Hes_Cpu& operator = ( const Hes_Cpu& ); + + +// Implementation +public: + Hes_Cpu() { cpu_state = &cpu_state_; } + enum { irq_inhibit_mask = 0x04 }; + + struct cpu_state_t { + byte const* code_map [page_count + 1]; + time_t base; + int time; + }; + cpu_state_t* cpu_state; // points to cpu_state_ or a local copy + cpu_state_t cpu_state_; + time_t irq_time_; + time_t end_time_; + +private: + void set_code_page( int, void const* ); + inline void update_end_time( time_t end, time_t irq ); +}; + +#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> Hes_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define HES_CPU_OFFSET( addr ) (addr) +#else + #define HES_CPU_OFFSET( addr ) ((addr) & (Hes_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t const* Hes_Cpu::get_code( addr_t addr ) +{ + return cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr ); +} + +inline void Hes_Cpu::update_end_time( time_t end, time_t irq ) +{ + if ( end > irq && !(r.flags & irq_inhibit_mask) ) + end = irq; + + cpu_state->time += cpu_state->base - end; + cpu_state->base = end; +} + +inline void Hes_Cpu::set_irq_time( time_t t ) +{ + irq_time_ = t; + update_end_time( end_time_, t ); +} + +inline void Hes_Cpu::set_end_time( time_t t ) +{ + end_time_ = t; + update_end_time( t, irq_time_ ); +} + +inline void Hes_Cpu::end_frame( time_t t ) +{ + assert( cpu_state == &cpu_state_ ); + cpu_state_.base -= t; + if ( irq_time_ < future_time ) irq_time_ -= t; + if ( end_time_ < future_time ) end_time_ -= t; +} + +inline void Hes_Cpu::set_mmr( int reg, int bank, void const* code ) +{ + assert( (unsigned) reg <= page_count ); // allow page past end to be set + assert( (unsigned) bank < 0x100 ); + mmr [reg] = bank; + byte const* p = STATIC_CAST(byte const*,code) - HES_CPU_OFFSET( reg << page_bits ); + cpu_state->code_map [reg] = p; + cpu_state_.code_map [reg] = p; +} + +#endif diff --git a/Frameworks/GME/gme/Hes_Cpu_run.h b/Frameworks/GME/gme/Hes_Cpu_run.h index 2b9c2ae06..2e8843807 100644 --- a/Frameworks/GME/gme/Hes_Cpu_run.h +++ b/Frameworks/GME/gme/Hes_Cpu_run.h @@ -1,1342 +1,1342 @@ -// $package. http://www.slack.net/~ant/ - -#if 0 -/* Define these macros in the source file before #including this file. -- Parameters might be expressions, so they are best evaluated only once, -though they NEVER have side-effects, so multiple evaluation is OK. -- Output parameters might be a multiple-assignment expression like "a=x", -so they must NOT be parenthesized. -- Except where noted, time() and related functions will NOT work -correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and -CACHE_TIME() allow the time changing functions to work. -- Macros "returning" void may use a {} statement block. */ - - // 0 <= addr <= 0xFFFF + page_size - // time functions can be used - int READ_MEM( addr_t ); - void WRITE_MEM( addr_t, int data ); - - // 0 <= addr <= 0x1FF - int READ_LOW( addr_t ); - void WRITE_LOW( addr_t, int data ); - - // 0 <= addr <= 0xFFFF + page_size - // Used by common instructions. - int READ_FAST( addr_t, int& out ); - void WRITE_FAST( addr_t, int data ); - - // 0 <= addr <= 2 - // ST0, ST1, ST2 instructions - void WRITE_VDP( int addr, int data ); - -// The following can be used within macros: - - // Current time - time_t TIME(); - - // Allows use of time functions - void FLUSH_TIME(); - - // Must be used before end of macro if FLUSH_TIME() was used earlier - void CACHE_TIME(); - -// Configuration (optional; commented behavior if defined) - - // Expanded just before beginning of code, to help debugger - #define CPU_BEGIN void my_run_cpu() { -#endif - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -// TODO: support T flag, including clearing it at appropriate times? - -// all zero-page should really use whatever is at page 1, but that would -// reduce efficiency quite a bit -int const ram_addr = 0x2000; - -void Hes_Cpu::reset() -{ - check( cpu_state == &cpu_state_ ); - cpu_state = &cpu_state_; - - cpu_state_.time = 0; - cpu_state_.base = 0; - irq_time_ = future_time; - end_time_ = future_time; - - r.flags = 0x04; - r.sp = 0; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - - // Be sure "blargg_endian.h" has been #included - blargg_verify_byte_order(); -} - -// Allows MWCW debugger to step through code properly -#ifdef CPU_BEGIN - CPU_BEGIN -#endif - -// Time -#define TIME() (s_time + s.base) -#define FLUSH_TIME() {s.time = s_time;} -#define CACHE_TIME() {s_time = s.time;} - -// Memory -#define READ_STACK READ_LOW -#define WRITE_STACK WRITE_LOW - -#define CODE_PAGE( addr ) s.code_map [HES_CPU_PAGE( addr )] -#define CODE_OFFSET( addr ) HES_CPU_OFFSET( addr ) -#define READ_CODE( addr ) CODE_PAGE( addr ) [CODE_OFFSET( addr )] - -// Stack -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100) - -// Truncation -#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */ -#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */ -#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */ - -// Flags with hex value for clarity when used as mask. -// Stored in indicated variable during emulation. -int const n80 = 0x80; // nz -int const v40 = 0x40; // flags -//int const t20 = 0x20; -int const b10 = 0x10; -int const d08 = 0x08; // flags -int const i04 = 0x04; // flags -int const z02 = 0x02; // nz -int const c01 = 0x01; // c - -#define IS_NEG (nz & 0x8080) - -#define GET_FLAGS( out ) \ -{\ - out = flags & (v40 | d08 | i04);\ - out += ((nz >> 8) | nz) & n80;\ - out += c >> 8 & c01;\ - if ( !BYTE( nz ) )\ - out += z02;\ -} - -#define SET_FLAGS( in ) \ -{\ - flags = in & (v40 | d08 | i04);\ - c = nz = in << 8;\ - nz += ~in & z02;\ -} - -bool illegal_encountered = false; -{ - Hes_Cpu::cpu_state_t s = CPU.cpu_state_; - CPU.cpu_state = &s; - // even on x86, using s.time in place of s_time was slower - int s_time = s.time; - - // registers - int pc = CPU.r.pc; - int a = CPU.r.a; - int x = CPU.r.x; - int y = CPU.r.y; - int sp; - SET_SP( CPU.r.sp ); - - // Flags - int flags; - int c; // carry set if (c & 0x100) != 0 - int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 - { - int temp = CPU.r.flags; - SET_FLAGS( temp ); - } - -loop: - - #ifndef NDEBUG - { - time_t correct = CPU.end_time_; - if ( !(flags & i04) && correct > CPU.irq_time_ ) - correct = CPU.irq_time_; - check( s.base == correct ); - /* - static int count; - if ( count == 1844 ) Debugger(); - if ( s.base != correct ) dprintf( "%ld\n", count ); - count++; - */ - } - #endif - - // Check all values - check( (unsigned) sp - 0x100 < 0x100 ); - check( (unsigned) pc < 0x10000 + 0x100 ); // +0x100 so emulator can catch wrap-around - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - // Read instruction - byte const* instr = CODE_PAGE( pc ); - int opcode; - - if ( CODE_OFFSET(~0) == ~0 ) - { - opcode = instr [pc]; - pc++; - instr += pc; - } - else - { - instr += CODE_OFFSET( pc ); - opcode = *instr++; - pc++; - } - - // TODO: each reference lists slightly different timing values, ugh - static byte const clock_table [256] = - {// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,4,// 0 - 2,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,4,// 1 - 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,4,// 2 - 2,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,4,// 3 - 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,4,// 4 - 2,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,4,// 5 - 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,4,// 6 - 2,7,7,17,4,4,6,7,2,5,4,2,7,5,7,4,// 7 - 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// 8 - 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// 9 - 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// A - 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// B - 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// C - 2,7,7,17,2,4,6,7,2,5,3,2,2,5,7,4,// D - 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// E - 2,7,7,17,2,4,6,7,2,5,4,2,2,5,7,4 // F - }; // 0x00 was 8 - - // Update time - if ( s_time >= 0 ) - goto out_of_time; - - #ifdef HES_CPU_LOG_H - log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], - instr [3], instr [4], instr [5], a, x, y ); - //log_opcode( opcode ); - #endif - - s_time += clock_table [opcode]; - - int data; - data = *instr; - - switch ( opcode ) - { -// Macros - -#define GET_MSB() (instr [1]) -#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB()); -#define GET_ADDR() GET_LE16( instr ) - -// TODO: is the penalty really always added? the original 6502 was much better -//#define PAGE_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) -#define PAGE_PENALTY( lsb ) - -// Branch - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH_( cond, adj )\ -{\ - pc++;\ - if ( !(cond) ) goto loop;\ - pc = (BOOST::uint16_t) (pc + SBYTE( data ));\ - s_time += adj;\ - goto loop;\ -} - -#define BRANCH( cond ) BRANCH_( cond, 2 ) - - case 0xF0: // BEQ - BRANCH( !BYTE( nz ) ); - - case 0xD0: // BNE - BRANCH( BYTE( nz ) ); - - case 0x10: // BPL - BRANCH( !IS_NEG ); - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x50: // BVC - BRANCH( !(flags & v40) ) - - case 0x70: // BVS - BRANCH( flags & v40 ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x80: // BRA - branch_taken: - BRANCH_( true, 0 ); - - case 0xFF: - #ifdef IDLE_ADDR - if ( pc == IDLE_ADDR + 1 ) - goto idle_done; - #endif - - pc = (BOOST::uint16_t) pc; - - case 0x0F: // BBRn - case 0x1F: - case 0x2F: - case 0x3F: - case 0x4F: - case 0x5F: - case 0x6F: - case 0x7F: - case 0x8F: // BBSn - case 0x9F: - case 0xAF: - case 0xBF: - case 0xCF: - case 0xDF: - case 0xEF: { - // Make two copies of bits, one negated - int t = 0x101 * READ_LOW( data ); - t ^= 0xFF; - pc++; - data = GET_MSB(); - BRANCH( t & (1 << (opcode >> 4)) ) - } - - case 0x4C: // JMP abs - pc = GET_ADDR(); - goto loop; - - case 0x7C: // JMP (ind+X) - data += x; - case 0x6C:{// JMP (ind) - data += 0x100 * GET_MSB(); - pc = GET_LE16( &READ_CODE( data ) ); - goto loop; - } - -// Subroutine - - case 0x44: // BSR - WRITE_STACK( SP( -1 ), pc >> 8 ); - sp = SP( -2 ); - WRITE_STACK( sp, pc ); - goto branch_taken; - - case 0x20: { // JSR - int temp = pc + 1; - pc = GET_ADDR(); - WRITE_STACK( SP( -1 ), temp >> 8 ); - sp = SP( -2 ); - WRITE_STACK( sp, temp ); - goto loop; - } - - case 0x60: // RTS - pc = 1 + READ_STACK( sp ); - pc += 0x100 * READ_STACK( SP( 1 ) ); - sp = SP( 2 ); - goto loop; - - case 0x00: // BRK - goto handle_brk; - -// Common - - case 0xBD:{// LDA abs,X - PAGE_PENALTY( data + x ); - int addr = GET_ADDR() + x; - pc += 2; - READ_FAST( addr, nz ); - a = nz; - goto loop; - } - - case 0x9D:{// STA abs,X - int addr = GET_ADDR() + x; - pc += 2; - WRITE_FAST( addr, a ); - goto loop; - } - - case 0x95: // STA zp,x - data = BYTE( data + x ); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xAE:{// LDX abs - int addr = GET_ADDR(); - pc += 2; - READ_FAST( addr, nz ); - x = nz; - goto loop; - } - - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - -// Load/store - - { - int addr; - case 0x91: // STA (ind),Y - addr = 0x100 * READ_LOW( BYTE( data + 1 ) ); - addr += READ_LOW( data ) + y; - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - data = BYTE( data + x ); - case 0x92: // STA (ind) - addr = 0x100 * READ_LOW( BYTE( data + 1 ) ); - addr += READ_LOW( data ); - pc++; - goto sta_ptr; - - case 0x99: // STA abs,Y - data += y; - case 0x8D: // STA abs - addr = data + 0x100 * GET_MSB(); - pc += 2; - sta_ptr: - WRITE_FAST( addr, a ); - goto loop; - } - - { - int addr; - case 0xA1: // LDA (ind,X) - data = BYTE( data + x ); - case 0xB2: // LDA (ind) - addr = 0x100 * READ_LOW( BYTE( data + 1 ) ); - addr += READ_LOW( data ); - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - PAGE_PENALTY( addr ); - addr += 0x100 * READ_LOW( BYTE( data + 1 ) ); - pc++; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - data += y; - PAGE_PENALTY( data ); - case 0xAD: // LDA abs - addr = data + 0x100 * GET_MSB(); - pc += 2; - a_nz_read_addr: - READ_FAST( addr, nz ); - a = nz; - goto loop; - } - - case 0xBE:{// LDX abs,y - PAGE_PENALTY( data + y ); - int addr = GET_ADDR() + y; - pc += 2; - FLUSH_TIME(); - x = nz = READ_MEM( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xB5: // LDA zp,x - a = nz = READ_LOW( BYTE( data + x ) ); - pc++; - goto loop; - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - -// Bit operations - - case 0x3C: // BIT abs,x - data += x; - case 0x2C:{// BIT abs - int addr; - ADD_PAGE( addr ); - FLUSH_TIME(); - nz = READ_MEM( addr ); - CACHE_TIME(); - goto bit_common; - } - case 0x34: // BIT zp,x - data = BYTE( data + x ); - case 0x24: // BIT zp - data = READ_LOW( data ); - case 0x89: // BIT imm - nz = data; - bit_common: - pc++; - flags = (flags & ~v40) + (nz & v40); - if ( nz & a ) - goto loop; // Z should be clear, and nz must be non-zero if nz & a is - nz <<= 8; // set Z flag without affecting N flag - goto loop; - - { - int addr; - - case 0xB3: // TST abs,x - addr = GET_MSB() + x; - goto tst_abs; - - case 0x93: // TST abs - addr = GET_MSB(); - tst_abs: - addr += 0x100 * instr [2]; - pc++; - FLUSH_TIME(); - nz = READ_MEM( addr ); - CACHE_TIME(); - goto tst_common; - } - - case 0xA3: // TST zp,x - nz = READ_LOW( BYTE( GET_MSB() + x ) ); - goto tst_common; - - case 0x83: // TST zp - nz = READ_LOW( GET_MSB() ); - tst_common: - pc += 2; - flags = (flags & ~v40) + (nz & v40); - if ( nz & data ) - goto loop; // Z should be clear, and nz must be non-zero if nz & data is - nz <<= 8; // set Z flag without affecting N flag - goto loop; - - { - int addr; - case 0x0C: // TSB abs - case 0x1C: // TRB abs - addr = GET_ADDR(); - pc++; - goto txb_addr; - - // TODO: everyone lists different behaviors for the flags flags, ugh - case 0x04: // TSB zp - case 0x14: // TRB zp - addr = data + ram_addr; - txb_addr: - FLUSH_TIME(); - nz = a | READ_MEM( addr ); - if ( opcode & 0x10 ) - nz ^= a; // bits from a will already be set, so this clears them - flags = (flags & ~v40) + (nz & v40); - pc++; - WRITE_MEM( addr, nz ); - CACHE_TIME(); - goto loop; - } - - case 0x07: // RMBn - case 0x17: - case 0x27: - case 0x37: - case 0x47: - case 0x57: - case 0x67: - case 0x77: - pc++; - READ_LOW( data ) &= ~(1 << (opcode >> 4)); - goto loop; - - case 0x87: // SMBn - case 0x97: - case 0xA7: - case 0xB7: - case 0xC7: - case 0xD7: - case 0xE7: - case 0xF7: - pc++; - READ_LOW( data ) |= 1 << ((opcode >> 4) - 8); - goto loop; - -// Load/store - - case 0x9E: // STZ abs,x - data += x; - case 0x9C: // STZ abs - ADD_PAGE( data ); - pc++; - FLUSH_TIME(); - WRITE_MEM( data, 0 ); - CACHE_TIME(); - goto loop; - - case 0x74: // STZ zp,x - data = BYTE( data + x ); - case 0x64: // STZ zp - pc++; - WRITE_LOW( data, 0 ); - goto loop; - - case 0x94: // STY zp,x - data = BYTE( data + x ); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = BYTE( data + y ); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = BYTE( data + y ); - case 0xA6: // LDX zp - data = READ_LOW( data ); - case 0xA2: // LDX #imm - pc++; - x = data; - nz = data; - goto loop; - - case 0xB4: // LDY zp,x - data = BYTE( data + x ); - case 0xA4: // LDY zp - data = READ_LOW( data ); - case 0xA0: // LDY #imm - pc++; - y = data; - nz = data; - goto loop; - - case 0xBC: // LDY abs,X - data += x; - PAGE_PENALTY( data ); - case 0xAC:{// LDY abs - int addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ_MEM( addr ); - CACHE_TIME(); - goto loop; - } - - { - int temp; - case 0x8C: // STY abs - temp = y; - if ( 0 ) - case 0x8E: // STX abs - temp = x; - int addr = GET_ADDR(); - pc += 2; - FLUSH_TIME(); - WRITE_MEM( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - int addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ_MEM( addr ); - CACHE_TIME(); - goto cpx_data; - } - - case 0xE4: // CPX zp - data = READ_LOW( data ); - case 0xE0: // CPX #imm - cpx_data: - nz = x - data; - pc++; - c = ~nz; - nz = BYTE( nz ); - goto loop; - - case 0xCC:{// CPY abs - int addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ_MEM( addr ); - CACHE_TIME(); - goto cpy_data; - } - - case 0xC4: // CPY zp - data = READ_LOW( data ); - case 0xC0: // CPY #imm - cpy_data: - nz = y - data; - pc++; - c = ~nz; - nz = BYTE( nz ); - goto loop; - -// Logical - -#define ARITH_ADDR_MODES( op )\ - case op - 0x04: /* (ind,x) */\ - data = BYTE( data + x );\ - case op + 0x0D: /* (ind) */\ - data = 0x100 * READ_LOW( BYTE( data + 1 ) ) + READ_LOW( data );\ - goto ptr##op;\ - case op + 0x0C:{/* (ind),y */\ - int temp = READ_LOW( data ) + y;\ - PAGE_PENALTY( temp );\ - data = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\ - goto ptr##op;\ - }\ - case op + 0x10: /* zp,X */\ - data = BYTE( data + x );\ - case op + 0x00: /* zp */\ - data = READ_LOW( data );\ - goto imm##op;\ - case op + 0x14: /* abs,Y */\ - data += y;\ - goto ind##op;\ - case op + 0x18: /* abs,X */\ - data += x;\ - ind##op:\ - PAGE_PENALTY( data );\ - case op + 0x08: /* abs */\ - ADD_PAGE( data );\ - ptr##op:\ - FLUSH_TIME();\ - data = READ_MEM( data );\ - CACHE_TIME();\ - case op + 0x04: /* imm */\ - imm##op: - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz = BYTE( nz ); - goto loop; - - ARITH_ADDR_MODES( 0x25 ) // AND - nz = (a &= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x45 ) // EOR - nz = (a ^= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x05 ) // ORA - nz = (a |= data); - pc++; - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - if ( flags & d08 ) - dprintf( "Decimal mode not supported\n" ); - int carry = c >> 8 & 1; - int ov = (a ^ 0x80) + carry + SBYTE( data ); - flags = (flags & ~v40) + (ov >> 2 & v40); - c = nz = a + data + carry; - pc++; - a = BYTE( nz ); - goto loop; - } - -// Shift/rotate - - case 0x4A: // LSR A - c = 0; - case 0x6A: // ROR A - nz = c >> 1 & 0x80; - c = a << 8; - nz += a >> 1; - a = nz; - goto loop; - - case 0x0A: // ASL A - nz = a << 1; - c = nz; - a = BYTE( nz ); - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - int temp = c >> 8 & 1; - c = nz; - nz += temp; - a = BYTE( nz ); - goto loop; - } - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - ADD_PAGE( data ); - FLUSH_TIME(); - int temp = READ_MEM( data ); - nz = (c >> 1 & 0x80) + (temp >> 1); - c = temp << 8; - goto rotate_common; - } - - case 0x3E: // ROL abs,X - data += x; - goto rol_abs; - - case 0x1E: // ASL abs,X - data += x; - case 0x0E: // ASL abs - c = 0; - case 0x2E: // ROL abs - rol_abs: - ADD_PAGE( data ); - nz = c >> 8 & 1; - FLUSH_TIME(); - nz += (c = READ_MEM( data ) << 1); - rotate_common: - pc++; - WRITE_MEM( data, BYTE( nz ) ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = BYTE( data + x ); - goto ror_zp; - - case 0x56: // LSR zp,x - data = BYTE( data + x ); - case 0x46: // LSR zp - c = 0; - case 0x66: // ROR zp - ror_zp: { - int temp = READ_LOW( data ); - nz = (c >> 1 & 0x80) + (temp >> 1); - c = temp << 8; - goto write_nz_zp; - } - - case 0x36: // ROL zp,x - data = BYTE( data + x ); - goto rol_zp; - - case 0x16: // ASL zp,x - data = BYTE( data + x ); - case 0x06: // ASL zp - c = 0; - case 0x26: // ROL zp - rol_zp: - nz = c >> 8 & 1; - nz += (c = READ_LOW( data ) << 1); - goto write_nz_zp; - -// Increment/decrement - -#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop; - - case 0x1A: // INA - INC_DEC( a, +1 ) - - case 0xE8: // INX - INC_DEC( x, +1 ) - - case 0xC8: // INY - INC_DEC( y, +1 ) - - case 0x3A: // DEA - INC_DEC( a, -1 ) - - case 0xCA: // DEX - INC_DEC( x, -1 ) - - case 0x88: // DEY - INC_DEC( y, -1 ) - - case 0xF6: // INC zp,x - data = BYTE( data + x ); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = BYTE( data + x ); - case 0xC6: // DEC zp - nz = -1; - add_nz_zp: - nz += READ_LOW( data ); - write_nz_zp: - pc++; - WRITE_LOW( data, nz ); - goto loop; - - case 0xFE: // INC abs,x - data = x + GET_ADDR(); - goto inc_ptr; - - case 0xEE: // INC abs - data = GET_ADDR(); - inc_ptr: - nz = 1; - goto inc_common; - - case 0xDE: // DEC abs,x - data = x + GET_ADDR(); - goto dec_ptr; - - case 0xCE: // DEC abs - data = GET_ADDR(); - dec_ptr: - nz = -1; - inc_common: - FLUSH_TIME(); - pc += 2; - nz += READ_MEM( data ); - WRITE_MEM( data, BYTE( nz ) ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xA8: // TAY - y = nz = a; - goto loop; - - case 0x98: // TYA - a = nz = y; - goto loop; - - case 0xAA: // TAX - x = nz = a; - goto loop; - - case 0x8A: // TXA - a = nz = x; - goto loop; - - case 0x9A: // TXS - SET_SP( x ); // verified (no flag change) - goto loop; - - case 0xBA: // TSX - x = nz = GET_SP(); - goto loop; - - #define SWAP_REGS( r1, r2 ) {\ - int t = r1;\ - r1 = r2;\ - r2 = t;\ - goto loop;\ - } - - case 0x02: // SXY - SWAP_REGS( x, y ); - - case 0x22: // SAX - SWAP_REGS( a, x ); - - case 0x42: // SAY - SWAP_REGS( a, y ); - - case 0x62: // CLA - a = 0; - goto loop; - - case 0x82: // CLX - x = 0; - goto loop; - - case 0xC2: // CLY - y = 0; - goto loop; - -// Stack - - case 0x48: // PHA - sp = SP( -1 ); - WRITE_STACK( sp, a ); - goto loop; - - case 0x68: // PLA - a = nz = READ_STACK( sp ); - sp = SP( 1 ); - goto loop; - - case 0xDA: // PHX - sp = SP( -1 ); - WRITE_STACK( sp, x ); - goto loop; - - case 0x5A: // PHY - sp = SP( -1 ); - WRITE_STACK( sp, y ); - goto loop; - - case 0x40:{// RTI - pc = READ_STACK( SP( 1 ) ); - pc += READ_STACK( SP( 2 ) ) * 0x100; - int temp = READ_STACK( sp ); - sp = SP( 3 ); - data = flags; - SET_FLAGS( temp ); - CPU.r.flags = flags; // update externally-visible I flag - if ( (data ^ flags) & i04 ) - { - time_t new_time = CPU.end_time_; - if ( !(flags & i04) && new_time > CPU.irq_time_ ) - new_time = CPU.irq_time_; - int delta = s.base - new_time; - s.base = new_time; - s_time += delta; - } - goto loop; - } - - case 0xFA: // PLX - x = nz = READ_STACK( sp ); - sp = SP( 1 ); - goto loop; - - case 0x7A: // PLY - y = nz = READ_STACK( sp ); - sp = SP( 1 ); - goto loop; - - case 0x28:{// PLP - int temp = READ_STACK( sp ); - sp = SP( 1 ); - int changed = flags ^ temp; - SET_FLAGS( temp ); - if ( !(changed & i04) ) - goto loop; // I flag didn't change - if ( flags & i04 ) - goto handle_sei; - goto handle_cli; - } - - case 0x08:{// PHP - int temp; - GET_FLAGS( temp ); - sp = SP( -1 ); - WRITE_STACK( sp, temp | b10 ); - goto loop; - } - -// Flags - - case 0x38: // SEC - c = 0x100; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - flags &= ~v40; - goto loop; - - case 0xD8: // CLD - flags &= ~d08; - goto loop; - - case 0xF8: // SED - flags |= d08; - goto loop; - - case 0x58: // CLI - if ( !(flags & i04) ) - goto loop; - flags &= ~i04; - handle_cli: { - //dprintf( "CLI at %d\n", TIME ); - CPU.r.flags = flags; // update externally-visible I flag - int delta = s.base - CPU.irq_time_; - if ( delta <= 0 ) - { - if ( TIME() < CPU.irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = CPU.irq_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - if ( delta >= s_time + 1 ) - { - // delayed irq until after next instruction - s.base += s_time + 1; - s_time = -1; - CPU.irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop - goto loop; - } - - // TODO: implement - delayed_cli: - dprintf( "Delayed CLI not supported\n" ); - goto loop; - } - - case 0x78: // SEI - if ( flags & i04 ) - goto loop; - flags |= i04; - handle_sei: { - CPU.r.flags = flags; // update externally-visible I flag - int delta = s.base - CPU.end_time_; - s.base = CPU.end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - dprintf( "Delayed SEI not supported\n" ); - goto loop; - } - -// Special - - case 0x53:{// TAM - int bits = data; // avoid using data across function call - pc++; - for ( int i = 0; i < 8; i++ ) - if ( bits & (1 << i) ) - SET_MMR( i, a ); - goto loop; - } - - case 0x43:{// TMA - pc++; - byte const* in = CPU.mmr; - do - { - if ( data & 1 ) - a = *in; - in++; - } - while ( (data >>= 1) != 0 ); - goto loop; - } - - case 0x03: // ST0 - case 0x13: // ST1 - case 0x23:{// ST2 - int addr = opcode >> 4; - if ( addr ) - addr++; - pc++; - FLUSH_TIME(); - WRITE_VDP( addr, data ); - CACHE_TIME(); - goto loop; - } - - case 0xEA: // NOP - goto loop; - - case 0x54: // CSL - dprintf( "CSL not supported\n" ); - illegal_encountered = true; - goto loop; - - case 0xD4: // CSH - goto loop; - - case 0xF4: { // SET - //int operand = GET_MSB(); - dprintf( "SET not handled\n" ); - //switch ( data ) - //{ - //} - illegal_encountered = true; - goto loop; - } - -// Block transfer - - { - int in_alt; - int in_inc; - int out_alt; - int out_inc; - - case 0xE3: // TIA - in_alt = 0; - goto bxfer_alt; - - case 0xF3: // TAI - in_alt = 1; - bxfer_alt: - in_inc = in_alt ^ 1; - out_alt = in_inc; - out_inc = in_alt; - goto bxfer; - - case 0xD3: // TIN - in_inc = 1; - out_inc = 0; - goto bxfer_no_alt; - - case 0xC3: // TDD - in_inc = -1; - out_inc = -1; - goto bxfer_no_alt; - - case 0x73: // TII - in_inc = 1; - out_inc = 1; - bxfer_no_alt: - in_alt = 0; - out_alt = 0; - bxfer: - int in = GET_LE16( instr + 0 ); - int out = GET_LE16( instr + 2 ); - int count = GET_LE16( instr + 4 ); - if ( !count ) - count = 0x10000; - pc += 6; - WRITE_STACK( SP( -1 ), y ); - WRITE_STACK( SP( -2 ), a ); - WRITE_STACK( SP( -3 ), x ); - FLUSH_TIME(); - do - { - // TODO: reads from $0800-$1400 in I/O page should do I/O - int t = READ_MEM( in ); - in = WORD( in + in_inc ); - s.time += 6; - if ( in_alt ) - in_inc = -in_inc; - WRITE_MEM( out, t ); - out = WORD( out + out_inc ); - if ( out_alt ) - out_inc = -out_inc; - } - while ( --count ); - CACHE_TIME(); - goto loop; - } - -// Illegal - - default: - check( (unsigned) opcode <= 0xFF ); - dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); - illegal_encountered = true; - goto loop; - } - assert( false ); // catch missing 'goto loop' or accidental 'break' - - int result_; -handle_brk: - pc++; - result_ = 6; - -interrupt: - { - s_time += 7; - - // Save PC and read vector - WRITE_STACK( SP( -1 ), pc >> 8 ); - WRITE_STACK( SP( -2 ), pc ); - pc = GET_LE16( &READ_CODE( 0xFFF0 ) + result_ ); - - // Save flags - int temp; - GET_FLAGS( temp ); - if ( result_ == 6 ) - temp |= b10; // BRK sets B bit - sp = SP( -3 ); - WRITE_STACK( sp, temp ); - - // Update I flag in externally-visible flags - flags &= ~d08; - CPU.r.flags = (flags |= i04); - - // Update time - int delta = s.base - CPU.end_time_; - if ( delta >= 0 ) - goto loop; - s_time += delta; - s.base = CPU.end_time_; - goto loop; - } - -idle_done: - s_time = 0; - -out_of_time: - pc--; - - // Optional action that triggers interrupt or changes irq/end time - #ifdef CPU_DONE - { - CPU_DONE( result_ ); - if ( result_ >= 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - } - #endif - - // Flush cached state - CPU.r.pc = pc; - CPU.r.sp = GET_SP(); - CPU.r.a = a; - CPU.r.x = x; - CPU.r.y = y; - - int temp; - GET_FLAGS( temp ); - CPU.r.flags = temp; - - CPU.cpu_state_.base = s.base; - CPU.cpu_state_.time = s_time; - CPU.cpu_state = &CPU.cpu_state_; -} +// $package. http://www.slack.net/~ant/ + +#if 0 +/* Define these macros in the source file before #including this file. +- Parameters might be expressions, so they are best evaluated only once, +though they NEVER have side-effects, so multiple evaluation is OK. +- Output parameters might be a multiple-assignment expression like "a=x", +so they must NOT be parenthesized. +- Except where noted, time() and related functions will NOT work +correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and +CACHE_TIME() allow the time changing functions to work. +- Macros "returning" void may use a {} statement block. */ + + // 0 <= addr <= 0xFFFF + page_size + // time functions can be used + int READ_MEM( addr_t ); + void WRITE_MEM( addr_t, int data ); + + // 0 <= addr <= 0x1FF + int READ_LOW( addr_t ); + void WRITE_LOW( addr_t, int data ); + + // 0 <= addr <= 0xFFFF + page_size + // Used by common instructions. + int READ_FAST( addr_t, int& out ); + void WRITE_FAST( addr_t, int data ); + + // 0 <= addr <= 2 + // ST0, ST1, ST2 instructions + void WRITE_VDP( int addr, int data ); + +// The following can be used within macros: + + // Current time + time_t TIME(); + + // Allows use of time functions + void FLUSH_TIME(); + + // Must be used before end of macro if FLUSH_TIME() was used earlier + void CACHE_TIME(); + +// Configuration (optional; commented behavior if defined) + + // Expanded just before beginning of code, to help debugger + #define CPU_BEGIN void my_run_cpu() { +#endif + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// TODO: support T flag, including clearing it at appropriate times? + +// all zero-page should really use whatever is at page 1, but that would +// reduce efficiency quite a bit +int const ram_addr = 0x2000; + +void Hes_Cpu::reset() +{ + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; + + cpu_state_.time = 0; + cpu_state_.base = 0; + irq_time_ = future_time; + end_time_ = future_time; + + r.flags = 0x04; + r.sp = 0; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + + // Be sure "blargg_endian.h" has been #included + blargg_verify_byte_order(); +} + +// Allows MWCW debugger to step through code properly +#ifdef CPU_BEGIN + CPU_BEGIN +#endif + +// Time +#define TIME() (s_time + s.base) +#define FLUSH_TIME() {s.time = s_time;} +#define CACHE_TIME() {s_time = s.time;} + +// Memory +#define READ_STACK READ_LOW +#define WRITE_STACK WRITE_LOW + +#define CODE_PAGE( addr ) s.code_map [HES_CPU_PAGE( addr )] +#define CODE_OFFSET( addr ) HES_CPU_OFFSET( addr ) +#define READ_CODE( addr ) CODE_PAGE( addr ) [CODE_OFFSET( addr )] + +// Stack +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100) + +// Truncation +#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */ +#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */ +#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */ + +// Flags with hex value for clarity when used as mask. +// Stored in indicated variable during emulation. +int const n80 = 0x80; // nz +int const v40 = 0x40; // flags +//int const t20 = 0x20; +int const b10 = 0x10; +int const d08 = 0x08; // flags +int const i04 = 0x04; // flags +int const z02 = 0x02; // nz +int const c01 = 0x01; // c + +#define IS_NEG (nz & 0x8080) + +#define GET_FLAGS( out ) \ +{\ + out = flags & (v40 | d08 | i04);\ + out += ((nz >> 8) | nz) & n80;\ + out += c >> 8 & c01;\ + if ( !BYTE( nz ) )\ + out += z02;\ +} + +#define SET_FLAGS( in ) \ +{\ + flags = in & (v40 | d08 | i04);\ + c = nz = in << 8;\ + nz += ~in & z02;\ +} + +bool illegal_encountered = false; +{ + Hes_Cpu::cpu_state_t s = CPU.cpu_state_; + CPU.cpu_state = &s; + // even on x86, using s.time in place of s_time was slower + int s_time = s.time; + + // registers + int pc = CPU.r.pc; + int a = CPU.r.a; + int x = CPU.r.x; + int y = CPU.r.y; + int sp; + SET_SP( CPU.r.sp ); + + // Flags + int flags; + int c; // carry set if (c & 0x100) != 0 + int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + int temp = CPU.r.flags; + SET_FLAGS( temp ); + } + +loop: + + #ifndef NDEBUG + { + time_t correct = CPU.end_time_; + if ( !(flags & i04) && correct > CPU.irq_time_ ) + correct = CPU.irq_time_; + check( s.base == correct ); + /* + static int count; + if ( count == 1844 ) Debugger(); + if ( s.base != correct ) dprintf( "%ld\n", count ); + count++; + */ + } + #endif + + // Check all values + check( (unsigned) sp - 0x100 < 0x100 ); + check( (unsigned) pc < 0x10000 + 0x100 ); // +0x100 so emulator can catch wrap-around + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + // Read instruction + byte const* instr = CODE_PAGE( pc ); + int opcode; + + if ( CODE_OFFSET(~0) == ~0 ) + { + opcode = instr [pc]; + pc++; + instr += pc; + } + else + { + instr += CODE_OFFSET( pc ); + opcode = *instr++; + pc++; + } + + // TODO: each reference lists slightly different timing values, ugh + static byte const clock_table [256] = + {// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,4,// 0 + 2,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,4,// 1 + 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,4,// 2 + 2,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,4,// 3 + 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,4,// 4 + 2,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,4,// 5 + 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,4,// 6 + 2,7,7,17,4,4,6,7,2,5,4,2,7,5,7,4,// 7 + 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// 8 + 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// 9 + 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// A + 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// B + 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// C + 2,7,7,17,2,4,6,7,2,5,3,2,2,5,7,4,// D + 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// E + 2,7,7,17,2,4,6,7,2,5,4,2,2,5,7,4 // F + }; // 0x00 was 8 + + // Update time + if ( s_time >= 0 ) + goto out_of_time; + + #ifdef HES_CPU_LOG_H + log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], + instr [3], instr [4], instr [5], a, x, y ); + //log_opcode( opcode ); + #endif + + s_time += clock_table [opcode]; + + int data; + data = *instr; + + switch ( opcode ) + { +// Macros + +#define GET_MSB() (instr [1]) +#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB()); +#define GET_ADDR() GET_LE16( instr ) + +// TODO: is the penalty really always added? the original 6502 was much better +//#define PAGE_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) +#define PAGE_PENALTY( lsb ) + +// Branch + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH_( cond, adj )\ +{\ + pc++;\ + if ( !(cond) ) goto loop;\ + pc = (BOOST::uint16_t) (pc + SBYTE( data ));\ + s_time += adj;\ + goto loop;\ +} + +#define BRANCH( cond ) BRANCH_( cond, 2 ) + + case 0xF0: // BEQ + BRANCH( !BYTE( nz ) ); + + case 0xD0: // BNE + BRANCH( BYTE( nz ) ); + + case 0x10: // BPL + BRANCH( !IS_NEG ); + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0x50: // BVC + BRANCH( !(flags & v40) ) + + case 0x70: // BVS + BRANCH( flags & v40 ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x80: // BRA + branch_taken: + BRANCH_( true, 0 ); + + case 0xFF: + #ifdef IDLE_ADDR + if ( pc == IDLE_ADDR + 1 ) + goto idle_done; + #endif + + pc = (BOOST::uint16_t) pc; + + case 0x0F: // BBRn + case 0x1F: + case 0x2F: + case 0x3F: + case 0x4F: + case 0x5F: + case 0x6F: + case 0x7F: + case 0x8F: // BBSn + case 0x9F: + case 0xAF: + case 0xBF: + case 0xCF: + case 0xDF: + case 0xEF: { + // Make two copies of bits, one negated + int t = 0x101 * READ_LOW( data ); + t ^= 0xFF; + pc++; + data = GET_MSB(); + BRANCH( t & (1 << (opcode >> 4)) ) + } + + case 0x4C: // JMP abs + pc = GET_ADDR(); + goto loop; + + case 0x7C: // JMP (ind+X) + data += x; + case 0x6C:{// JMP (ind) + data += 0x100 * GET_MSB(); + pc = GET_LE16( &READ_CODE( data ) ); + goto loop; + } + +// Subroutine + + case 0x44: // BSR + WRITE_STACK( SP( -1 ), pc >> 8 ); + sp = SP( -2 ); + WRITE_STACK( sp, pc ); + goto branch_taken; + + case 0x20: { // JSR + int temp = pc + 1; + pc = GET_ADDR(); + WRITE_STACK( SP( -1 ), temp >> 8 ); + sp = SP( -2 ); + WRITE_STACK( sp, temp ); + goto loop; + } + + case 0x60: // RTS + pc = 1 + READ_STACK( sp ); + pc += 0x100 * READ_STACK( SP( 1 ) ); + sp = SP( 2 ); + goto loop; + + case 0x00: // BRK + goto handle_brk; + +// Common + + case 0xBD:{// LDA abs,X + PAGE_PENALTY( data + x ); + int addr = GET_ADDR() + x; + pc += 2; + READ_FAST( addr, nz ); + a = nz; + goto loop; + } + + case 0x9D:{// STA abs,X + int addr = GET_ADDR() + x; + pc += 2; + WRITE_FAST( addr, a ); + goto loop; + } + + case 0x95: // STA zp,x + data = BYTE( data + x ); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xAE:{// LDX abs + int addr = GET_ADDR(); + pc += 2; + READ_FAST( addr, nz ); + x = nz; + goto loop; + } + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + +// Load/store + + { + int addr; + case 0x91: // STA (ind),Y + addr = 0x100 * READ_LOW( BYTE( data + 1 ) ); + addr += READ_LOW( data ) + y; + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + data = BYTE( data + x ); + case 0x92: // STA (ind) + addr = 0x100 * READ_LOW( BYTE( data + 1 ) ); + addr += READ_LOW( data ); + pc++; + goto sta_ptr; + + case 0x99: // STA abs,Y + data += y; + case 0x8D: // STA abs + addr = data + 0x100 * GET_MSB(); + pc += 2; + sta_ptr: + WRITE_FAST( addr, a ); + goto loop; + } + + { + int addr; + case 0xA1: // LDA (ind,X) + data = BYTE( data + x ); + case 0xB2: // LDA (ind) + addr = 0x100 * READ_LOW( BYTE( data + 1 ) ); + addr += READ_LOW( data ); + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + PAGE_PENALTY( addr ); + addr += 0x100 * READ_LOW( BYTE( data + 1 ) ); + pc++; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + data += y; + PAGE_PENALTY( data ); + case 0xAD: // LDA abs + addr = data + 0x100 * GET_MSB(); + pc += 2; + a_nz_read_addr: + READ_FAST( addr, nz ); + a = nz; + goto loop; + } + + case 0xBE:{// LDX abs,y + PAGE_PENALTY( data + y ); + int addr = GET_ADDR() + y; + pc += 2; + FLUSH_TIME(); + x = nz = READ_MEM( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( BYTE( data + x ) ); + pc++; + goto loop; + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + +// Bit operations + + case 0x3C: // BIT abs,x + data += x; + case 0x2C:{// BIT abs + int addr; + ADD_PAGE( addr ); + FLUSH_TIME(); + nz = READ_MEM( addr ); + CACHE_TIME(); + goto bit_common; + } + case 0x34: // BIT zp,x + data = BYTE( data + x ); + case 0x24: // BIT zp + data = READ_LOW( data ); + case 0x89: // BIT imm + nz = data; + bit_common: + pc++; + flags = (flags & ~v40) + (nz & v40); + if ( nz & a ) + goto loop; // Z should be clear, and nz must be non-zero if nz & a is + nz <<= 8; // set Z flag without affecting N flag + goto loop; + + { + int addr; + + case 0xB3: // TST abs,x + addr = GET_MSB() + x; + goto tst_abs; + + case 0x93: // TST abs + addr = GET_MSB(); + tst_abs: + addr += 0x100 * instr [2]; + pc++; + FLUSH_TIME(); + nz = READ_MEM( addr ); + CACHE_TIME(); + goto tst_common; + } + + case 0xA3: // TST zp,x + nz = READ_LOW( BYTE( GET_MSB() + x ) ); + goto tst_common; + + case 0x83: // TST zp + nz = READ_LOW( GET_MSB() ); + tst_common: + pc += 2; + flags = (flags & ~v40) + (nz & v40); + if ( nz & data ) + goto loop; // Z should be clear, and nz must be non-zero if nz & data is + nz <<= 8; // set Z flag without affecting N flag + goto loop; + + { + int addr; + case 0x0C: // TSB abs + case 0x1C: // TRB abs + addr = GET_ADDR(); + pc++; + goto txb_addr; + + // TODO: everyone lists different behaviors for the flags flags, ugh + case 0x04: // TSB zp + case 0x14: // TRB zp + addr = data + ram_addr; + txb_addr: + FLUSH_TIME(); + nz = a | READ_MEM( addr ); + if ( opcode & 0x10 ) + nz ^= a; // bits from a will already be set, so this clears them + flags = (flags & ~v40) + (nz & v40); + pc++; + WRITE_MEM( addr, nz ); + CACHE_TIME(); + goto loop; + } + + case 0x07: // RMBn + case 0x17: + case 0x27: + case 0x37: + case 0x47: + case 0x57: + case 0x67: + case 0x77: + pc++; + READ_LOW( data ) &= ~(1 << (opcode >> 4)); + goto loop; + + case 0x87: // SMBn + case 0x97: + case 0xA7: + case 0xB7: + case 0xC7: + case 0xD7: + case 0xE7: + case 0xF7: + pc++; + READ_LOW( data ) |= 1 << ((opcode >> 4) - 8); + goto loop; + +// Load/store + + case 0x9E: // STZ abs,x + data += x; + case 0x9C: // STZ abs + ADD_PAGE( data ); + pc++; + FLUSH_TIME(); + WRITE_MEM( data, 0 ); + CACHE_TIME(); + goto loop; + + case 0x74: // STZ zp,x + data = BYTE( data + x ); + case 0x64: // STZ zp + pc++; + WRITE_LOW( data, 0 ); + goto loop; + + case 0x94: // STY zp,x + data = BYTE( data + x ); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = BYTE( data + y ); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = BYTE( data + y ); + case 0xA6: // LDX zp + data = READ_LOW( data ); + case 0xA2: // LDX #imm + pc++; + x = data; + nz = data; + goto loop; + + case 0xB4: // LDY zp,x + data = BYTE( data + x ); + case 0xA4: // LDY zp + data = READ_LOW( data ); + case 0xA0: // LDY #imm + pc++; + y = data; + nz = data; + goto loop; + + case 0xBC: // LDY abs,X + data += x; + PAGE_PENALTY( data ); + case 0xAC:{// LDY abs + int addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ_MEM( addr ); + CACHE_TIME(); + goto loop; + } + + { + int temp; + case 0x8C: // STY abs + temp = y; + if ( 0 ) + case 0x8E: // STX abs + temp = x; + int addr = GET_ADDR(); + pc += 2; + FLUSH_TIME(); + WRITE_MEM( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + int addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ_MEM( addr ); + CACHE_TIME(); + goto cpx_data; + } + + case 0xE4: // CPX zp + data = READ_LOW( data ); + case 0xE0: // CPX #imm + cpx_data: + nz = x - data; + pc++; + c = ~nz; + nz = BYTE( nz ); + goto loop; + + case 0xCC:{// CPY abs + int addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ_MEM( addr ); + CACHE_TIME(); + goto cpy_data; + } + + case 0xC4: // CPY zp + data = READ_LOW( data ); + case 0xC0: // CPY #imm + cpy_data: + nz = y - data; + pc++; + c = ~nz; + nz = BYTE( nz ); + goto loop; + +// Logical + +#define ARITH_ADDR_MODES( op )\ + case op - 0x04: /* (ind,x) */\ + data = BYTE( data + x );\ + case op + 0x0D: /* (ind) */\ + data = 0x100 * READ_LOW( BYTE( data + 1 ) ) + READ_LOW( data );\ + goto ptr##op;\ + case op + 0x0C:{/* (ind),y */\ + int temp = READ_LOW( data ) + y;\ + PAGE_PENALTY( temp );\ + data = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\ + goto ptr##op;\ + }\ + case op + 0x10: /* zp,X */\ + data = BYTE( data + x );\ + case op + 0x00: /* zp */\ + data = READ_LOW( data );\ + goto imm##op;\ + case op + 0x14: /* abs,Y */\ + data += y;\ + goto ind##op;\ + case op + 0x18: /* abs,X */\ + data += x;\ + ind##op:\ + PAGE_PENALTY( data );\ + case op + 0x08: /* abs */\ + ADD_PAGE( data );\ + ptr##op:\ + FLUSH_TIME();\ + data = READ_MEM( data );\ + CACHE_TIME();\ + case op + 0x04: /* imm */\ + imm##op: + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz = BYTE( nz ); + goto loop; + + ARITH_ADDR_MODES( 0x25 ) // AND + nz = (a &= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x45 ) // EOR + nz = (a ^= data); + pc++; + goto loop; + + ARITH_ADDR_MODES( 0x05 ) // ORA + nz = (a |= data); + pc++; + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + if ( flags & d08 ) + dprintf( "Decimal mode not supported\n" ); + int carry = c >> 8 & 1; + int ov = (a ^ 0x80) + carry + SBYTE( data ); + flags = (flags & ~v40) + (ov >> 2 & v40); + c = nz = a + data + carry; + pc++; + a = BYTE( nz ); + goto loop; + } + +// Shift/rotate + + case 0x4A: // LSR A + c = 0; + case 0x6A: // ROR A + nz = c >> 1 & 0x80; + c = a << 8; + nz += a >> 1; + a = nz; + goto loop; + + case 0x0A: // ASL A + nz = a << 1; + c = nz; + a = BYTE( nz ); + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + int temp = c >> 8 & 1; + c = nz; + nz += temp; + a = BYTE( nz ); + goto loop; + } + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + ADD_PAGE( data ); + FLUSH_TIME(); + int temp = READ_MEM( data ); + nz = (c >> 1 & 0x80) + (temp >> 1); + c = temp << 8; + goto rotate_common; + } + + case 0x3E: // ROL abs,X + data += x; + goto rol_abs; + + case 0x1E: // ASL abs,X + data += x; + case 0x0E: // ASL abs + c = 0; + case 0x2E: // ROL abs + rol_abs: + ADD_PAGE( data ); + nz = c >> 8 & 1; + FLUSH_TIME(); + nz += (c = READ_MEM( data ) << 1); + rotate_common: + pc++; + WRITE_MEM( data, BYTE( nz ) ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = BYTE( data + x ); + goto ror_zp; + + case 0x56: // LSR zp,x + data = BYTE( data + x ); + case 0x46: // LSR zp + c = 0; + case 0x66: // ROR zp + ror_zp: { + int temp = READ_LOW( data ); + nz = (c >> 1 & 0x80) + (temp >> 1); + c = temp << 8; + goto write_nz_zp; + } + + case 0x36: // ROL zp,x + data = BYTE( data + x ); + goto rol_zp; + + case 0x16: // ASL zp,x + data = BYTE( data + x ); + case 0x06: // ASL zp + c = 0; + case 0x26: // ROL zp + rol_zp: + nz = c >> 8 & 1; + nz += (c = READ_LOW( data ) << 1); + goto write_nz_zp; + +// Increment/decrement + +#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop; + + case 0x1A: // INA + INC_DEC( a, +1 ) + + case 0xE8: // INX + INC_DEC( x, +1 ) + + case 0xC8: // INY + INC_DEC( y, +1 ) + + case 0x3A: // DEA + INC_DEC( a, -1 ) + + case 0xCA: // DEX + INC_DEC( x, -1 ) + + case 0x88: // DEY + INC_DEC( y, -1 ) + + case 0xF6: // INC zp,x + data = BYTE( data + x ); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = BYTE( data + x ); + case 0xC6: // DEC zp + nz = -1; + add_nz_zp: + nz += READ_LOW( data ); + write_nz_zp: + pc++; + WRITE_LOW( data, nz ); + goto loop; + + case 0xFE: // INC abs,x + data = x + GET_ADDR(); + goto inc_ptr; + + case 0xEE: // INC abs + data = GET_ADDR(); + inc_ptr: + nz = 1; + goto inc_common; + + case 0xDE: // DEC abs,x + data = x + GET_ADDR(); + goto dec_ptr; + + case 0xCE: // DEC abs + data = GET_ADDR(); + dec_ptr: + nz = -1; + inc_common: + FLUSH_TIME(); + pc += 2; + nz += READ_MEM( data ); + WRITE_MEM( data, BYTE( nz ) ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xA8: // TAY + y = nz = a; + goto loop; + + case 0x98: // TYA + a = nz = y; + goto loop; + + case 0xAA: // TAX + x = nz = a; + goto loop; + + case 0x8A: // TXA + a = nz = x; + goto loop; + + case 0x9A: // TXS + SET_SP( x ); // verified (no flag change) + goto loop; + + case 0xBA: // TSX + x = nz = GET_SP(); + goto loop; + + #define SWAP_REGS( r1, r2 ) {\ + int t = r1;\ + r1 = r2;\ + r2 = t;\ + goto loop;\ + } + + case 0x02: // SXY + SWAP_REGS( x, y ); + + case 0x22: // SAX + SWAP_REGS( a, x ); + + case 0x42: // SAY + SWAP_REGS( a, y ); + + case 0x62: // CLA + a = 0; + goto loop; + + case 0x82: // CLX + x = 0; + goto loop; + + case 0xC2: // CLY + y = 0; + goto loop; + +// Stack + + case 0x48: // PHA + sp = SP( -1 ); + WRITE_STACK( sp, a ); + goto loop; + + case 0x68: // PLA + a = nz = READ_STACK( sp ); + sp = SP( 1 ); + goto loop; + + case 0xDA: // PHX + sp = SP( -1 ); + WRITE_STACK( sp, x ); + goto loop; + + case 0x5A: // PHY + sp = SP( -1 ); + WRITE_STACK( sp, y ); + goto loop; + + case 0x40:{// RTI + pc = READ_STACK( SP( 1 ) ); + pc += READ_STACK( SP( 2 ) ) * 0x100; + int temp = READ_STACK( sp ); + sp = SP( 3 ); + data = flags; + SET_FLAGS( temp ); + CPU.r.flags = flags; // update externally-visible I flag + if ( (data ^ flags) & i04 ) + { + time_t new_time = CPU.end_time_; + if ( !(flags & i04) && new_time > CPU.irq_time_ ) + new_time = CPU.irq_time_; + int delta = s.base - new_time; + s.base = new_time; + s_time += delta; + } + goto loop; + } + + case 0xFA: // PLX + x = nz = READ_STACK( sp ); + sp = SP( 1 ); + goto loop; + + case 0x7A: // PLY + y = nz = READ_STACK( sp ); + sp = SP( 1 ); + goto loop; + + case 0x28:{// PLP + int temp = READ_STACK( sp ); + sp = SP( 1 ); + int changed = flags ^ temp; + SET_FLAGS( temp ); + if ( !(changed & i04) ) + goto loop; // I flag didn't change + if ( flags & i04 ) + goto handle_sei; + goto handle_cli; + } + + case 0x08:{// PHP + int temp; + GET_FLAGS( temp ); + sp = SP( -1 ); + WRITE_STACK( sp, temp | b10 ); + goto loop; + } + +// Flags + + case 0x38: // SEC + c = 0x100; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + flags &= ~v40; + goto loop; + + case 0xD8: // CLD + flags &= ~d08; + goto loop; + + case 0xF8: // SED + flags |= d08; + goto loop; + + case 0x58: // CLI + if ( !(flags & i04) ) + goto loop; + flags &= ~i04; + handle_cli: { + //dprintf( "CLI at %d\n", TIME ); + CPU.r.flags = flags; // update externally-visible I flag + int delta = s.base - CPU.irq_time_; + if ( delta <= 0 ) + { + if ( TIME() < CPU.irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = CPU.irq_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + if ( delta >= s_time + 1 ) + { + // delayed irq until after next instruction + s.base += s_time + 1; + s_time = -1; + CPU.irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop + goto loop; + } + + // TODO: implement + delayed_cli: + dprintf( "Delayed CLI not supported\n" ); + goto loop; + } + + case 0x78: // SEI + if ( flags & i04 ) + goto loop; + flags |= i04; + handle_sei: { + CPU.r.flags = flags; // update externally-visible I flag + int delta = s.base - CPU.end_time_; + s.base = CPU.end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + dprintf( "Delayed SEI not supported\n" ); + goto loop; + } + +// Special + + case 0x53:{// TAM + int bits = data; // avoid using data across function call + pc++; + for ( int i = 0; i < 8; i++ ) + if ( bits & (1 << i) ) + SET_MMR( i, a ); + goto loop; + } + + case 0x43:{// TMA + pc++; + byte const* in = CPU.mmr; + do + { + if ( data & 1 ) + a = *in; + in++; + } + while ( (data >>= 1) != 0 ); + goto loop; + } + + case 0x03: // ST0 + case 0x13: // ST1 + case 0x23:{// ST2 + int addr = opcode >> 4; + if ( addr ) + addr++; + pc++; + FLUSH_TIME(); + WRITE_VDP( addr, data ); + CACHE_TIME(); + goto loop; + } + + case 0xEA: // NOP + goto loop; + + case 0x54: // CSL + dprintf( "CSL not supported\n" ); + illegal_encountered = true; + goto loop; + + case 0xD4: // CSH + goto loop; + + case 0xF4: { // SET + //int operand = GET_MSB(); + dprintf( "SET not handled\n" ); + //switch ( data ) + //{ + //} + illegal_encountered = true; + goto loop; + } + +// Block transfer + + { + int in_alt; + int in_inc; + int out_alt; + int out_inc; + + case 0xE3: // TIA + in_alt = 0; + goto bxfer_alt; + + case 0xF3: // TAI + in_alt = 1; + bxfer_alt: + in_inc = in_alt ^ 1; + out_alt = in_inc; + out_inc = in_alt; + goto bxfer; + + case 0xD3: // TIN + in_inc = 1; + out_inc = 0; + goto bxfer_no_alt; + + case 0xC3: // TDD + in_inc = -1; + out_inc = -1; + goto bxfer_no_alt; + + case 0x73: // TII + in_inc = 1; + out_inc = 1; + bxfer_no_alt: + in_alt = 0; + out_alt = 0; + bxfer: + int in = GET_LE16( instr + 0 ); + int out = GET_LE16( instr + 2 ); + int count = GET_LE16( instr + 4 ); + if ( !count ) + count = 0x10000; + pc += 6; + WRITE_STACK( SP( -1 ), y ); + WRITE_STACK( SP( -2 ), a ); + WRITE_STACK( SP( -3 ), x ); + FLUSH_TIME(); + do + { + // TODO: reads from $0800-$1400 in I/O page should do I/O + int t = READ_MEM( in ); + in = WORD( in + in_inc ); + s.time += 6; + if ( in_alt ) + in_inc = -in_inc; + WRITE_MEM( out, t ); + out = WORD( out + out_inc ); + if ( out_alt ) + out_inc = -out_inc; + } + while ( --count ); + CACHE_TIME(); + goto loop; + } + +// Illegal + + default: + check( (unsigned) opcode <= 0xFF ); + dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); + illegal_encountered = true; + goto loop; + } + assert( false ); // catch missing 'goto loop' or accidental 'break' + + int result_; +handle_brk: + pc++; + result_ = 6; + +interrupt: + { + s_time += 7; + + // Save PC and read vector + WRITE_STACK( SP( -1 ), pc >> 8 ); + WRITE_STACK( SP( -2 ), pc ); + pc = GET_LE16( &READ_CODE( 0xFFF0 ) + result_ ); + + // Save flags + int temp; + GET_FLAGS( temp ); + if ( result_ == 6 ) + temp |= b10; // BRK sets B bit + sp = SP( -3 ); + WRITE_STACK( sp, temp ); + + // Update I flag in externally-visible flags + flags &= ~d08; + CPU.r.flags = (flags |= i04); + + // Update time + int delta = s.base - CPU.end_time_; + if ( delta >= 0 ) + goto loop; + s_time += delta; + s.base = CPU.end_time_; + goto loop; + } + +idle_done: + s_time = 0; + +out_of_time: + pc--; + + // Optional action that triggers interrupt or changes irq/end time + #ifdef CPU_DONE + { + CPU_DONE( result_ ); + if ( result_ >= 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + } + #endif + + // Flush cached state + CPU.r.pc = pc; + CPU.r.sp = GET_SP(); + CPU.r.a = a; + CPU.r.x = x; + CPU.r.y = y; + + int temp; + GET_FLAGS( temp ); + CPU.r.flags = temp; + + CPU.cpu_state_.base = s.base; + CPU.cpu_state_.time = s_time; + CPU.cpu_state = &CPU.cpu_state_; +} diff --git a/Frameworks/GME/gme/Hes_Emu.h b/Frameworks/GME/gme/Hes_Emu.h index 50568da31..7d9cad3be 100644 --- a/Frameworks/GME/gme/Hes_Emu.h +++ b/Frameworks/GME/gme/Hes_Emu.h @@ -1,42 +1,42 @@ -// TurboGrafx-16/PC Engine HES music file emulator - -// Game_Music_Emu $vers -#ifndef HES_EMU_H -#define HES_EMU_H - -#include "Classic_Emu.h" -#include "Hes_Core.h" - -class Hes_Emu : public Classic_Emu { -public: - - static gme_type_t static_type() { return gme_hes_type; } - - // HES file header (see Hes_Core.h) - typedef Hes_Core::header_t header_t; - - // Header for currently loaded file - header_t const& header() const { return core.header(); } - - blargg_err_t hash_( Hash_Function& ) const; - -// Implementation -public: - Hes_Emu(); - ~Hes_Emu(); - virtual void unload(); - -protected: - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t load_( Data_Reader& ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - -private: - Hes_Core core; -}; - -#endif +// TurboGrafx-16/PC Engine HES music file emulator + +// Game_Music_Emu $vers +#ifndef HES_EMU_H +#define HES_EMU_H + +#include "Classic_Emu.h" +#include "Hes_Core.h" + +class Hes_Emu : public Classic_Emu { +public: + + static gme_type_t static_type() { return gme_hes_type; } + + // HES file header (see Hes_Core.h) + typedef Hes_Core::header_t header_t; + + // Header for currently loaded file + header_t const& header() const { return core.header(); } + + blargg_err_t hash_( Hash_Function& ) const; + +// Implementation +public: + Hes_Emu(); + ~Hes_Emu(); + virtual void unload(); + +protected: + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + +private: + Hes_Core core; +}; + +#endif diff --git a/Frameworks/GME/gme/K051649_Emu.cpp b/Frameworks/GME/gme/K051649_Emu.cpp deleted file mode 100644 index 26aaa6794..000000000 --- a/Frameworks/GME/gme/K051649_Emu.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "K051649_Emu.h" -#include "k051649.h" - -K051649_Emu::K051649_Emu() { SCC = 0; } - -K051649_Emu::~K051649_Emu() -{ - if ( SCC ) device_stop_k051649( SCC ); -} - -int K051649_Emu::set_rate( int clock_rate ) -{ - if ( SCC ) - { - device_stop_k051649( SCC ); - SCC = 0; - } - - SCC = device_start_k051649( clock_rate ); - if ( !SCC ) - return 1; - - reset(); - return 0; -} - -void K051649_Emu::reset() -{ - device_reset_k051649( SCC ); - k051649_set_mute_mask( SCC, 0 ); -} - -void K051649_Emu::write( int port, int offset, int data ) -{ - k051649_w( SCC, (port << 1) | 0x00, offset); - k051649_w( SCC, (port << 1) | 0x01, data); -} - -void K051649_Emu::mute_voices( int mask ) -{ - k051649_set_mute_mask( SCC, mask ); -} - -void K051649_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - k051649_update( SCC, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/K051649_Emu.h b/Frameworks/GME/gme/K051649_Emu.h deleted file mode 100644 index f45214db6..000000000 --- a/Frameworks/GME/gme/K051649_Emu.h +++ /dev/null @@ -1,33 +0,0 @@ -// K051649 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef K051649_EMU_H -#define K051649_EMU_H - -class K051649_Emu { - void* SCC; -public: - K051649_Emu(); - ~K051649_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 5 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int port, int offset, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/K053260_Emu.cpp b/Frameworks/GME/gme/K053260_Emu.cpp deleted file mode 100644 index 8444b8c4d..000000000 --- a/Frameworks/GME/gme/K053260_Emu.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "K053260_Emu.h" -#include "k053260.h" - -K053260_Emu::K053260_Emu() { chip = 0; } - -K053260_Emu::~K053260_Emu() -{ - if ( chip ) device_stop_k053260( chip ); -} - -int K053260_Emu::set_rate( int clock_rate ) -{ - if ( chip ) - { - device_stop_k053260( chip ); - chip = 0; - } - - chip = device_start_k053260( clock_rate ); - if ( !chip ) - return 1; - - reset(); - return 0; -} - -void K053260_Emu::reset() -{ - device_reset_k053260( chip ); - k053260_set_mute_mask( chip, 0 ); -} - -void K053260_Emu::write( int addr, int data ) -{ - k053260_w( chip, addr, data); -} - -void K053260_Emu::write_rom( int size, int start, int length, void * data ) -{ - k053260_write_rom( chip, size, start, length, (const UINT8 *) data ); -} - -void K053260_Emu::mute_voices( int mask ) -{ - k053260_set_mute_mask( chip, mask ); -} - -void K053260_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - k053260_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/K053260_Emu.h b/Frameworks/GME/gme/K053260_Emu.h deleted file mode 100644 index af346c1cc..000000000 --- a/Frameworks/GME/gme/K053260_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// K053260 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef K053260_EMU_H -#define K053260_EMU_H - -class K053260_Emu { - void* chip; -public: - K053260_Emu(); - ~K053260_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 5 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/K054539_Emu.cpp b/Frameworks/GME/gme/K054539_Emu.cpp deleted file mode 100644 index 3c25eb633..000000000 --- a/Frameworks/GME/gme/K054539_Emu.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "K054539_Emu.h" -#include "k054539.h" - -K054539_Emu::K054539_Emu() { chip = 0; } - -K054539_Emu::~K054539_Emu() -{ - if ( chip ) device_stop_k054539( chip ); -} - -int K054539_Emu::set_rate( int clock_rate, int flags ) -{ - if ( chip ) - { - device_stop_k054539( chip ); - chip = 0; - } - - chip = device_start_k054539( clock_rate ); - if ( !chip ) - return 1; - - k054539_init_flags( chip, flags ); - - reset(); - return 0; -} - -void K054539_Emu::reset() -{ - device_reset_k054539( chip ); - k054539_set_mute_mask( chip, 0 ); -} - -void K054539_Emu::write( int addr, int data ) -{ - k054539_w( chip, addr, data); -} - -void K054539_Emu::write_rom( int size, int start, int length, void * data ) -{ - k054539_write_rom( chip, size, start, length, (const UINT8 *) data ); -} - -void K054539_Emu::mute_voices( int mask ) -{ - k054539_set_mute_mask( chip, mask ); -} - -void K054539_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - k054539_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/K054539_Emu.h b/Frameworks/GME/gme/K054539_Emu.h deleted file mode 100644 index 1af976212..000000000 --- a/Frameworks/GME/gme/K054539_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// K054539 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef K054539_EMU_H -#define K054539_EMU_H - -class K054539_Emu { - void* chip; -public: - K054539_Emu(); - ~K054539_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock_rate, int flags ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 5 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Kss_Core.cpp b/Frameworks/GME/gme/Kss_Core.cpp index 97ac2ded5..cf39ecd29 100644 --- a/Frameworks/GME/gme/Kss_Core.cpp +++ b/Frameworks/GME/gme/Kss_Core.cpp @@ -1,214 +1,214 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Kss_Core.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2006-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Kss_Core::Kss_Core() : rom( Kss_Cpu::page_size ) -{ - memset( unmapped_read, 0xFF, sizeof unmapped_read ); -} - -Kss_Core::~Kss_Core() { } - -void Kss_Core::unload() -{ - rom.clear(); -} - -static blargg_err_t check_kss_header( void const* header ) -{ - if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) - return blargg_err_file_type; - return blargg_ok; -} - -blargg_err_t Kss_Core::load_( Data_Reader& in ) -{ - memset( &header_, 0, sizeof header_ ); - assert( offsetof (header_t,msx_audio_vol) == header_t::size - 1 ); - RETURN_ERR( rom.load( in, header_t::base_size, &header_, 0 ) ); - - RETURN_ERR( check_kss_header( header_.tag ) ); - - header_.last_track [0] = 255; - if ( header_.tag [3] == 'C' ) - { - if ( header_.extra_header ) - { - header_.extra_header = 0; - set_warning( "Unknown data in header" ); - } - if ( header_.device_flags & ~0x0F ) - { - header_.device_flags &= 0x0F; - set_warning( "Unknown data in header" ); - } - } - else if ( header_.extra_header ) - { - if ( header_.extra_header != header_.ext_size ) - { - header_.extra_header = 0; - set_warning( "Invalid extra_header_size" ); - } - else - { - memcpy( header_.data_size, rom.begin(), header_.ext_size ); - } - } - - #ifndef NDEBUG - { - int ram_mode = header_.device_flags & 0x84; // MSX - if ( header_.device_flags & 0x02 ) // SMS - ram_mode = (header_.device_flags & 0x88); - - if ( ram_mode ) - dprintf( "RAM not supported\n" ); // TODO: support - } - #endif - - return blargg_ok; -} - -void Kss_Core::jsr( byte const (&addr) [2] ) -{ - ram [--cpu.r.sp] = idle_addr >> 8; - ram [--cpu.r.sp] = idle_addr & 0xFF; - cpu.r.pc = get_le16( addr ); -} - -blargg_err_t Kss_Core::start_track( int track ) -{ - memset( ram, 0xC9, 0x4000 ); - memset( ram + 0x4000, 0, sizeof ram - 0x4000 ); - - // copy driver code to lo RAM - static byte const bios [] = { - 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG - 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG - }; - static byte const vectors [] = { - 0xC3, 0x01, 0x00, // $0093: WRTPSG vector - 0xC3, 0x09, 0x00, // $0096: RDPSG vector - }; - memcpy( ram + 0x01, bios, sizeof bios ); - memcpy( ram + 0x93, vectors, sizeof vectors ); - - // copy non-banked data into RAM - int load_addr = get_le16( header_.load_addr ); - int orig_load_size = get_le16( header_.load_size ); - int load_size = min( orig_load_size, rom.file_size() ); - load_size = min( load_size, (int) mem_size - load_addr ); - if ( load_size != orig_load_size ) - set_warning( "Excessive data size" ); - memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size ); - - rom.set_addr( -load_size - header_.extra_header ); - - // check available bank data - int const bank_size = this->bank_size(); - int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size; - bank_count = header_.bank_mode & 0x7F; - if ( bank_count > max_banks ) - { - bank_count = max_banks; - set_warning( "Bank data missing" ); - } - //dprintf( "load_size : $%X\n", load_size ); - //dprintf( "bank_size : $%X\n", bank_size ); - //dprintf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); - - ram [idle_addr] = 0xFF; - cpu.reset( unmapped_write, unmapped_read ); - cpu.map_mem( 0, mem_size, ram, ram ); - - cpu.r.sp = 0xF380; - cpu.r.b.a = track; - cpu.r.b.h = 0; - next_play = play_period; - gain_updated = false; - jsr( header_.init_addr ); - - return blargg_ok; -} - -void Kss_Core::set_bank( int logical, int physical ) -{ - int const bank_size = this->bank_size(); - - int addr = 0x8000; - if ( logical && bank_size == 8 * 1024 ) - addr = 0xA000; - - physical -= header_.first_bank; - if ( (unsigned) physical >= (unsigned) bank_count ) - { - byte* data = ram + addr; - cpu.map_mem( addr, bank_size, data, data ); - } - else - { - int phys = physical * bank_size; - for ( int offset = 0; offset < bank_size; offset += cpu.page_size ) - cpu.map_mem( addr + offset, cpu.page_size, - unmapped_write, rom.at_addr( phys + offset ) ); - } -} - -void Kss_Core::cpu_out( time_t, addr_t addr, int data ) -{ - dprintf( "OUT $%04X,$%02X\n", addr, data ); -} - -int Kss_Core::cpu_in( time_t, addr_t addr ) -{ - dprintf( "IN $%04X\n", addr ); - return 0xFF; -} - -blargg_err_t Kss_Core::end_frame( time_t end ) -{ - while ( cpu.time() < end ) - { - time_t next = min( end, next_play ); - run_cpu( next ); - if ( cpu.r.pc == idle_addr ) - cpu.set_time( next ); - - if ( cpu.time() >= next_play ) - { - next_play += play_period; - if ( cpu.r.pc == idle_addr ) - { - if ( !gain_updated ) - { - gain_updated = true; - update_gain(); - } - - jsr( header_.play_addr ); - } - } - } - - next_play -= end; - check( next_play >= 0 ); - cpu.adjust_time( -end ); - - return blargg_ok; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Kss_Core.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Kss_Core::Kss_Core() : rom( Kss_Cpu::page_size ) +{ + memset( unmapped_read, 0xFF, sizeof unmapped_read ); +} + +Kss_Core::~Kss_Core() { } + +void Kss_Core::unload() +{ + rom.clear(); +} + +static blargg_err_t check_kss_header( void const* header ) +{ + if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) + return blargg_err_file_type; + return blargg_ok; +} + +blargg_err_t Kss_Core::load_( Data_Reader& in ) +{ + memset( &header_, 0, sizeof header_ ); + assert( offsetof (header_t,msx_audio_vol) == header_t::size - 1 ); + RETURN_ERR( rom.load( in, header_t::base_size, &header_, 0 ) ); + + RETURN_ERR( check_kss_header( header_.tag ) ); + + header_.last_track [0] = 255; + if ( header_.tag [3] == 'C' ) + { + if ( header_.extra_header ) + { + header_.extra_header = 0; + set_warning( "Unknown data in header" ); + } + if ( header_.device_flags & ~0x0F ) + { + header_.device_flags &= 0x0F; + set_warning( "Unknown data in header" ); + } + } + else if ( header_.extra_header ) + { + if ( header_.extra_header != header_.ext_size ) + { + header_.extra_header = 0; + set_warning( "Invalid extra_header_size" ); + } + else + { + memcpy( header_.data_size, rom.begin(), header_.ext_size ); + } + } + + #ifndef NDEBUG + { + int ram_mode = header_.device_flags & 0x84; // MSX + if ( header_.device_flags & 0x02 ) // SMS + ram_mode = (header_.device_flags & 0x88); + + if ( ram_mode ) + dprintf( "RAM not supported\n" ); // TODO: support + } + #endif + + return blargg_ok; +} + +void Kss_Core::jsr( byte const (&addr) [2] ) +{ + ram [--cpu.r.sp] = idle_addr >> 8; + ram [--cpu.r.sp] = idle_addr & 0xFF; + cpu.r.pc = get_le16( addr ); +} + +blargg_err_t Kss_Core::start_track( int track ) +{ + memset( ram, 0xC9, 0x4000 ); + memset( ram + 0x4000, 0, sizeof ram - 0x4000 ); + + // copy driver code to lo RAM + static byte const bios [] = { + 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG + 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG + }; + static byte const vectors [] = { + 0xC3, 0x01, 0x00, // $0093: WRTPSG vector + 0xC3, 0x09, 0x00, // $0096: RDPSG vector + }; + memcpy( ram + 0x01, bios, sizeof bios ); + memcpy( ram + 0x93, vectors, sizeof vectors ); + + // copy non-banked data into RAM + int load_addr = get_le16( header_.load_addr ); + int orig_load_size = get_le16( header_.load_size ); + int load_size = min( orig_load_size, rom.file_size() ); + load_size = min( load_size, (int) mem_size - load_addr ); + if ( load_size != orig_load_size ) + set_warning( "Excessive data size" ); + memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size ); + + rom.set_addr( -load_size - header_.extra_header ); + + // check available bank data + int const bank_size = this->bank_size(); + int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size; + bank_count = header_.bank_mode & 0x7F; + if ( bank_count > max_banks ) + { + bank_count = max_banks; + set_warning( "Bank data missing" ); + } + //dprintf( "load_size : $%X\n", load_size ); + //dprintf( "bank_size : $%X\n", bank_size ); + //dprintf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); + + ram [idle_addr] = 0xFF; + cpu.reset( unmapped_write, unmapped_read ); + cpu.map_mem( 0, mem_size, ram, ram ); + + cpu.r.sp = 0xF380; + cpu.r.b.a = track; + cpu.r.b.h = 0; + next_play = play_period; + gain_updated = false; + jsr( header_.init_addr ); + + return blargg_ok; +} + +void Kss_Core::set_bank( int logical, int physical ) +{ + int const bank_size = this->bank_size(); + + int addr = 0x8000; + if ( logical && bank_size == 8 * 1024 ) + addr = 0xA000; + + physical -= header_.first_bank; + if ( (unsigned) physical >= (unsigned) bank_count ) + { + byte* data = ram + addr; + cpu.map_mem( addr, bank_size, data, data ); + } + else + { + int phys = physical * bank_size; + for ( int offset = 0; offset < bank_size; offset += cpu.page_size ) + cpu.map_mem( addr + offset, cpu.page_size, + unmapped_write, rom.at_addr( phys + offset ) ); + } +} + +void Kss_Core::cpu_out( time_t, addr_t addr, int data ) +{ + dprintf( "OUT $%04X,$%02X\n", addr, data ); +} + +int Kss_Core::cpu_in( time_t, addr_t addr ) +{ + dprintf( "IN $%04X\n", addr ); + return 0xFF; +} + +blargg_err_t Kss_Core::end_frame( time_t end ) +{ + while ( cpu.time() < end ) + { + time_t next = min( end, next_play ); + run_cpu( next ); + if ( cpu.r.pc == idle_addr ) + cpu.set_time( next ); + + if ( cpu.time() >= next_play ) + { + next_play += play_period; + if ( cpu.r.pc == idle_addr ) + { + if ( !gain_updated ) + { + gain_updated = true; + update_gain(); + } + + jsr( header_.play_addr ); + } + } + } + + next_play -= end; + check( next_play >= 0 ); + cpu.adjust_time( -end ); + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Kss_Core.h b/Frameworks/GME/gme/Kss_Core.h index 67ec94e20..07926bae8 100644 --- a/Frameworks/GME/gme/Kss_Core.h +++ b/Frameworks/GME/gme/Kss_Core.h @@ -1,100 +1,100 @@ -// MSX computer KSS music file emulator - -// Game_Music_Emu $vers -#ifndef KSS_CORE_H -#define KSS_CORE_H - -#include "Gme_Loader.h" -#include "Rom_Data.h" -#include "Z80_Cpu.h" - -class Kss_Core : public Gme_Loader { -public: - // KSS file header - struct header_t - { - enum { size = 0x20 }; - enum { base_size = 0x10 }; - enum { ext_size = size - base_size }; - - byte tag [4]; - byte load_addr [2]; - byte load_size [2]; - byte init_addr [2]; - byte play_addr [2]; - byte first_bank; - byte bank_mode; - byte extra_header; - byte device_flags; - - // KSSX extended data, if extra_header==0x10 - byte data_size [4]; - byte unused [4]; - byte first_track [2]; - byte last_track [2]; // if no extended data, we set this to 0xFF - byte psg_vol; - byte scc_vol; - byte msx_music_vol; - byte msx_audio_vol; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - // ROM data - Rom_Data const& rom_() const { return rom; } - - typedef int time_t; - void set_play_period( time_t p ) { play_period = p; } - - blargg_err_t start_track( int ); - - blargg_err_t end_frame( time_t ); - -protected: - typedef Z80_Cpu Kss_Cpu; - Kss_Cpu cpu; - - void set_bank( int logical, int physical ); - - typedef int addr_t; - virtual void cpu_write( addr_t, int ) = 0; - virtual int cpu_in( time_t, addr_t ); - virtual void cpu_out( time_t, addr_t, int ); - - // Called after one frame of emulation - virtual void update_gain() = 0; - -// Implementation -public: - Kss_Core(); - virtual ~Kss_Core(); - -protected: - virtual blargg_err_t load_( Data_Reader& ); - virtual void unload(); - -private: - enum { idle_addr = 0xFFFF }; - - Rom_Data rom; - header_t header_; - bool gain_updated; - int bank_count; - time_t play_period; - time_t next_play; - - // large items - enum { mem_size = 0x10000 }; - byte ram [mem_size + Kss_Cpu::cpu_padding]; - byte unmapped_read [0x100]; // TODO: why isn't this page_size? - // because CPU can't read beyond this in last page? or because it will spill into unmapped_write? - - byte unmapped_write [Kss_Cpu::page_size]; - - int bank_size() const { return (16 * 1024) >> (header_.bank_mode >> 7 & 1); } - bool run_cpu( time_t end ); - void jsr( byte const (&addr) [2] ); -}; - -#endif +// MSX computer KSS music file emulator + +// Game_Music_Emu $vers +#ifndef KSS_CORE_H +#define KSS_CORE_H + +#include "Gme_Loader.h" +#include "Rom_Data.h" +#include "Z80_Cpu.h" + +class Kss_Core : public Gme_Loader { +public: + // KSS file header + struct header_t + { + enum { size = 0x20 }; + enum { base_size = 0x10 }; + enum { ext_size = size - base_size }; + + byte tag [4]; + byte load_addr [2]; + byte load_size [2]; + byte init_addr [2]; + byte play_addr [2]; + byte first_bank; + byte bank_mode; + byte extra_header; + byte device_flags; + + // KSSX extended data, if extra_header==0x10 + byte data_size [4]; + byte unused [4]; + byte first_track [2]; + byte last_track [2]; // if no extended data, we set this to 0xFF + byte psg_vol; + byte scc_vol; + byte msx_music_vol; + byte msx_audio_vol; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + // ROM data + Rom_Data const& rom_() const { return rom; } + + typedef int time_t; + void set_play_period( time_t p ) { play_period = p; } + + blargg_err_t start_track( int ); + + blargg_err_t end_frame( time_t ); + +protected: + typedef Z80_Cpu Kss_Cpu; + Kss_Cpu cpu; + + void set_bank( int logical, int physical ); + + typedef int addr_t; + virtual void cpu_write( addr_t, int ) = 0; + virtual int cpu_in( time_t, addr_t ); + virtual void cpu_out( time_t, addr_t, int ); + + // Called after one frame of emulation + virtual void update_gain() = 0; + +// Implementation +public: + Kss_Core(); + virtual ~Kss_Core(); + +protected: + virtual blargg_err_t load_( Data_Reader& ); + virtual void unload(); + +private: + enum { idle_addr = 0xFFFF }; + + Rom_Data rom; + header_t header_; + bool gain_updated; + int bank_count; + time_t play_period; + time_t next_play; + + // large items + enum { mem_size = 0x10000 }; + byte ram [mem_size + Kss_Cpu::cpu_padding]; + byte unmapped_read [0x100]; // TODO: why isn't this page_size? + // because CPU can't read beyond this in last page? or because it will spill into unmapped_write? + + byte unmapped_write [Kss_Cpu::page_size]; + + int bank_size() const { return (16 * 1024) >> (header_.bank_mode >> 7 & 1); } + bool run_cpu( time_t end ); + void jsr( byte const (&addr) [2] ); +}; + +#endif diff --git a/Frameworks/GME/gme/Kss_Emu.cpp b/Frameworks/GME/gme/Kss_Emu.cpp index e7643b50c..0036034d9 100644 --- a/Frameworks/GME/gme/Kss_Emu.cpp +++ b/Frameworks/GME/gme/Kss_Emu.cpp @@ -1,493 +1,493 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Kss_Emu.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2006-2009 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#define IF_PTR( ptr ) if ( ptr ) (ptr) - -int const clock_rate = 3579545; - -#define FOR_EACH_APU( macro )\ -{\ - macro( sms.psg );\ - macro( sms.fm );\ - macro( msx.psg );\ - macro( msx.scc );\ - macro( msx.music );\ - macro( msx.audio );\ -} - -Kss_Emu::Kss_Emu() : - core( this ) -{ - #define ACTION( apu ) { core.apu = NULL; } - FOR_EACH_APU( ACTION ); - #undef ACTION - - set_type( gme_kss_type ); -} - -Kss_Emu::~Kss_Emu() -{ - unload(); -} - -inline void Kss_Emu::Core::unload() -{ - #define ACTION( ptr ) { delete (ptr); (ptr) = 0; } - FOR_EACH_APU( ACTION ); - #undef ACTION -} - -void Kss_Emu::unload() -{ - core.unload(); - Classic_Emu::unload(); -} - -// Track info - -static void copy_kss_fields( Kss_Core::header_t const& h, track_info_t* out ) -{ - const char* system = "MSX"; - - if ( h.device_flags & 0x02 ) - { - system = "Sega Master System"; - if ( h.device_flags & 0x04 ) - system = "Game Gear"; - - if ( h.device_flags & 0x01 ) - system = "Sega Mark III"; - } - else - { - if ( h.device_flags & 0x09 ) - system = "MSX + FM Sound"; - } - Gme_File::copy_field_( out->system, system ); -} - -static void hash_kss_file( Kss_Core::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) -{ - out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); - out.hash_( &h.load_size[0], sizeof(h.load_size) ); - out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); - out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); - out.hash_( &h.first_bank, sizeof(h.first_bank) ); - out.hash_( &h.bank_mode, sizeof(h.bank_mode) ); - out.hash_( &h.extra_header, sizeof(h.extra_header) ); - out.hash_( &h.device_flags, sizeof(h.device_flags) ); - - out.hash_( data, data_size ); -} - -blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const -{ - copy_kss_fields( header(), out ); -// TODO: remove -//if ( msx.music ) strcpy( out->system, "msxmusic" ); -//if ( msx.audio ) strcpy( out->system, "msxaudio" ); -//if ( sms.fm ) strcpy( out->system, "fmunit" ); - return blargg_ok; -} - -static blargg_err_t check_kss_header( void const* header ) -{ - if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) - return blargg_err_file_type; - - return blargg_ok; -} - -struct Kss_File : Gme_Info_ -{ - Kss_Emu::header_t const* header_; - - Kss_File() { set_type( gme_kss_type ); } - - blargg_err_t load_mem_( byte const begin [], int size ) - { - header_ = ( Kss_Emu::header_t const* ) begin; - - if ( header_->tag [3] == 'X' && header_->extra_header == 0x10 ) - set_track_count( get_le16( header_->last_track ) + 1 ); - - return check_kss_header( header_ ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_kss_fields( *header_, out ); - return blargg_ok; - } - - blargg_err_t hash_( Hash_Function& out ) const - { - hash_kss_file( *header_, file_begin() + Kss_Core::header_t::base_size, file_end() - file_begin() - Kss_Core::header_t::base_size, out ); - return blargg_ok; - } -}; - -static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; } -static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; } - -gme_type_t_ const gme_kss_type [1] = {{ - "MSX", - 256, - &new_kss_emu, - &new_kss_file, - "KSS", - 0x03 -}}; - -// Setup - -void Kss_Emu::Core::update_gain_() -{ - double g = emu.gain(); - if ( msx.music || msx.audio || sms.fm ) - { - g *= 0.3; - } - else - { - g *= 1.2; - if ( scc_accessed ) - g *= 1.4; - } - - #define ACTION( apu ) IF_PTR( apu )->volume( g ) - FOR_EACH_APU( ACTION ); - #undef ACTION -} - -static blargg_err_t new_opl_apu( Opl_Apu::type_t type, Opl_Apu** out ) -{ - check( !*out ); - CHECK_ALLOC( *out = BLARGG_NEW( Opl_Apu ) ); - blip_time_t const period = 72; - int const rate = clock_rate / period; - return (*out)->init( rate * period, rate, period, type ); -} - -blargg_err_t Kss_Emu::load_( Data_Reader& in ) -{ - RETURN_ERR( core.load( in ) ); - set_warning( core.warning() ); - - set_track_count( get_le16( header().last_track ) + 1 ); - - core.scc_enabled = false; - if ( header().device_flags & 0x02 ) // Sega Master System - { - int const osc_count = Sms_Apu::osc_count + Opl_Apu::osc_count; - static const char* const names [osc_count] = { - "Square 1", "Square 2", "Square 3", "Noise", "FM" - }; - set_voice_names( names ); - - static int const types [osc_count] = { - wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0 - }; - set_voice_types( types ); - - // sms.psg - set_voice_count( Sms_Apu::osc_count ); - check( !core.sms.psg ); - CHECK_ALLOC( core.sms.psg = BLARGG_NEW Sms_Apu ); - - // sms.fm - if ( header().device_flags & 0x01 ) - { - set_voice_count( osc_count ); - RETURN_ERR( new_opl_apu( Opl_Apu::type_smsfmunit, &core.sms.fm ) ); - } - - } - else // MSX - { - int const osc_count = Ay_Apu::osc_count + Opl_Apu::osc_count; - static const char* const names [osc_count] = { - "Square 1", "Square 2", "Square 3", "FM" - }; - set_voice_names( names ); - - static int const types [osc_count] = { - wave_type+1, wave_type+3, wave_type+2, wave_type+0 - }; - set_voice_types( types ); - - // msx.psg - set_voice_count( Ay_Apu::osc_count ); - check( !core.msx.psg ); - CHECK_ALLOC( core.msx.psg = BLARGG_NEW Ay_Apu ); - - if ( header().device_flags & 0x10 ) - set_warning( "MSX stereo not supported" ); - - // msx.music - if ( header().device_flags & 0x01 ) - { - set_voice_count( osc_count ); - RETURN_ERR( new_opl_apu( Opl_Apu::type_msxmusic, &core.msx.music ) ); - } - - // msx.audio - if ( header().device_flags & 0x08 ) - { - set_voice_count( osc_count ); - RETURN_ERR( new_opl_apu( Opl_Apu::type_msxaudio, &core.msx.audio ) ); - } - - if ( !(header().device_flags & 0x80) ) - { - if ( !(header().device_flags & 0x84) ) - core.scc_enabled = core.scc_enabled_true; - - // msx.scc - check( !core.msx.scc ); - CHECK_ALLOC( core.msx.scc = BLARGG_NEW Scc_Apu ); - - int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; - static const char* const names [osc_count] = { - "Square 1", "Square 2", "Square 3", - "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" - }; - set_voice_names( names ); - - static int const types [osc_count] = { - wave_type+1, wave_type+3, wave_type+2, - wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7, - }; - set_voice_types( types ); - - set_voice_count( osc_count ); - } - } - - set_silence_lookahead( 6 ); - if ( core.sms.fm || core.msx.music || core.msx.audio ) - { - if ( !Opl_Apu::supported() ) - set_warning( "FM sound not supported" ); - else - set_silence_lookahead( 3 ); // Opl_Apu is really slow - } - - return setup_buffer( ::clock_rate ); -} - -void Kss_Emu::update_eq( blip_eq_t const& eq ) -{ - #define ACTION( apu ) IF_PTR( core.apu )->treble_eq( eq ) - FOR_EACH_APU( ACTION ); - #undef ACTION -} - -void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - if ( core.sms.psg ) // Sega Master System - { - i -= core.sms.psg->osc_count; - if ( i < 0 ) - { - core.sms.psg->set_output( i + core.sms.psg->osc_count, center, left, right ); - return; - } - - if ( core.sms.fm && i < core.sms.fm->osc_count ) - core.sms.fm->set_output( i, center, NULL, NULL ); - } - else if ( core.msx.psg ) // MSX - { - i -= core.msx.psg->osc_count; - if ( i < 0 ) - { - core.msx.psg->set_output( i + core.msx.psg->osc_count, center ); - return; - } - - if ( core.msx.scc && i < core.msx.scc->osc_count ) core.msx.scc ->set_output( i, center ); - if ( core.msx.music && i < core.msx.music->osc_count ) core.msx.music->set_output( i, center, NULL, NULL ); - if ( core.msx.audio && i < core.msx.audio->osc_count ) core.msx.audio->set_output( i, center, NULL, NULL ); - } -} - -void Kss_Emu::set_tempo_( double t ) -{ - int period = (header().device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); - core.set_play_period( (Kss_Core::time_t) (period / t) ); -} - -blargg_err_t Kss_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - #define ACTION( apu ) IF_PTR( core.apu )->reset() - FOR_EACH_APU( ACTION ); - #undef ACTION - - core.scc_accessed = false; - core.update_gain_(); - - return core.start_track( track ); -} - -void Kss_Emu::Core::cpu_write_( addr_t addr, int data ) -{ - // TODO: SCC+ support - - data &= 0xFF; - switch ( addr ) - { - case 0x9000: - set_bank( 0, data ); - return; - - case 0xB000: - set_bank( 1, data ); - return; - - case 0xBFFE: // selects between mapping areas (we just always enable both) - if ( data == 0 || data == 0x20 ) - return; - } - - int scc_addr = (addr & 0xDFFF) - 0x9800; - if ( (unsigned) scc_addr < 0xB0 && msx.scc ) - { - scc_accessed = true; - //if ( (unsigned) (scc_addr - 0x90) < 0x10 ) - // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F - if ( scc_addr < Scc_Apu::reg_count ) - msx.scc->write( cpu.time(), addr, data ); - return; - } - - dprintf( "LD ($%04X),$%02X\n", addr, data ); -} - -void Kss_Emu::Core::cpu_write( addr_t addr, int data ) -{ - *cpu.write( addr ) = data; - if ( (addr & scc_enabled) == 0x8000 ) - cpu_write_( addr, data ); -} - -void Kss_Emu::Core::cpu_out( time_t time, addr_t addr, int data ) -{ - data &= 0xFF; - switch ( addr & 0xFF ) - { - case 0xA0: - if ( msx.psg ) - msx.psg->write_addr( data ); - return; - - case 0xA1: - if ( msx.psg ) - msx.psg->write_data( time, data ); - return; - - case 0x06: - if ( sms.psg && (header().device_flags & 0x04) ) - { - sms.psg->write_ggstereo( time, data ); - return; - } - break; - - case 0x7E: - case 0x7F: - if ( sms.psg ) - { - sms.psg->write_data( time, data ); - return; - } - break; - - #define OPL_WRITE_HANDLER( base, opl )\ - case base : if ( opl ) { opl->write_addr( data ); return; } break;\ - case base+1: if ( opl ) { opl->write_data( time, data ); return; } break; - - OPL_WRITE_HANDLER( 0x7C, msx.music ) - OPL_WRITE_HANDLER( 0xC0, msx.audio ) - OPL_WRITE_HANDLER( 0xF0, sms.fm ) - - case 0xFE: - set_bank( 0, data ); - return; - - #ifndef NDEBUG - case 0xA8: // PPI - return; - #endif - } - - Kss_Core::cpu_out( time, addr, data ); -} - -int Kss_Emu::Core::cpu_in( time_t time, addr_t addr ) -{ - switch ( addr & 0xFF ) - { - case 0xC0: - case 0xC1: - if ( msx.audio ) - return msx.audio->read( time, addr & 1 ); - break; - - case 0xA2: - if ( msx.psg ) - return msx.psg->read(); - break; - - #ifndef NDEBUG - case 0xA8: // PPI - return 0; - #endif - } - - return Kss_Core::cpu_in( time, addr ); -} - -void Kss_Emu::Core::update_gain() -{ - if ( scc_accessed ) - { - dprintf( "SCC accessed\n" ); - update_gain_(); - } -} - -blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) -{ - RETURN_ERR( core.end_frame( duration ) ); - - #define ACTION( apu ) IF_PTR( core.apu )->end_frame( duration ) - FOR_EACH_APU( ACTION ); - #undef ACTION - - return blargg_ok; -} - -blargg_err_t Kss_Emu::hash_( Hash_Function& out ) const -{ - hash_kss_file( header(), core.rom_().begin(), core.rom_().file_size(), out ); - return blargg_ok; +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Kss_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define IF_PTR( ptr ) if ( ptr ) (ptr) + +int const clock_rate = 3579545; + +#define FOR_EACH_APU( macro )\ +{\ + macro( sms.psg );\ + macro( sms.fm );\ + macro( msx.psg );\ + macro( msx.scc );\ + macro( msx.music );\ + macro( msx.audio );\ +} + +Kss_Emu::Kss_Emu() : + core( this ) +{ + #define ACTION( apu ) { core.apu = NULL; } + FOR_EACH_APU( ACTION ); + #undef ACTION + + set_type( gme_kss_type ); +} + +Kss_Emu::~Kss_Emu() +{ + unload(); +} + +inline void Kss_Emu::Core::unload() +{ + #define ACTION( ptr ) { delete (ptr); (ptr) = 0; } + FOR_EACH_APU( ACTION ); + #undef ACTION +} + +void Kss_Emu::unload() +{ + core.unload(); + Classic_Emu::unload(); +} + +// Track info + +static void copy_kss_fields( Kss_Core::header_t const& h, track_info_t* out ) +{ + const char* system = "MSX"; + + if ( h.device_flags & 0x02 ) + { + system = "Sega Master System"; + if ( h.device_flags & 0x04 ) + system = "Game Gear"; + + if ( h.device_flags & 0x01 ) + system = "Sega Mark III"; + } + else + { + if ( h.device_flags & 0x09 ) + system = "MSX + FM Sound"; + } + Gme_File::copy_field_( out->system, system ); +} + +static void hash_kss_file( Kss_Core::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.load_size[0], sizeof(h.load_size) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.first_bank, sizeof(h.first_bank) ); + out.hash_( &h.bank_mode, sizeof(h.bank_mode) ); + out.hash_( &h.extra_header, sizeof(h.extra_header) ); + out.hash_( &h.device_flags, sizeof(h.device_flags) ); + + out.hash_( data, data_size ); +} + +blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const +{ + copy_kss_fields( header(), out ); +// TODO: remove +//if ( msx.music ) strcpy( out->system, "msxmusic" ); +//if ( msx.audio ) strcpy( out->system, "msxaudio" ); +//if ( sms.fm ) strcpy( out->system, "fmunit" ); + return blargg_ok; +} + +static blargg_err_t check_kss_header( void const* header ) +{ + if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) + return blargg_err_file_type; + + return blargg_ok; +} + +struct Kss_File : Gme_Info_ +{ + Kss_Emu::header_t const* header_; + + Kss_File() { set_type( gme_kss_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + header_ = ( Kss_Emu::header_t const* ) begin; + + if ( header_->tag [3] == 'X' && header_->extra_header == 0x10 ) + set_track_count( get_le16( header_->last_track ) + 1 ); + + return check_kss_header( header_ ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_kss_fields( *header_, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_kss_file( *header_, file_begin() + Kss_Core::header_t::base_size, file_end() - file_begin() - Kss_Core::header_t::base_size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; } +static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; } + +gme_type_t_ const gme_kss_type [1] = {{ + "MSX", + 256, + &new_kss_emu, + &new_kss_file, + "KSS", + 0x03 +}}; + +// Setup + +void Kss_Emu::Core::update_gain_() +{ + double g = emu.gain(); + if ( msx.music || msx.audio || sms.fm ) + { + g *= 0.3; + } + else + { + g *= 1.2; + if ( scc_accessed ) + g *= 1.4; + } + + #define ACTION( apu ) IF_PTR( apu )->volume( g ) + FOR_EACH_APU( ACTION ); + #undef ACTION +} + +static blargg_err_t new_opl_apu( Opl_Apu::type_t type, Opl_Apu** out ) +{ + check( !*out ); + CHECK_ALLOC( *out = BLARGG_NEW( Opl_Apu ) ); + blip_time_t const period = 72; + int const rate = clock_rate / period; + return (*out)->init( rate * period, rate, period, type ); +} + +blargg_err_t Kss_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core.load( in ) ); + set_warning( core.warning() ); + + set_track_count( get_le16( header().last_track ) + 1 ); + + core.scc_enabled = false; + if ( header().device_flags & 0x02 ) // Sega Master System + { + int const osc_count = Sms_Apu::osc_count + Opl_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", "Noise", "FM" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0 + }; + set_voice_types( types ); + + // sms.psg + set_voice_count( Sms_Apu::osc_count ); + check( !core.sms.psg ); + CHECK_ALLOC( core.sms.psg = BLARGG_NEW Sms_Apu ); + + // sms.fm + if ( header().device_flags & 0x01 ) + { + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_smsfmunit, &core.sms.fm ) ); + } + + } + else // MSX + { + int const osc_count = Ay_Apu::osc_count + Opl_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", "FM" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, wave_type+0 + }; + set_voice_types( types ); + + // msx.psg + set_voice_count( Ay_Apu::osc_count ); + check( !core.msx.psg ); + CHECK_ALLOC( core.msx.psg = BLARGG_NEW Ay_Apu ); + + if ( header().device_flags & 0x10 ) + set_warning( "MSX stereo not supported" ); + + // msx.music + if ( header().device_flags & 0x01 ) + { + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_msxmusic, &core.msx.music ) ); + } + + // msx.audio + if ( header().device_flags & 0x08 ) + { + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_msxaudio, &core.msx.audio ) ); + } + + if ( !(header().device_flags & 0x80) ) + { + if ( !(header().device_flags & 0x84) ) + core.scc_enabled = core.scc_enabled_true; + + // msx.scc + check( !core.msx.scc ); + CHECK_ALLOC( core.msx.scc = BLARGG_NEW Scc_Apu ); + + int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, + wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7, + }; + set_voice_types( types ); + + set_voice_count( osc_count ); + } + } + + set_silence_lookahead( 6 ); + if ( core.sms.fm || core.msx.music || core.msx.audio ) + { + if ( !Opl_Apu::supported() ) + set_warning( "FM sound not supported" ); + else + set_silence_lookahead( 3 ); // Opl_Apu is really slow + } + + return setup_buffer( ::clock_rate ); +} + +void Kss_Emu::update_eq( blip_eq_t const& eq ) +{ + #define ACTION( apu ) IF_PTR( core.apu )->treble_eq( eq ) + FOR_EACH_APU( ACTION ); + #undef ACTION +} + +void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + if ( core.sms.psg ) // Sega Master System + { + i -= core.sms.psg->osc_count; + if ( i < 0 ) + { + core.sms.psg->set_output( i + core.sms.psg->osc_count, center, left, right ); + return; + } + + if ( core.sms.fm && i < core.sms.fm->osc_count ) + core.sms.fm->set_output( i, center, NULL, NULL ); + } + else if ( core.msx.psg ) // MSX + { + i -= core.msx.psg->osc_count; + if ( i < 0 ) + { + core.msx.psg->set_output( i + core.msx.psg->osc_count, center ); + return; + } + + if ( core.msx.scc && i < core.msx.scc->osc_count ) core.msx.scc ->set_output( i, center ); + if ( core.msx.music && i < core.msx.music->osc_count ) core.msx.music->set_output( i, center, NULL, NULL ); + if ( core.msx.audio && i < core.msx.audio->osc_count ) core.msx.audio->set_output( i, center, NULL, NULL ); + } +} + +void Kss_Emu::set_tempo_( double t ) +{ + int period = (header().device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); + core.set_play_period( (Kss_Core::time_t) (period / t) ); +} + +blargg_err_t Kss_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + #define ACTION( apu ) IF_PTR( core.apu )->reset() + FOR_EACH_APU( ACTION ); + #undef ACTION + + core.scc_accessed = false; + core.update_gain_(); + + return core.start_track( track ); +} + +void Kss_Emu::Core::cpu_write_( addr_t addr, int data ) +{ + // TODO: SCC+ support + + data &= 0xFF; + switch ( addr ) + { + case 0x9000: + set_bank( 0, data ); + return; + + case 0xB000: + set_bank( 1, data ); + return; + + case 0xBFFE: // selects between mapping areas (we just always enable both) + if ( data == 0 || data == 0x20 ) + return; + } + + int scc_addr = (addr & 0xDFFF) - 0x9800; + if ( (unsigned) scc_addr < 0xB0 && msx.scc ) + { + scc_accessed = true; + //if ( (unsigned) (scc_addr - 0x90) < 0x10 ) + // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F + if ( scc_addr < Scc_Apu::reg_count ) + msx.scc->write( cpu.time(), addr, data ); + return; + } + + dprintf( "LD ($%04X),$%02X\n", addr, data ); +} + +void Kss_Emu::Core::cpu_write( addr_t addr, int data ) +{ + *cpu.write( addr ) = data; + if ( (addr & scc_enabled) == 0x8000 ) + cpu_write_( addr, data ); +} + +void Kss_Emu::Core::cpu_out( time_t time, addr_t addr, int data ) +{ + data &= 0xFF; + switch ( addr & 0xFF ) + { + case 0xA0: + if ( msx.psg ) + msx.psg->write_addr( data ); + return; + + case 0xA1: + if ( msx.psg ) + msx.psg->write_data( time, data ); + return; + + case 0x06: + if ( sms.psg && (header().device_flags & 0x04) ) + { + sms.psg->write_ggstereo( time, data ); + return; + } + break; + + case 0x7E: + case 0x7F: + if ( sms.psg ) + { + sms.psg->write_data( time, data ); + return; + } + break; + + #define OPL_WRITE_HANDLER( base, opl )\ + case base : if ( opl ) { opl->write_addr( data ); return; } break;\ + case base+1: if ( opl ) { opl->write_data( time, data ); return; } break; + + OPL_WRITE_HANDLER( 0x7C, msx.music ) + OPL_WRITE_HANDLER( 0xC0, msx.audio ) + OPL_WRITE_HANDLER( 0xF0, sms.fm ) + + case 0xFE: + set_bank( 0, data ); + return; + + #ifndef NDEBUG + case 0xA8: // PPI + return; + #endif + } + + Kss_Core::cpu_out( time, addr, data ); +} + +int Kss_Emu::Core::cpu_in( time_t time, addr_t addr ) +{ + switch ( addr & 0xFF ) + { + case 0xC0: + case 0xC1: + if ( msx.audio ) + return msx.audio->read( time, addr & 1 ); + break; + + case 0xA2: + if ( msx.psg ) + return msx.psg->read(); + break; + + #ifndef NDEBUG + case 0xA8: // PPI + return 0; + #endif + } + + return Kss_Core::cpu_in( time, addr ); +} + +void Kss_Emu::Core::update_gain() +{ + if ( scc_accessed ) + { + dprintf( "SCC accessed\n" ); + update_gain_(); + } +} + +blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) +{ + RETURN_ERR( core.end_frame( duration ) ); + + #define ACTION( apu ) IF_PTR( core.apu )->end_frame( duration ) + FOR_EACH_APU( ACTION ); + #undef ACTION + + return blargg_ok; +} + +blargg_err_t Kss_Emu::hash_( Hash_Function& out ) const +{ + hash_kss_file( header(), core.rom_().begin(), core.rom_().file_size(), out ); + return blargg_ok; } \ No newline at end of file diff --git a/Frameworks/GME/gme/Kss_Emu.h b/Frameworks/GME/gme/Kss_Emu.h index cfc7d0730..f0d229f2f 100644 --- a/Frameworks/GME/gme/Kss_Emu.h +++ b/Frameworks/GME/gme/Kss_Emu.h @@ -1,79 +1,79 @@ -// MSX computer KSS music file emulator - -// Game_Music_Emu $vers -#ifndef KSS_EMU_H -#define KSS_EMU_H - -#include "Classic_Emu.h" -#include "Kss_Core.h" -#include "Kss_Scc_Apu.h" -#include "Sms_Apu.h" -#include "Ay_Apu.h" -#include "Opl_Apu.h" - -class Kss_Emu : public Classic_Emu { -public: - // KSS file header (see Kss_Core.h) - typedef Kss_Core::header_t header_t; - - // Header for currently loaded file - header_t const& header() const { return core.header(); } - - blargg_err_t hash_( Hash_Function& ) const; - - static gme_type_t static_type() { return gme_kss_type; } - -// Implementation -public: - Kss_Emu(); - ~Kss_Emu(); - -protected: - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t load_( Data_Reader& ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - virtual void unload(); - -private: - struct Core; - friend struct Core; - struct Core : Kss_Core { - Kss_Emu& emu; - - // detection of tunes that use SCC so they can be made louder - bool scc_accessed; - - enum { scc_enabled_true = 0xC000 }; - unsigned scc_enabled; // 0 or 0xC000 - int ay_latch; - - struct { - Sms_Apu* psg; - Opl_Apu* fm; - } sms; - - struct { - Ay_Apu* psg; - Scc_Apu* scc; - Opl_Apu* music; - Opl_Apu* audio; - } msx; - - Core( Kss_Emu* e ) : emu( *e ) { } - - virtual void cpu_write( addr_t, int ); - virtual int cpu_in( time_t, addr_t ); - virtual void cpu_out( time_t, addr_t, int ); - virtual void update_gain(); - - void cpu_write_( addr_t addr, int data ); - void update_gain_(); - void unload(); - } core; -}; - -#endif +// MSX computer KSS music file emulator + +// Game_Music_Emu $vers +#ifndef KSS_EMU_H +#define KSS_EMU_H + +#include "Classic_Emu.h" +#include "Kss_Core.h" +#include "Kss_Scc_Apu.h" +#include "Sms_Apu.h" +#include "Ay_Apu.h" +#include "Opl_Apu.h" + +class Kss_Emu : public Classic_Emu { +public: + // KSS file header (see Kss_Core.h) + typedef Kss_Core::header_t header_t; + + // Header for currently loaded file + header_t const& header() const { return core.header(); } + + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_kss_type; } + +// Implementation +public: + Kss_Emu(); + ~Kss_Emu(); + +protected: + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual void unload(); + +private: + struct Core; + friend struct Core; + struct Core : Kss_Core { + Kss_Emu& emu; + + // detection of tunes that use SCC so they can be made louder + bool scc_accessed; + + enum { scc_enabled_true = 0xC000 }; + unsigned scc_enabled; // 0 or 0xC000 + int ay_latch; + + struct { + Sms_Apu* psg; + Opl_Apu* fm; + } sms; + + struct { + Ay_Apu* psg; + Scc_Apu* scc; + Opl_Apu* music; + Opl_Apu* audio; + } msx; + + Core( Kss_Emu* e ) : emu( *e ) { } + + virtual void cpu_write( addr_t, int ); + virtual int cpu_in( time_t, addr_t ); + virtual void cpu_out( time_t, addr_t, int ); + virtual void update_gain(); + + void cpu_write_( addr_t addr, int data ); + void update_gain_(); + void unload(); + } core; +}; + +#endif diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.cpp b/Frameworks/GME/gme/Kss_Scc_Apu.cpp index 60c53293e..77c7a3e17 100644 --- a/Frameworks/GME/gme/Kss_Scc_Apu.cpp +++ b/Frameworks/GME/gme/Kss_Scc_Apu.cpp @@ -1,124 +1,124 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Kss_Scc_Apu.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// Tones above this frequency are treated as disabled tone at half volume. -// Power of two is more efficient (avoids division). -int const inaudible_freq = 16384; - -int const wave_size = 0x20; - -void Scc_Apu::set_output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; ++i ) - set_output( i, buf ); -} - -void Scc_Apu::volume( double v ) -{ - synth.volume( 0.43 / osc_count / amp_range * v ); -} - -void Scc_Apu::reset() -{ - last_time = 0; - - for ( int i = osc_count; --i >= 0; ) - memset( &oscs [i], 0, offsetof (osc_t,output) ); - - memset( regs, 0, sizeof regs ); -} - -Scc_Apu::Scc_Apu() -{ - set_output( NULL ); - volume( 1.0 ); - reset(); -} - -void Scc_Apu::run_until( blip_time_t end_time ) -{ - for ( int index = 0; index < osc_count; index++ ) - { - osc_t& osc = oscs [index]; - - Blip_Buffer* const output = osc.output; - if ( !output ) - continue; - - blip_time_t period = (regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + - regs [0xA0 + index * 2] + 1; - int volume = 0; - if ( regs [0xAF] & (1 << index) ) - { - blip_time_t inaudible_period = (unsigned) (output->clock_rate() + - inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); - if ( period > inaudible_period ) - volume = (regs [0xAA + index] & 0x0F) * (amp_range / 256 / 15); - } - - BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; - /*if ( index == osc_count - 1 ) - wave -= wave_size; // last two oscs share same wave RAM*/ - - { - int delta = wave [osc.phase] * volume - osc.last_amp; - if ( delta ) - { - osc.last_amp += delta; - output->set_modified(); - synth.offset( last_time, delta, output ); - } - } - - blip_time_t time = last_time + osc.delay; - if ( time < end_time ) - { - int phase = osc.phase; - if ( !volume ) - { - // maintain phase - int count = (end_time - time + period - 1) / period; - phase += count; // will be masked below - time += count * period; - } - else - { - int last_wave = wave [phase]; - phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop - do - { - int delta = wave [phase] - last_wave; - phase = (phase + 1) & (wave_size - 1); - if ( delta ) - { - last_wave += delta; - synth.offset_inline( time, delta * volume, output ); - } - time += period; - } - while ( time < end_time ); - - osc.last_amp = last_wave * volume; - output->set_modified(); - phase--; // undo pre-advance - } - osc.phase = phase & (wave_size - 1); - } - osc.delay = time - end_time; - } - last_time = end_time; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Kss_Scc_Apu.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Tones above this frequency are treated as disabled tone at half volume. +// Power of two is more efficient (avoids division). +int const inaudible_freq = 16384; + +int const wave_size = 0x20; + +void Scc_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +void Scc_Apu::volume( double v ) +{ + synth.volume( 0.43 / osc_count / amp_range * v ); +} + +void Scc_Apu::reset() +{ + last_time = 0; + + for ( int i = osc_count; --i >= 0; ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); + + memset( regs, 0, sizeof regs ); +} + +Scc_Apu::Scc_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +void Scc_Apu::run_until( blip_time_t end_time ) +{ + for ( int index = 0; index < osc_count; index++ ) + { + osc_t& osc = oscs [index]; + + Blip_Buffer* const output = osc.output; + if ( !output ) + continue; + + blip_time_t period = (regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + + regs [0xA0 + index * 2] + 1; + int volume = 0; + if ( regs [0xAF] & (1 << index) ) + { + blip_time_t inaudible_period = (unsigned) (output->clock_rate() + + inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); + if ( period > inaudible_period ) + volume = (regs [0xAA + index] & 0x0F) * (amp_range / 256 / 15); + } + + BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; + /*if ( index == osc_count - 1 ) + wave -= wave_size; // last two oscs share same wave RAM*/ + + { + int delta = wave [osc.phase] * volume - osc.last_amp; + if ( delta ) + { + osc.last_amp += delta; + output->set_modified(); + synth.offset( last_time, delta, output ); + } + } + + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + int phase = osc.phase; + if ( !volume ) + { + // maintain phase + int count = (end_time - time + period - 1) / period; + phase += count; // will be masked below + time += count * period; + } + else + { + int last_wave = wave [phase]; + phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop + do + { + int delta = wave [phase] - last_wave; + phase = (phase + 1) & (wave_size - 1); + if ( delta ) + { + last_wave += delta; + synth.offset_inline( time, delta * volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.last_amp = last_wave * volume; + output->set_modified(); + phase--; // undo pre-advance + } + osc.phase = phase & (wave_size - 1); + } + osc.delay = time - end_time; + } + last_time = end_time; +} diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.h b/Frameworks/GME/gme/Kss_Scc_Apu.h index a48ff5396..202f09006 100644 --- a/Frameworks/GME/gme/Kss_Scc_Apu.h +++ b/Frameworks/GME/gme/Kss_Scc_Apu.h @@ -1,111 +1,111 @@ -// Konami SCC sound chip emulator - -// $package -#ifndef KSS_SCC_APU_H -#define KSS_SCC_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Scc_Apu { -public: -// Basics - - // Sets buffer to generate sound into, or 0 to mute. - void set_output( Blip_Buffer* ); - - // Emulates to time t, then writes data to reg - enum { reg_count = 0xB0 }; // 0 <= reg < reg_count - void write( blip_time_t t, int reg, int data ); - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Resets sound chip - void reset(); - - // Same as set_output(), but for a particular channel - enum { osc_count = 5 }; - void set_output( int chan, Blip_Buffer* ); - - // Set overall volume, where 1.0 is normal - void volume( double ); - - // Set treble equalization - void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - -private: - // noncopyable - Scc_Apu( const Scc_Apu& ); - Scc_Apu& operator = ( const Scc_Apu& ); - - -// Implementation -public: - Scc_Apu(); - BLARGG_DISABLE_NOTHROW - -private: - enum { amp_range = 0x8000 }; - struct osc_t - { - int delay; - int phase; - int last_amp; - Blip_Buffer* output; - }; - osc_t oscs [osc_count]; - blip_time_t last_time; - unsigned char regs [reg_count]; - Blip_Synth_Fast synth; - - void run_until( blip_time_t ); -}; - -inline void Scc_Apu::set_output( int index, Blip_Buffer* b ) -{ - assert( (unsigned) index < osc_count ); - oscs [index].output = b; -} - -inline void Scc_Apu::write( blip_time_t time, int addr, int data ) -{ - //assert( (unsigned) addr < reg_count ); - assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) ); - run_until( time ); - - addr -= 0x9800; - if ( ( unsigned ) addr < 0x90 ) - { - if ( ( unsigned ) addr < 0x60 ) - regs [addr] = data; - else if ( ( unsigned ) addr < 0x80 ) - { - regs [addr] = regs[addr + 0x20] = data; - } - else if ( ( unsigned ) addr < 0x90 ) - { - regs [addr + 0x20] = data; - } - } - else - { - addr -= 0xB800 - 0x9800; - if ( ( unsigned ) addr < 0xB0 ) - regs [addr] = data; - } -} - -inline void Scc_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - last_time -= end_time; - assert( last_time >= 0 ); -} - -#endif +// Konami SCC sound chip emulator + +// $package +#ifndef KSS_SCC_APU_H +#define KSS_SCC_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Scc_Apu { +public: +// Basics + + // Sets buffer to generate sound into, or 0 to mute. + void set_output( Blip_Buffer* ); + + // Emulates to time t, then writes data to reg + enum { reg_count = 0xB0 }; // 0 <= reg < reg_count + void write( blip_time_t t, int reg, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip + void reset(); + + // Same as set_output(), but for a particular channel + enum { osc_count = 5 }; + void set_output( int chan, Blip_Buffer* ); + + // Set overall volume, where 1.0 is normal + void volume( double ); + + // Set treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +private: + // noncopyable + Scc_Apu( const Scc_Apu& ); + Scc_Apu& operator = ( const Scc_Apu& ); + + +// Implementation +public: + Scc_Apu(); + BLARGG_DISABLE_NOTHROW + +private: + enum { amp_range = 0x8000 }; + struct osc_t + { + int delay; + int phase; + int last_amp; + Blip_Buffer* output; + }; + osc_t oscs [osc_count]; + blip_time_t last_time; + unsigned char regs [reg_count]; + Blip_Synth_Fast synth; + + void run_until( blip_time_t ); +}; + +inline void Scc_Apu::set_output( int index, Blip_Buffer* b ) +{ + assert( (unsigned) index < osc_count ); + oscs [index].output = b; +} + +inline void Scc_Apu::write( blip_time_t time, int addr, int data ) +{ + //assert( (unsigned) addr < reg_count ); + assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) ); + run_until( time ); + + addr -= 0x9800; + if ( ( unsigned ) addr < 0x90 ) + { + if ( ( unsigned ) addr < 0x60 ) + regs [addr] = data; + else if ( ( unsigned ) addr < 0x80 ) + { + regs [addr] = regs[addr + 0x20] = data; + } + else if ( ( unsigned ) addr < 0x90 ) + { + regs [addr + 0x20] = data; + } + } + else + { + addr -= 0xB800 - 0x9800; + if ( ( unsigned ) addr < 0xB0 ) + regs [addr] = data; + } +} + +inline void Scc_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + last_time -= end_time; + assert( last_time >= 0 ); +} + +#endif diff --git a/Frameworks/GME/gme/Multi_Buffer.h b/Frameworks/GME/gme/Multi_Buffer.h index 677d80ddc..a38445a90 100644 --- a/Frameworks/GME/gme/Multi_Buffer.h +++ b/Frameworks/GME/gme/Multi_Buffer.h @@ -1,219 +1,219 @@ -// Multi-channel sound buffer interface, and basic mono and stereo buffers - -// Blip_Buffer $vers -#ifndef MULTI_BUFFER_H -#define MULTI_BUFFER_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -// Interface to one or more Blip_Buffers mapped to one or more channels -// consisting of left, center, and right buffers. -class Multi_Buffer { -public: - - // 1=mono, 2=stereo - Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } - - // Sets the number of channels available and optionally their types - // (type information used by Effects_Buffer) - enum { type_index_mask = 0xFF }; - enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; - virtual blargg_err_t set_channel_count( int, int const types [] = NULL ); - int channel_count() const { return channel_count_; } - - // Gets indexed channel, from 0 to channel_count()-1 - struct channel_t { - Blip_Buffer* center; - Blip_Buffer* left; - Blip_Buffer* right; - }; - virtual channel_t channel( int index ) BLARGG_PURE( ; ) - - // Number of samples per output frame (1 = mono, 2 = stereo) - int samples_per_frame() const; - - // Count of changes to channel configuration. Incremented whenever - // a change is made to any of the Blip_Buffers for any channel. - unsigned channels_changed_count() { return channels_changed_count_; } - - // See Blip_Buffer.h - virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ) BLARGG_PURE( ; ) - int sample_rate() const; - int length() const; - virtual void clock_rate( int ) BLARGG_PURE( ; ) - virtual void bass_freq( int ) BLARGG_PURE( ; ) - virtual void clear() BLARGG_PURE( ; ) - virtual void end_frame( blip_time_t ) BLARGG_PURE( ; ) - virtual int read_samples( blip_sample_t [], int ) BLARGG_PURE( ; ) - virtual int samples_avail() const BLARGG_PURE( ; ) - -private: - // noncopyable - Multi_Buffer( const Multi_Buffer& ); - Multi_Buffer& operator = ( const Multi_Buffer& ); - -// Implementation -public: - BLARGG_DISABLE_NOTHROW - void disable_immediate_removal() { immediate_removal_ = false; } - -protected: - bool immediate_removal() const { return immediate_removal_; } - int const* channel_types() const { return channel_types_; } - void channels_changed() { channels_changed_count_++; } - -private: - unsigned channels_changed_count_; - int sample_rate_; - int length_; - int channel_count_; - int const samples_per_frame_; - int const* channel_types_; - bool immediate_removal_; -}; - - -// Uses a single buffer and outputs mono samples. -class Mono_Buffer : public Multi_Buffer { -public: - // Buffer used for all channels - Blip_Buffer* center() { return &buf; } - -// Implementation -public: - Mono_Buffer(); - ~Mono_Buffer(); - virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); - virtual void clock_rate( int rate ) { buf.clock_rate( rate ); } - virtual void bass_freq( int freq ) { buf.bass_freq( freq ); } - virtual void clear() { buf.clear(); } - virtual int samples_avail() const { return buf.samples_avail(); } - virtual int read_samples( blip_sample_t p [], int s ) { return buf.read_samples( p, s ); } - virtual channel_t channel( int ) { return chan; } - virtual void end_frame( blip_time_t t ) { buf.end_frame( t ); } - -private: - Blip_Buffer buf; - channel_t chan; -}; - - class Tracked_Blip_Buffer : public Blip_Buffer { - public: - // Non-zero if buffer still has non-silent samples in it. Requires that you call - // set_modified() appropriately. - unsigned non_silent() const; - - // remove_samples( samples_avail() ) - void remove_all_samples(); - - // Implementation - public: - BLARGG_DISABLE_NOTHROW - int read_samples( blip_sample_t [], int ); - void remove_silence( int ); - void remove_samples( int ); - Tracked_Blip_Buffer(); - void clear(); - void end_frame( blip_time_t ); - - private: - int last_non_silence; - - delta_t unsettled() const { return integrator() >> delta_bits; } - void remove_( int ); - }; - - class Stereo_Mixer { - public: - Tracked_Blip_Buffer* bufs [3]; - int samples_read; - - Stereo_Mixer() : samples_read( 0 ) { } - void read_pairs( blip_sample_t out [], int count ); - - private: - void mix_mono ( blip_sample_t out [], int pair_count ); - void mix_stereo( blip_sample_t out [], int pair_count ); - }; - - -// Uses three buffers (one for center) and outputs stereo sample pairs. -class Stereo_Buffer : public Multi_Buffer { -public: - - // Buffers used for all channels - Blip_Buffer* center() { return &bufs [2]; } - Blip_Buffer* left() { return &bufs [0]; } - Blip_Buffer* right() { return &bufs [1]; } - -// Implementation -public: - Stereo_Buffer(); - ~Stereo_Buffer(); - virtual blargg_err_t set_sample_rate( int, int msec = blip_default_length ); - virtual void clock_rate( int ); - virtual void bass_freq( int ); - virtual void clear(); - virtual channel_t channel( int ) { return chan; } - virtual void end_frame( blip_time_t ); - virtual int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } - virtual int read_samples( blip_sample_t [], int ); - -private: - enum { bufs_size = 3 }; - typedef Tracked_Blip_Buffer buf_t; - buf_t bufs [bufs_size]; - Stereo_Mixer mixer; - channel_t chan; - int samples_avail_; -}; - - -// Silent_Buffer generates no samples, useful where no sound is wanted -class Silent_Buffer : public Multi_Buffer { - channel_t chan; -public: - Silent_Buffer(); - virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); - virtual void clock_rate( int ) { } - virtual void bass_freq( int ) { } - virtual void clear() { } - virtual channel_t channel( int ) { return chan; } - virtual void end_frame( blip_time_t ) { } - virtual int samples_avail() const { return 0; } - virtual int read_samples( blip_sample_t [], int ) { return 0; } -}; - - -inline blargg_err_t Multi_Buffer::set_sample_rate( int rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return blargg_ok; -} - -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } -inline int Multi_Buffer::sample_rate() const { return sample_rate_; } -inline int Multi_Buffer::length() const { return length_; } -inline void Multi_Buffer::clock_rate( int ) { } -inline void Multi_Buffer::bass_freq( int ) { } -inline void Multi_Buffer::clear() { } -inline void Multi_Buffer::end_frame( blip_time_t ) { } -inline int Multi_Buffer::read_samples( blip_sample_t [], int ) { return 0; } -inline int Multi_Buffer::samples_avail() const { return 0; } - -inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const types [] ) -{ - channel_count_ = n; - channel_types_ = types; - return blargg_ok; -} - -inline blargg_err_t Silent_Buffer::set_sample_rate( int rate, int msec ) -{ - return Multi_Buffer::set_sample_rate( rate, msec ); -} - -#endif +// Multi-channel sound buffer interface, and basic mono and stereo buffers + +// Blip_Buffer $vers +#ifndef MULTI_BUFFER_H +#define MULTI_BUFFER_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +// Interface to one or more Blip_Buffers mapped to one or more channels +// consisting of left, center, and right buffers. +class Multi_Buffer { +public: + + // 1=mono, 2=stereo + Multi_Buffer( int samples_per_frame ); + virtual ~Multi_Buffer() { } + + // Sets the number of channels available and optionally their types + // (type information used by Effects_Buffer) + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual blargg_err_t set_channel_count( int, int const types [] = NULL ); + int channel_count() const { return channel_count_; } + + // Gets indexed channel, from 0 to channel_count()-1 + struct channel_t { + Blip_Buffer* center; + Blip_Buffer* left; + Blip_Buffer* right; + }; + virtual channel_t channel( int index ) BLARGG_PURE( ; ) + + // Number of samples per output frame (1 = mono, 2 = stereo) + int samples_per_frame() const; + + // Count of changes to channel configuration. Incremented whenever + // a change is made to any of the Blip_Buffers for any channel. + unsigned channels_changed_count() { return channels_changed_count_; } + + // See Blip_Buffer.h + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ) BLARGG_PURE( ; ) + int sample_rate() const; + int length() const; + virtual void clock_rate( int ) BLARGG_PURE( ; ) + virtual void bass_freq( int ) BLARGG_PURE( ; ) + virtual void clear() BLARGG_PURE( ; ) + virtual void end_frame( blip_time_t ) BLARGG_PURE( ; ) + virtual int read_samples( blip_sample_t [], int ) BLARGG_PURE( ; ) + virtual int samples_avail() const BLARGG_PURE( ; ) + +private: + // noncopyable + Multi_Buffer( const Multi_Buffer& ); + Multi_Buffer& operator = ( const Multi_Buffer& ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + void disable_immediate_removal() { immediate_removal_ = false; } + +protected: + bool immediate_removal() const { return immediate_removal_; } + int const* channel_types() const { return channel_types_; } + void channels_changed() { channels_changed_count_++; } + +private: + unsigned channels_changed_count_; + int sample_rate_; + int length_; + int channel_count_; + int const samples_per_frame_; + int const* channel_types_; + bool immediate_removal_; +}; + + +// Uses a single buffer and outputs mono samples. +class Mono_Buffer : public Multi_Buffer { +public: + // Buffer used for all channels + Blip_Buffer* center() { return &buf; } + +// Implementation +public: + Mono_Buffer(); + ~Mono_Buffer(); + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); + virtual void clock_rate( int rate ) { buf.clock_rate( rate ); } + virtual void bass_freq( int freq ) { buf.bass_freq( freq ); } + virtual void clear() { buf.clear(); } + virtual int samples_avail() const { return buf.samples_avail(); } + virtual int read_samples( blip_sample_t p [], int s ) { return buf.read_samples( p, s ); } + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t t ) { buf.end_frame( t ); } + +private: + Blip_Buffer buf; + channel_t chan; +}; + + class Tracked_Blip_Buffer : public Blip_Buffer { + public: + // Non-zero if buffer still has non-silent samples in it. Requires that you call + // set_modified() appropriately. + unsigned non_silent() const; + + // remove_samples( samples_avail() ) + void remove_all_samples(); + + // Implementation + public: + BLARGG_DISABLE_NOTHROW + int read_samples( blip_sample_t [], int ); + void remove_silence( int ); + void remove_samples( int ); + Tracked_Blip_Buffer(); + void clear(); + void end_frame( blip_time_t ); + + private: + int last_non_silence; + + delta_t unsettled() const { return integrator() >> delta_bits; } + void remove_( int ); + }; + + class Stereo_Mixer { + public: + Tracked_Blip_Buffer* bufs [3]; + int samples_read; + + Stereo_Mixer() : samples_read( 0 ) { } + void read_pairs( blip_sample_t out [], int count ); + + private: + void mix_mono ( blip_sample_t out [], int pair_count ); + void mix_stereo( blip_sample_t out [], int pair_count ); + }; + + +// Uses three buffers (one for center) and outputs stereo sample pairs. +class Stereo_Buffer : public Multi_Buffer { +public: + + // Buffers used for all channels + Blip_Buffer* center() { return &bufs [2]; } + Blip_Buffer* left() { return &bufs [0]; } + Blip_Buffer* right() { return &bufs [1]; } + +// Implementation +public: + Stereo_Buffer(); + ~Stereo_Buffer(); + virtual blargg_err_t set_sample_rate( int, int msec = blip_default_length ); + virtual void clock_rate( int ); + virtual void bass_freq( int ); + virtual void clear(); + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t ); + virtual int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + virtual int read_samples( blip_sample_t [], int ); + +private: + enum { bufs_size = 3 }; + typedef Tracked_Blip_Buffer buf_t; + buf_t bufs [bufs_size]; + Stereo_Mixer mixer; + channel_t chan; + int samples_avail_; +}; + + +// Silent_Buffer generates no samples, useful where no sound is wanted +class Silent_Buffer : public Multi_Buffer { + channel_t chan; +public: + Silent_Buffer(); + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); + virtual void clock_rate( int ) { } + virtual void bass_freq( int ) { } + virtual void clear() { } + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t ) { } + virtual int samples_avail() const { return 0; } + virtual int read_samples( blip_sample_t [], int ) { return 0; } +}; + + +inline blargg_err_t Multi_Buffer::set_sample_rate( int rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return blargg_ok; +} + +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } +inline int Multi_Buffer::sample_rate() const { return sample_rate_; } +inline int Multi_Buffer::length() const { return length_; } +inline void Multi_Buffer::clock_rate( int ) { } +inline void Multi_Buffer::bass_freq( int ) { } +inline void Multi_Buffer::clear() { } +inline void Multi_Buffer::end_frame( blip_time_t ) { } +inline int Multi_Buffer::read_samples( blip_sample_t [], int ) { return 0; } +inline int Multi_Buffer::samples_avail() const { return 0; } + +inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const types [] ) +{ + channel_count_ = n; + channel_types_ = types; + return blargg_ok; +} + +inline blargg_err_t Silent_Buffer::set_sample_rate( int rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +#endif diff --git a/Frameworks/GME/gme/Music_Emu.cpp b/Frameworks/GME/gme/Music_Emu.cpp index 3f13b462e..df937971e 100644 --- a/Frameworks/GME/gme/Music_Emu.cpp +++ b/Frameworks/GME/gme/Music_Emu.cpp @@ -220,7 +220,7 @@ void Music_Emu::set_fade( int start_msec, int length_msec ) fade_set = true; this->length_msec = start_msec; this->fade_msec = length_msec; - track_filter.set_fade( msec_to_samples( start_msec ), + track_filter.set_fade( start_msec < 0 ? Track_Filter::indefinite_count : msec_to_samples( start_msec ), length_msec * sample_rate() / (1000 / stereo) ); } diff --git a/Frameworks/GME/gme/Nes_Apu.cpp b/Frameworks/GME/gme/Nes_Apu.cpp index 1862846ea..1cfe38881 100644 --- a/Frameworks/GME/gme/Nes_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Apu.cpp @@ -1,394 +1,394 @@ -// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const amp_range = 15; - -Nes_Apu::Nes_Apu() : - square1( &square_synth ), - square2( &square_synth ) -{ - tempo_ = 1.0; - dmc.apu = this; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = ▵ - oscs [3] = &noise; - oscs [4] = &dmc; - - set_output( NULL ); - dmc.nonlinear = false; - volume( 1.0 ); - reset( false ); -} - -void Nes_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth .treble_eq( eq ); - triangle.synth.treble_eq( eq ); - noise .synth.treble_eq( eq ); - dmc .synth.treble_eq( eq ); -} - -void Nes_Apu::enable_nonlinear_( double sq, double tnd ) -{ - dmc.nonlinear = true; - square_synth.volume( sq ); - - triangle.synth.volume( tnd * 2.752 ); - noise .synth.volume( tnd * 1.849 ); - dmc .synth.volume( tnd ); - - square1 .last_amp = 0; - square2 .last_amp = 0; - triangle.last_amp = 0; - noise .last_amp = 0; - dmc .last_amp = 0; -} - -void Nes_Apu::volume( double v ) -{ - if ( !dmc.nonlinear ) - { - v *= 1.0 / 1.11; // TODO: merge into values below - square_synth .volume( 0.125 / amp_range * v ); // was 0.1128 1.108 - triangle.synth.volume( 0.150 / amp_range * v ); // was 0.12765 1.175 - noise .synth.volume( 0.095 / amp_range * v ); // was 0.0741 1.282 - dmc .synth.volume( 0.450 / 2048 * v ); // was 0.42545 1.058 - } -} - -void Nes_Apu::set_output( Blip_Buffer* buffer ) -{ - for ( int i = 0; i < osc_count; ++i ) - set_output( i, buffer ); -} - -void Nes_Apu::set_tempo( double t ) -{ - tempo_ = t; - frame_period = (dmc.pal_mode ? 8314 : 7458); - if ( t != 1.0 ) - frame_period = (int) (frame_period / t) & ~1; // must be even -} - -void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) -{ - dmc.pal_mode = pal_mode; - set_tempo( tempo_ ); - - square1.reset(); - square2.reset(); - triangle.reset(); - noise.reset(); - dmc.reset(); - - last_time = 0; - last_dmc_time = 0; - osc_enables = 0; - irq_flag = false; - enable_w4011 = true; - earliest_irq_ = no_irq; - frame_delay = 1; - write_register( 0, 0x4017, 0x00 ); - write_register( 0, 0x4015, 0x00 ); - - for ( int addr = io_addr; addr <= 0x4013; addr++ ) - write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); - - dmc.dac = initial_dmc_dac; - if ( !dmc.nonlinear ) - triangle.last_amp = 15; - if ( !dmc.nonlinear ) // TODO: remove? - dmc.last_amp = initial_dmc_dac; // prevent output transition -} - -void Nes_Apu::irq_changed() -{ - blip_time_t new_irq = dmc.next_irq; - if ( dmc.irq_flag | irq_flag ) { - new_irq = 0; - } - else if ( new_irq > next_irq ) { - new_irq = next_irq; - } - - if ( new_irq != earliest_irq_ ) { - earliest_irq_ = new_irq; - if ( irq_notifier.f ) - irq_notifier.f( irq_notifier.data ); - } -} - -// frames - -void Nes_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_dmc_time ); - if ( end_time > next_dmc_read_time() ) - { - blip_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } -} - -void Nes_Apu::run_until_( blip_time_t end_time ) -{ - require( end_time >= last_time ); - - if ( end_time == last_time ) - return; - - if ( last_dmc_time < end_time ) - { - blip_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } - - while ( true ) - { - // earlier of next frame time or end time - blip_time_t time = last_time + frame_delay; - if ( time > end_time ) - time = end_time; - frame_delay -= time - last_time; - - // run oscs to present - square1.run( last_time, time ); - square2.run( last_time, time ); - triangle.run( last_time, time ); - noise.run( last_time, time ); - last_time = time; - - if ( time == end_time ) - break; // no more frames to run - - // take frame-specific actions - frame_delay = frame_period; - switch ( frame++ ) - { - case 0: - if ( !(frame_mode & 0xC0) ) { - next_irq = time + frame_period * 4 + 2; - irq_flag = true; - } - // fall through - case 2: - // clock length and sweep on frames 0 and 2 - square1.clock_length( 0x20 ); - square2.clock_length( 0x20 ); - noise.clock_length( 0x20 ); - triangle.clock_length( 0x80 ); // different bit for halt flag on triangle - - square1.clock_sweep( -1 ); - square2.clock_sweep( 0 ); - - // frame 2 is slightly shorter in mode 1 - if ( dmc.pal_mode && frame == 3 ) - frame_delay -= 2; - break; - - case 1: - // frame 1 is slightly shorter in mode 0 - if ( !dmc.pal_mode ) - frame_delay -= 2; - break; - - case 3: - frame = 0; - - // frame 3 is almost twice as long in mode 1 - if ( frame_mode & 0x80 ) - frame_delay += frame_period - (dmc.pal_mode ? 2 : 6); - break; - } - - // clock envelopes and linear counter every frame - triangle.clock_linear_counter(); - square1.clock_envelope(); - square2.clock_envelope(); - noise.clock_envelope(); - } -} - -template -inline void zero_apu_osc( T* osc, blip_time_t time ) -{ - Blip_Buffer* output = osc->output; - int last_amp = osc->last_amp; - osc->last_amp = 0; - if ( output && last_amp ) - osc->synth.offset( time, -last_amp, output ); -} - -void Nes_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until_( end_time ); - - if ( dmc.nonlinear ) - { - zero_apu_osc( &square1, last_time ); - zero_apu_osc( &square2, last_time ); - zero_apu_osc( &triangle, last_time ); - zero_apu_osc( &noise, last_time ); - zero_apu_osc( &dmc, last_time ); - } - - // make times relative to new frame - last_time -= end_time; - require( last_time >= 0 ); - - last_dmc_time -= end_time; - require( last_dmc_time >= 0 ); - - if ( next_irq != no_irq ) { - next_irq -= end_time; - check( next_irq >= 0 ); - } - if ( dmc.next_irq != no_irq ) { - dmc.next_irq -= end_time; - check( dmc.next_irq >= 0 ); - } - if ( earliest_irq_ != no_irq ) { - earliest_irq_ -= end_time; - if ( earliest_irq_ < 0 ) - earliest_irq_ = 0; - } -} - -// registers - -static const unsigned char length_table [0x20] = { - 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, - 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, - 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, - 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E -}; - -void Nes_Apu::write_register( blip_time_t time, int addr, int data ) -{ - require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) - require( (unsigned) data <= 0xFF ); - - // Ignore addresses outside range - if ( unsigned (addr - io_addr) >= io_size ) - return; - - run_until_( time ); - - if ( addr < 0x4014 ) - { - // Write to channel - int osc_index = (addr - io_addr) >> 2; - Nes_Osc* osc = oscs [osc_index]; - - int reg = addr & 3; - osc->regs [reg] = data; - osc->reg_written [reg] = true; - - if ( osc_index == 4 ) - { - // handle DMC specially - if ( enable_w4011 || reg != 1 ) - dmc.write_register( reg, data ); - } - else if ( reg == 3 ) - { - // load length counter - if ( (osc_enables >> osc_index) & 1 ) - osc->length_counter = length_table [(data >> 3) & 0x1F]; - - // reset square phase - if ( osc_index < 2 ) - ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; - } - } - else if ( addr == 0x4015 ) - { - // Channel enables - for ( int i = osc_count; i--; ) - if ( !((data >> i) & 1) ) - oscs [i]->length_counter = 0; - - bool recalc_irq = dmc.irq_flag; - dmc.irq_flag = false; - - int old_enables = osc_enables; - osc_enables = data; - if ( !(data & 0x10) ) { - dmc.next_irq = no_irq; - recalc_irq = true; - } - else if ( !(old_enables & 0x10) ) { - dmc.start(); // dmc just enabled - } - - if ( recalc_irq ) - irq_changed(); - } - else if ( addr == 0x4017 ) - { - // Frame mode - frame_mode = data; - - bool irq_enabled = !(data & 0x40); - irq_flag &= irq_enabled; - next_irq = no_irq; - - // mode 1 - frame_delay = (frame_delay & 1); - frame = 0; - - if ( !(data & 0x80) ) - { - // mode 0 - frame = 1; - frame_delay += frame_period; - if ( irq_enabled ) - next_irq = time + frame_delay + frame_period * 3 + 1; - } - - irq_changed(); - } -} - -int Nes_Apu::read_status( blip_time_t time ) -{ - run_until_( time - 1 ); - - int result = (dmc.irq_flag << 7) | (irq_flag << 6); - - for ( int i = 0; i < osc_count; i++ ) - if ( oscs [i]->length_counter ) - result |= 1 << i; - - run_until_( time ); - - if ( irq_flag ) - { - result |= 0x40; - irq_flag = false; - irq_changed(); - } - - //dprintf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result ); - - return result; -} +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Nes_Apu.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const amp_range = 15; + +Nes_Apu::Nes_Apu() : + square1( &square_synth ), + square2( &square_synth ) +{ + tempo_ = 1.0; + dmc.apu = this; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = ▵ + oscs [3] = &noise; + oscs [4] = &dmc; + + set_output( NULL ); + dmc.nonlinear = false; + volume( 1.0 ); + reset( false ); +} + +void Nes_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth .treble_eq( eq ); + triangle.synth.treble_eq( eq ); + noise .synth.treble_eq( eq ); + dmc .synth.treble_eq( eq ); +} + +void Nes_Apu::enable_nonlinear_( double sq, double tnd ) +{ + dmc.nonlinear = true; + square_synth.volume( sq ); + + triangle.synth.volume( tnd * 2.752 ); + noise .synth.volume( tnd * 1.849 ); + dmc .synth.volume( tnd ); + + square1 .last_amp = 0; + square2 .last_amp = 0; + triangle.last_amp = 0; + noise .last_amp = 0; + dmc .last_amp = 0; +} + +void Nes_Apu::volume( double v ) +{ + if ( !dmc.nonlinear ) + { + v *= 1.0 / 1.11; // TODO: merge into values below + square_synth .volume( 0.125 / amp_range * v ); // was 0.1128 1.108 + triangle.synth.volume( 0.150 / amp_range * v ); // was 0.12765 1.175 + noise .synth.volume( 0.095 / amp_range * v ); // was 0.0741 1.282 + dmc .synth.volume( 0.450 / 2048 * v ); // was 0.42545 1.058 + } +} + +void Nes_Apu::set_output( Blip_Buffer* buffer ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buffer ); +} + +void Nes_Apu::set_tempo( double t ) +{ + tempo_ = t; + frame_period = (dmc.pal_mode ? 8314 : 7458); + if ( t != 1.0 ) + frame_period = (int) (frame_period / t) & ~1; // must be even +} + +void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) +{ + dmc.pal_mode = pal_mode; + set_tempo( tempo_ ); + + square1.reset(); + square2.reset(); + triangle.reset(); + noise.reset(); + dmc.reset(); + + last_time = 0; + last_dmc_time = 0; + osc_enables = 0; + irq_flag = false; + enable_w4011 = true; + earliest_irq_ = no_irq; + frame_delay = 1; + write_register( 0, 0x4017, 0x00 ); + write_register( 0, 0x4015, 0x00 ); + + for ( int addr = io_addr; addr <= 0x4013; addr++ ) + write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); + + dmc.dac = initial_dmc_dac; + if ( !dmc.nonlinear ) + triangle.last_amp = 15; + if ( !dmc.nonlinear ) // TODO: remove? + dmc.last_amp = initial_dmc_dac; // prevent output transition +} + +void Nes_Apu::irq_changed() +{ + blip_time_t new_irq = dmc.next_irq; + if ( dmc.irq_flag | irq_flag ) { + new_irq = 0; + } + else if ( new_irq > next_irq ) { + new_irq = next_irq; + } + + if ( new_irq != earliest_irq_ ) { + earliest_irq_ = new_irq; + if ( irq_notifier.f ) + irq_notifier.f( irq_notifier.data ); + } +} + +// frames + +void Nes_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_dmc_time ); + if ( end_time > next_dmc_read_time() ) + { + blip_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } +} + +void Nes_Apu::run_until_( blip_time_t end_time ) +{ + require( end_time >= last_time ); + + if ( end_time == last_time ) + return; + + if ( last_dmc_time < end_time ) + { + blip_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } + + while ( true ) + { + // earlier of next frame time or end time + blip_time_t time = last_time + frame_delay; + if ( time > end_time ) + time = end_time; + frame_delay -= time - last_time; + + // run oscs to present + square1.run( last_time, time ); + square2.run( last_time, time ); + triangle.run( last_time, time ); + noise.run( last_time, time ); + last_time = time; + + if ( time == end_time ) + break; // no more frames to run + + // take frame-specific actions + frame_delay = frame_period; + switch ( frame++ ) + { + case 0: + if ( !(frame_mode & 0xC0) ) { + next_irq = time + frame_period * 4 + 2; + irq_flag = true; + } + // fall through + case 2: + // clock length and sweep on frames 0 and 2 + square1.clock_length( 0x20 ); + square2.clock_length( 0x20 ); + noise.clock_length( 0x20 ); + triangle.clock_length( 0x80 ); // different bit for halt flag on triangle + + square1.clock_sweep( -1 ); + square2.clock_sweep( 0 ); + + // frame 2 is slightly shorter in mode 1 + if ( dmc.pal_mode && frame == 3 ) + frame_delay -= 2; + break; + + case 1: + // frame 1 is slightly shorter in mode 0 + if ( !dmc.pal_mode ) + frame_delay -= 2; + break; + + case 3: + frame = 0; + + // frame 3 is almost twice as long in mode 1 + if ( frame_mode & 0x80 ) + frame_delay += frame_period - (dmc.pal_mode ? 2 : 6); + break; + } + + // clock envelopes and linear counter every frame + triangle.clock_linear_counter(); + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } +} + +template +inline void zero_apu_osc( T* osc, blip_time_t time ) +{ + Blip_Buffer* output = osc->output; + int last_amp = osc->last_amp; + osc->last_amp = 0; + if ( output && last_amp ) + osc->synth.offset( time, -last_amp, output ); +} + +void Nes_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until_( end_time ); + + if ( dmc.nonlinear ) + { + zero_apu_osc( &square1, last_time ); + zero_apu_osc( &square2, last_time ); + zero_apu_osc( &triangle, last_time ); + zero_apu_osc( &noise, last_time ); + zero_apu_osc( &dmc, last_time ); + } + + // make times relative to new frame + last_time -= end_time; + require( last_time >= 0 ); + + last_dmc_time -= end_time; + require( last_dmc_time >= 0 ); + + if ( next_irq != no_irq ) { + next_irq -= end_time; + check( next_irq >= 0 ); + } + if ( dmc.next_irq != no_irq ) { + dmc.next_irq -= end_time; + check( dmc.next_irq >= 0 ); + } + if ( earliest_irq_ != no_irq ) { + earliest_irq_ -= end_time; + if ( earliest_irq_ < 0 ) + earliest_irq_ = 0; + } +} + +// registers + +static const unsigned char length_table [0x20] = { + 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, + 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, + 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, + 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E +}; + +void Nes_Apu::write_register( blip_time_t time, int addr, int data ) +{ + require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) + require( (unsigned) data <= 0xFF ); + + // Ignore addresses outside range + if ( unsigned (addr - io_addr) >= io_size ) + return; + + run_until_( time ); + + if ( addr < 0x4014 ) + { + // Write to channel + int osc_index = (addr - io_addr) >> 2; + Nes_Osc* osc = oscs [osc_index]; + + int reg = addr & 3; + osc->regs [reg] = data; + osc->reg_written [reg] = true; + + if ( osc_index == 4 ) + { + // handle DMC specially + if ( enable_w4011 || reg != 1 ) + dmc.write_register( reg, data ); + } + else if ( reg == 3 ) + { + // load length counter + if ( (osc_enables >> osc_index) & 1 ) + osc->length_counter = length_table [(data >> 3) & 0x1F]; + + // reset square phase + if ( osc_index < 2 ) + ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; + } + } + else if ( addr == 0x4015 ) + { + // Channel enables + for ( int i = osc_count; i--; ) + if ( !((data >> i) & 1) ) + oscs [i]->length_counter = 0; + + bool recalc_irq = dmc.irq_flag; + dmc.irq_flag = false; + + int old_enables = osc_enables; + osc_enables = data; + if ( !(data & 0x10) ) { + dmc.next_irq = no_irq; + recalc_irq = true; + } + else if ( !(old_enables & 0x10) ) { + dmc.start(); // dmc just enabled + } + + if ( recalc_irq ) + irq_changed(); + } + else if ( addr == 0x4017 ) + { + // Frame mode + frame_mode = data; + + bool irq_enabled = !(data & 0x40); + irq_flag &= irq_enabled; + next_irq = no_irq; + + // mode 1 + frame_delay = (frame_delay & 1); + frame = 0; + + if ( !(data & 0x80) ) + { + // mode 0 + frame = 1; + frame_delay += frame_period; + if ( irq_enabled ) + next_irq = time + frame_delay + frame_period * 3 + 1; + } + + irq_changed(); + } +} + +int Nes_Apu::read_status( blip_time_t time ) +{ + run_until_( time - 1 ); + + int result = (dmc.irq_flag << 7) | (irq_flag << 6); + + for ( int i = 0; i < osc_count; i++ ) + if ( oscs [i]->length_counter ) + result |= 1 << i; + + run_until_( time ); + + if ( irq_flag ) + { + result |= 0x40; + irq_flag = false; + irq_changed(); + } + + //dprintf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result ); + + return result; +} diff --git a/Frameworks/GME/gme/Nes_Apu.h b/Frameworks/GME/gme/Nes_Apu.h index ed6374685..cb7676531 100644 --- a/Frameworks/GME/gme/Nes_Apu.h +++ b/Frameworks/GME/gme/Nes_Apu.h @@ -1,184 +1,184 @@ -// NES 2A03 APU sound chip emulator - -// Nes_Snd_Emu $vers -#ifndef NES_APU_H -#define NES_APU_H - -#include "blargg_common.h" -#include "Nes_Oscs.h" - -struct apu_state_t; -class Nes_Buffer; - -class Nes_Apu { -public: -// Basics - - typedef int nes_time_t; // NES CPU clock cycle count - - // Sets memory reader callback used by DMC oscillator to fetch samples. - // When callback is invoked, 'user_data' is passed unchanged as the - // first parameter. - //void dmc_reader( int (*callback)( void* user_data, int addr ), void* user_data = NULL ); - - // Sets buffer to generate sound into, or 0 to mute output (reduces - // emulation accuracy). - void set_output( Blip_Buffer* ); - - // All time values are the number of CPU clock cycles relative to the - // beginning of the current time frame. Before resetting the CPU clock - // count, call end_frame( last_cpu_time ). - - // Writes to register (0x4000-0x4013, and 0x4015 and 0x4017) - enum { io_addr = 0x4000 }; - enum { io_size = 0x18 }; - void write_register( nes_time_t, int addr, int data ); - - // Reads from status register (0x4015) - enum { status_addr = 0x4015 }; - int read_status( nes_time_t ); - - // Runs all oscillators up to specified time, ends current time frame, then - // starts a new time frame at time 0. Time frames have no effect on emulation - // and each can be whatever length is convenient. - void end_frame( nes_time_t ); - -// Optional - - // Resets internal frame counter, registers, and all oscillators. - // Uses PAL timing if pal_timing is true, otherwise use NTSC timing. - // Sets the DMC oscillator's initial DAC value to initial_dmc_dac without - // any audible click. - void reset( bool pal_mode = false, int initial_dmc_dac = 0 ); - - // Same as set_output(), but for a particular channel - // 0: Square 1, 1: Square 2, 2: Triangle, 3: Noise, 4: DMC - enum { osc_count = 5 }; - void set_output( int chan, Blip_Buffer* buf ); - - // Adjusts frame period - void set_tempo( double ); - - // Saves/loads exact emulation state - void save_state( apu_state_t* out ) const; - void load_state( apu_state_t const& ); - - // Sets overall volume (default is 1.0) - void volume( double ); - - // Sets treble equalization (see notes.txt) - void treble_eq( const blip_eq_t& ); - - // Sets IRQ time callback that is invoked when the time of earliest IRQ - // may have changed, or NULL to disable. When callback is invoked, - // 'user_data' is passed unchanged as the first parameter. - //void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - - // Gets time that APU-generated IRQ will occur if no further register reads - // or writes occur. If IRQ is already pending, returns irq_waiting. If no - // IRQ will occur, returns no_irq. - enum { no_irq = INT_MAX/2 + 1 }; - enum { irq_waiting = 0 }; - nes_time_t earliest_irq( nes_time_t ) const; - - // Counts number of DMC reads that would occur if 'run_until( t )' were executed. - // If last_read is not NULL, set *last_read to the earliest time that - // 'count_dmc_reads( time )' would result in the same result. - int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; - - // Time when next DMC memory read will occur - nes_time_t next_dmc_read_time() const; - - // Runs DMC until specified time, so that any DMC memory reads can be - // accounted for (i.e. inserting CPU wait states). - void run_until( nes_time_t ); - - -// Implementation -public: - Nes_Apu(); - BLARGG_DISABLE_NOTHROW - // Use set_output() in place of these - BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) - BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ); ) - - BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4000 }; ) - BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4017 }; ) - - blargg_callback dmc_reader; - blargg_callback irq_notifier; - - void enable_nonlinear_( double sq, double tnd ); - static float tnd_total_() { return 196.015f; } - - void enable_w4011_( bool enable = true ) { enable_w4011 = enable; } - -private: - friend struct Nes_Dmc; - - // noncopyable - Nes_Apu( const Nes_Apu& ); - Nes_Apu& operator = ( const Nes_Apu& ); - - Nes_Osc* oscs [osc_count]; - Nes_Square square1; - Nes_Square square2; - Nes_Noise noise; - Nes_Triangle triangle; - Nes_Dmc dmc; - - double tempo_; - nes_time_t last_time; // has been run until this time in current frame - nes_time_t last_dmc_time; - nes_time_t earliest_irq_; - nes_time_t next_irq; - int frame_period; - int frame_delay; // cycles until frame counter runs next - int frame; // current frame (0-3) - int osc_enables; - int frame_mode; - bool irq_flag; - bool enable_w4011; - Nes_Square::Synth square_synth; // shared by squares - - void irq_changed(); - void state_restored(); - void run_until_( nes_time_t ); - - // TODO: remove - friend class Nes_Core; -}; - -inline void Nes_Apu::set_output( int osc, Blip_Buffer* buf ) -{ - assert( (unsigned) osc < osc_count ); - oscs [osc]->output = buf; -} - -inline Nes_Apu::nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const -{ - return earliest_irq_; -} - -inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const -{ - return dmc.count_reads( time, last_read ); -} - -inline Nes_Apu::nes_time_t Nes_Dmc::next_read_time() const -{ - if ( length_counter == 0 ) - return Nes_Apu::no_irq; // not reading - - return apu->last_dmc_time + delay + (bits_remain - 1) * period; -} - -inline Nes_Apu::nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } - -BLARGG_DEPRECATED( typedef int nes_time_t; ) // use your own typedef -BLARGG_DEPRECATED( typedef unsigned nes_addr_t; ) // use your own typedef - -BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::output ( Blip_Buffer* c ) { set_output( c ); } ) -BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::osc_output( int i, Blip_Buffer* c ) { set_output( i, c ); } ) - -#endif +// NES 2A03 APU sound chip emulator + +// Nes_Snd_Emu $vers +#ifndef NES_APU_H +#define NES_APU_H + +#include "blargg_common.h" +#include "Nes_Oscs.h" + +struct apu_state_t; +class Nes_Buffer; + +class Nes_Apu { +public: +// Basics + + typedef int nes_time_t; // NES CPU clock cycle count + + // Sets memory reader callback used by DMC oscillator to fetch samples. + // When callback is invoked, 'user_data' is passed unchanged as the + // first parameter. + //void dmc_reader( int (*callback)( void* user_data, int addr ), void* user_data = NULL ); + + // Sets buffer to generate sound into, or 0 to mute output (reduces + // emulation accuracy). + void set_output( Blip_Buffer* ); + + // All time values are the number of CPU clock cycles relative to the + // beginning of the current time frame. Before resetting the CPU clock + // count, call end_frame( last_cpu_time ). + + // Writes to register (0x4000-0x4013, and 0x4015 and 0x4017) + enum { io_addr = 0x4000 }; + enum { io_size = 0x18 }; + void write_register( nes_time_t, int addr, int data ); + + // Reads from status register (0x4015) + enum { status_addr = 0x4015 }; + int read_status( nes_time_t ); + + // Runs all oscillators up to specified time, ends current time frame, then + // starts a new time frame at time 0. Time frames have no effect on emulation + // and each can be whatever length is convenient. + void end_frame( nes_time_t ); + +// Optional + + // Resets internal frame counter, registers, and all oscillators. + // Uses PAL timing if pal_timing is true, otherwise use NTSC timing. + // Sets the DMC oscillator's initial DAC value to initial_dmc_dac without + // any audible click. + void reset( bool pal_mode = false, int initial_dmc_dac = 0 ); + + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Triangle, 3: Noise, 4: DMC + enum { osc_count = 5 }; + void set_output( int chan, Blip_Buffer* buf ); + + // Adjusts frame period + void set_tempo( double ); + + // Saves/loads exact emulation state + void save_state( apu_state_t* out ) const; + void load_state( apu_state_t const& ); + + // Sets overall volume (default is 1.0) + void volume( double ); + + // Sets treble equalization (see notes.txt) + void treble_eq( const blip_eq_t& ); + + // Sets IRQ time callback that is invoked when the time of earliest IRQ + // may have changed, or NULL to disable. When callback is invoked, + // 'user_data' is passed unchanged as the first parameter. + //void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); + + // Gets time that APU-generated IRQ will occur if no further register reads + // or writes occur. If IRQ is already pending, returns irq_waiting. If no + // IRQ will occur, returns no_irq. + enum { no_irq = INT_MAX/2 + 1 }; + enum { irq_waiting = 0 }; + nes_time_t earliest_irq( nes_time_t ) const; + + // Counts number of DMC reads that would occur if 'run_until( t )' were executed. + // If last_read is not NULL, set *last_read to the earliest time that + // 'count_dmc_reads( time )' would result in the same result. + int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; + + // Time when next DMC memory read will occur + nes_time_t next_dmc_read_time() const; + + // Runs DMC until specified time, so that any DMC memory reads can be + // accounted for (i.e. inserting CPU wait states). + void run_until( nes_time_t ); + + +// Implementation +public: + Nes_Apu(); + BLARGG_DISABLE_NOTHROW + // Use set_output() in place of these + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ); ) + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4000 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4017 }; ) + + blargg_callback dmc_reader; + blargg_callback irq_notifier; + + void enable_nonlinear_( double sq, double tnd ); + static float tnd_total_() { return 196.015f; } + + void enable_w4011_( bool enable = true ) { enable_w4011 = enable; } + +private: + friend struct Nes_Dmc; + + // noncopyable + Nes_Apu( const Nes_Apu& ); + Nes_Apu& operator = ( const Nes_Apu& ); + + Nes_Osc* oscs [osc_count]; + Nes_Square square1; + Nes_Square square2; + Nes_Noise noise; + Nes_Triangle triangle; + Nes_Dmc dmc; + + double tempo_; + nes_time_t last_time; // has been run until this time in current frame + nes_time_t last_dmc_time; + nes_time_t earliest_irq_; + nes_time_t next_irq; + int frame_period; + int frame_delay; // cycles until frame counter runs next + int frame; // current frame (0-3) + int osc_enables; + int frame_mode; + bool irq_flag; + bool enable_w4011; + Nes_Square::Synth square_synth; // shared by squares + + void irq_changed(); + void state_restored(); + void run_until_( nes_time_t ); + + // TODO: remove + friend class Nes_Core; +}; + +inline void Nes_Apu::set_output( int osc, Blip_Buffer* buf ) +{ + assert( (unsigned) osc < osc_count ); + oscs [osc]->output = buf; +} + +inline Nes_Apu::nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const +{ + return earliest_irq_; +} + +inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const +{ + return dmc.count_reads( time, last_read ); +} + +inline Nes_Apu::nes_time_t Nes_Dmc::next_read_time() const +{ + if ( length_counter == 0 ) + return Nes_Apu::no_irq; // not reading + + return apu->last_dmc_time + delay + (bits_remain - 1) * period; +} + +inline Nes_Apu::nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } + +BLARGG_DEPRECATED( typedef int nes_time_t; ) // use your own typedef +BLARGG_DEPRECATED( typedef unsigned nes_addr_t; ) // use your own typedef + +BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::output ( Blip_Buffer* c ) { set_output( c ); } ) +BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::osc_output( int i, Blip_Buffer* c ) { set_output( i, c ); } ) + +#endif diff --git a/Frameworks/GME/gme/Nes_Cpu.cpp b/Frameworks/GME/gme/Nes_Cpu.cpp index 96d594177..6cff5a910 100644 --- a/Frameworks/GME/gme/Nes_Cpu.cpp +++ b/Frameworks/GME/gme/Nes_Cpu.cpp @@ -1,62 +1,62 @@ -// $package. http://www.slack.net/~ant/ - -#include "Nes_Cpu.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -inline void Nes_Cpu::set_code_page( int i, void const* p ) -{ - byte const* p2 = STATIC_CAST(byte const*,p) - NES_CPU_OFFSET( i * page_size ); - cpu_state->code_map [i] = p2; - cpu_state_.code_map [i] = p2; -} - -void Nes_Cpu::map_code( addr_t start, int size, void const* data, int mirror_size ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 ); - require( mirror_size % page_size == 0 ); - - for ( int offset = 0; offset < size; offset += page_size ) - set_code_page( NES_CPU_PAGE( start + offset ), - STATIC_CAST(char const*,data) + (offset & ((unsigned) mirror_size - 1)) ); -} - -void Nes_Cpu::reset( void const* unmapped_page ) -{ - check( cpu_state == &cpu_state_ ); - cpu_state = &cpu_state_; - - r.flags = irq_inhibit_mask; - r.sp = 0xFF; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - - cpu_state_.time = 0; - cpu_state_.base = 0; - irq_time_ = future_time; - end_time_ = future_time; - error_count_ = 0; - - set_code_page( page_count, unmapped_page ); - map_code( 0, 0x10000, unmapped_page, page_size ); - - blargg_verify_byte_order(); -} +// $package. http://www.slack.net/~ant/ + +#include "Nes_Cpu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +inline void Nes_Cpu::set_code_page( int i, void const* p ) +{ + byte const* p2 = STATIC_CAST(byte const*,p) - NES_CPU_OFFSET( i * page_size ); + cpu_state->code_map [i] = p2; + cpu_state_.code_map [i] = p2; +} + +void Nes_Cpu::map_code( addr_t start, int size, void const* data, int mirror_size ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + require( mirror_size % page_size == 0 ); + + for ( int offset = 0; offset < size; offset += page_size ) + set_code_page( NES_CPU_PAGE( start + offset ), + STATIC_CAST(char const*,data) + (offset & ((unsigned) mirror_size - 1)) ); +} + +void Nes_Cpu::reset( void const* unmapped_page ) +{ + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; + + r.flags = irq_inhibit_mask; + r.sp = 0xFF; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + + cpu_state_.time = 0; + cpu_state_.base = 0; + irq_time_ = future_time; + end_time_ = future_time; + error_count_ = 0; + + set_code_page( page_count, unmapped_page ); + map_code( 0, 0x10000, unmapped_page, page_size ); + + blargg_verify_byte_order(); +} diff --git a/Frameworks/GME/gme/Nes_Cpu.h b/Frameworks/GME/gme/Nes_Cpu.h index 8fa09f2e3..361789eaa 100644 --- a/Frameworks/GME/gme/Nes_Cpu.h +++ b/Frameworks/GME/gme/Nes_Cpu.h @@ -1,131 +1,131 @@ -// NES CPU emulator - -// $package -#ifndef NES_CPU_H -#define NES_CPU_H - -#include "blargg_common.h" - -class Nes_Cpu { -public: - typedef BOOST::uint8_t byte; - typedef int time_t; - typedef int addr_t; - enum { future_time = INT_MAX/2 + 1 }; - - // Clears registers and maps all pages to unmapped_page - void reset( void const* unmapped_page = NULL ); - - // Maps code memory (memory accessed via the program counter). Start and size - // must be multiple of page_size. If mirror_size is non-zero, the first - // mirror_size bytes are repeated over the range. mirror_size must be a - // multiple of page_size. - enum { page_bits = 11 }; - enum { page_size = 1 << page_bits }; - void map_code( addr_t start, int size, void const* code, int mirror_size = 0 ); - - // Accesses emulated memory as CPU does - byte const* get_code( addr_t ) const; - - // NES 6502 registers. NOT kept updated during emulation. - struct registers_t { - BOOST::uint16_t pc; - byte a; - byte x; - byte y; - byte flags; - byte sp; - }; - registers_t r; - - // Time of beginning of next instruction to be executed - time_t time() const { return cpu_state->time + cpu_state->base; } - void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } - void adjust_time( int delta ) { cpu_state->time += delta; } - - // Clocks past end (negative if before) - int time_past_end() const { return cpu_state->time; } - - // Time of next IRQ - time_t irq_time() const { return irq_time_; } - void set_irq_time( time_t ); - - // Emulation stops once time >= end_time - time_t end_time() const { return end_time_; } - void set_end_time( time_t ); - - // Number of unimplemented instructions encountered and skipped - void clear_error_count() { error_count_ = 0; } - unsigned error_count() const { return error_count_; } - void count_error() { error_count_++; } - - // Unmapped page should be filled with this - enum { halt_opcode = 0x22 }; - - enum { irq_inhibit_mask = 0x04 }; - - // Can read this many bytes past end of a page - enum { cpu_padding = 8 }; - -private: - // noncopyable - Nes_Cpu( const Nes_Cpu& ); - Nes_Cpu& operator = ( const Nes_Cpu& ); - - -// Implementation -public: - Nes_Cpu() { cpu_state = &cpu_state_; } - enum { page_count = 0x10000 >> page_bits }; - - struct cpu_state_t { - byte const* code_map [page_count + 1]; - time_t base; - int time; - }; - cpu_state_t* cpu_state; // points to cpu_state_ or a local copy - cpu_state_t cpu_state_; - time_t irq_time_; - time_t end_time_; - unsigned error_count_; - -private: - void set_code_page( int, void const* ); - inline void update_end_time( time_t end, time_t irq ); -}; - -#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits) - -#if BLARGG_NONPORTABLE - #define NES_CPU_OFFSET( addr ) (addr) -#else - #define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1)) -#endif - -inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const -{ - return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr ); -} - -inline void Nes_Cpu::update_end_time( time_t end, time_t irq ) -{ - if ( end > irq && !(r.flags & irq_inhibit_mask) ) - end = irq; - - cpu_state->time += cpu_state->base - end; - cpu_state->base = end; -} - -inline void Nes_Cpu::set_irq_time( time_t t ) -{ - irq_time_ = t; - update_end_time( end_time_, t ); -} - -inline void Nes_Cpu::set_end_time( time_t t ) -{ - end_time_ = t; - update_end_time( t, irq_time_ ); -} - -#endif +// NES CPU emulator + +// $package +#ifndef NES_CPU_H +#define NES_CPU_H + +#include "blargg_common.h" + +class Nes_Cpu { +public: + typedef BOOST::uint8_t byte; + typedef int time_t; + typedef int addr_t; + enum { future_time = INT_MAX/2 + 1 }; + + // Clears registers and maps all pages to unmapped_page + void reset( void const* unmapped_page = NULL ); + + // Maps code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. If mirror_size is non-zero, the first + // mirror_size bytes are repeated over the range. mirror_size must be a + // multiple of page_size. + enum { page_bits = 11 }; + enum { page_size = 1 << page_bits }; + void map_code( addr_t start, int size, void const* code, int mirror_size = 0 ); + + // Accesses emulated memory as CPU does + byte const* get_code( addr_t ) const; + + // NES 6502 registers. NOT kept updated during emulation. + struct registers_t { + BOOST::uint16_t pc; + byte a; + byte x; + byte y; + byte flags; + byte sp; + }; + registers_t r; + + // Time of beginning of next instruction to be executed + time_t time() const { return cpu_state->time + cpu_state->base; } + void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } + void adjust_time( int delta ) { cpu_state->time += delta; } + + // Clocks past end (negative if before) + int time_past_end() const { return cpu_state->time; } + + // Time of next IRQ + time_t irq_time() const { return irq_time_; } + void set_irq_time( time_t ); + + // Emulation stops once time >= end_time + time_t end_time() const { return end_time_; } + void set_end_time( time_t ); + + // Number of unimplemented instructions encountered and skipped + void clear_error_count() { error_count_ = 0; } + unsigned error_count() const { return error_count_; } + void count_error() { error_count_++; } + + // Unmapped page should be filled with this + enum { halt_opcode = 0x22 }; + + enum { irq_inhibit_mask = 0x04 }; + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + +private: + // noncopyable + Nes_Cpu( const Nes_Cpu& ); + Nes_Cpu& operator = ( const Nes_Cpu& ); + + +// Implementation +public: + Nes_Cpu() { cpu_state = &cpu_state_; } + enum { page_count = 0x10000 >> page_bits }; + + struct cpu_state_t { + byte const* code_map [page_count + 1]; + time_t base; + int time; + }; + cpu_state_t* cpu_state; // points to cpu_state_ or a local copy + cpu_state_t cpu_state_; + time_t irq_time_; + time_t end_time_; + unsigned error_count_; + +private: + void set_code_page( int, void const* ); + inline void update_end_time( time_t end, time_t irq ); +}; + +#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define NES_CPU_OFFSET( addr ) (addr) +#else + #define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const +{ + return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr ); +} + +inline void Nes_Cpu::update_end_time( time_t end, time_t irq ) +{ + if ( end > irq && !(r.flags & irq_inhibit_mask) ) + end = irq; + + cpu_state->time += cpu_state->base - end; + cpu_state->base = end; +} + +inline void Nes_Cpu::set_irq_time( time_t t ) +{ + irq_time_ = t; + update_end_time( end_time_, t ); +} + +inline void Nes_Cpu::set_end_time( time_t t ) +{ + end_time_ = t; + update_end_time( t, irq_time_ ); +} + +#endif diff --git a/Frameworks/GME/gme/Nes_Fds_Apu.cpp b/Frameworks/GME/gme/Nes_Fds_Apu.cpp index 69fe2c082..0868ef1c8 100644 --- a/Frameworks/GME/gme/Nes_Fds_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Fds_Apu.cpp @@ -1,280 +1,280 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Nes_Fds_Apu.h" - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const fract_range = 65536; - -void Nes_Fds_Apu::reset() -{ - memset( regs_, 0, sizeof regs_ ); - memset( mod_wave, 0, sizeof mod_wave ); - - last_time = 0; - env_delay = 0; - sweep_delay = 0; - wave_pos = 0; - last_amp = 0; - wave_fract = fract_range; - mod_fract = fract_range; - mod_pos = 0; - mod_write_pos = 0; - - static byte const initial_regs [0x0B] = { - 0x80, // disable envelope - 0, 0, 0xC0, // disable wave and lfo - 0x80, // disable sweep - 0, 0, 0x80, // disable modulation - 0, 0, 0xFF // LFO period // TODO: use 0xE8 as FDS ROM does? - }; - for ( int i = 0; i < (int) sizeof initial_regs; i++ ) - { - // two writes to set both gain and period for envelope registers - write_( io_addr + wave_size + i, 0 ); - write_( io_addr + wave_size + i, initial_regs [i] ); - } -} - -void Nes_Fds_Apu::write_( unsigned addr, int data ) -{ - unsigned reg = addr - io_addr; - if ( reg < io_size ) - { - if ( reg < wave_size ) - { - if ( regs (0x4089) & 0x80 ) - regs_ [reg] = data & wave_sample_max; - } - else - { - regs_ [reg] = data; - switch ( addr ) - { - case 0x4080: - if ( data & 0x80 ) - env_gain = data & 0x3F; - else - env_speed = (data & 0x3F) + 1; - break; - - case 0x4084: - if ( data & 0x80 ) - sweep_gain = data & 0x3F; - else - sweep_speed = (data & 0x3F) + 1; - break; - - case 0x4085: - mod_pos = mod_write_pos; - regs (0x4085) = data & 0x7F; - break; - - case 0x4088: - if ( regs (0x4087) & 0x80 ) - { - int pos = mod_write_pos; - data &= 0x07; - mod_wave [pos ] = data; - mod_wave [pos + 1] = data; - mod_write_pos = (pos + 2) & (wave_size - 1); - mod_pos = (mod_pos + 2) & (wave_size - 1); - } - break; - } - } - } -} - -void Nes_Fds_Apu::set_tempo( double t ) -{ - lfo_tempo = lfo_base_tempo; - if ( t != 1.0 ) - { - lfo_tempo = int ((double) lfo_base_tempo / t + 0.5); - if ( lfo_tempo <= 0 ) - lfo_tempo = 1; - } -} - -void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) -{ - int const wave_freq = (regs (0x4083) & 0x0F) * 0x100 + regs (0x4082); - Blip_Buffer* const output_ = this->output_; - if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) ) - { - output_->set_modified(); - - // master_volume - #define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100 - static unsigned char const master_volumes [4] = { - MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 ) - }; - int const master_volume = master_volumes [regs (0x4089) & 0x03]; - - // lfo_period - blip_time_t lfo_period = regs (0x408A) * lfo_tempo; - if ( regs (0x4083) & 0x40 ) - lfo_period = 0; - - // sweep setup - blip_time_t sweep_time = last_time + sweep_delay; - blip_time_t const sweep_period = lfo_period * sweep_speed; - if ( !sweep_period || regs (0x4084) & 0x80 ) - sweep_time = final_end_time; - - // envelope setup - blip_time_t env_time = last_time + env_delay; - blip_time_t const env_period = lfo_period * env_speed; - if ( !env_period || regs (0x4080) & 0x80 ) - env_time = final_end_time; - - // modulation - int mod_freq = 0; - if ( !(regs (0x4087) & 0x80) ) - mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086); - - blip_time_t end_time = last_time; - do - { - // sweep - if ( sweep_time <= end_time ) - { - sweep_time += sweep_period; - int mode = regs (0x4084) >> 5 & 2; - int new_sweep_gain = sweep_gain + mode - 1; - if ( (unsigned) new_sweep_gain <= (unsigned) 0x80 >> mode ) - sweep_gain = new_sweep_gain; - else - regs (0x4084) |= 0x80; // optimization only - } - - // envelope - if ( env_time <= end_time ) - { - env_time += env_period; - int mode = regs (0x4080) >> 5 & 2; - int new_env_gain = env_gain + mode - 1; - if ( (unsigned) new_env_gain <= (unsigned) 0x80 >> mode ) - env_gain = new_env_gain; - else - regs (0x4080) |= 0x80; // optimization only - } - - // new end_time - blip_time_t const start_time = end_time; - end_time = final_end_time; - if ( end_time > env_time ) end_time = env_time; - if ( end_time > sweep_time ) end_time = sweep_time; - - // frequency modulation - int freq = wave_freq; - if ( mod_freq ) - { - // time of next modulation clock - blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq; - if ( end_time > mod_time ) - end_time = mod_time; - - // run modulator up to next clock and save old sweep_bias - int sweep_bias = regs (0x4085); - mod_fract -= (end_time - start_time) * mod_freq; - if ( mod_fract <= 0 ) - { - mod_fract += fract_range; - check( (unsigned) mod_fract <= fract_range ); - - static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 }; - int mod = mod_wave [mod_pos]; - mod_pos = (mod_pos + 1) & (wave_size - 1); - int new_sweep_bias = (sweep_bias + mod_table [mod]) & 0x7F; - if ( mod == 4 ) - new_sweep_bias = 0; - regs (0x4085) = new_sweep_bias; - } - - // apply frequency modulation - sweep_bias = (sweep_bias ^ 0x40) - 0x40; - int factor = sweep_bias * sweep_gain; - int extra = factor & 0x0F; - factor >>= 4; - if ( extra ) - { - factor--; - if ( sweep_bias >= 0 ) - factor += 3; - } - if ( factor > 193 ) factor -= 258; - if ( factor < -64 ) factor += 256; - freq += (freq * factor) >> 6; - if ( freq <= 0 ) - continue; - } - - // wave - int wave_fract = this->wave_fract; - blip_time_t delay = (wave_fract + freq - 1) / freq; - blip_time_t time = start_time + delay; - - if ( time <= end_time ) - { - // at least one wave clock within start_time...end_time - - blip_time_t const min_delay = fract_range / freq; - int wave_pos = this->wave_pos; - - int volume = env_gain; - if ( volume > vol_max ) - volume = vol_max; - volume *= master_volume; - - int const min_fract = min_delay * freq; - - do - { - // clock wave - int amp = regs_ [wave_pos] * volume; - wave_pos = (wave_pos + 1) & (wave_size - 1); - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth.offset_inline( time, delta, output_ ); - } - - wave_fract += fract_range - delay * freq; - check( unsigned (fract_range - wave_fract) < freq ); - - // delay until next clock - delay = min_delay; - if ( wave_fract > min_fract ) - delay++; - check( delay && delay == (wave_fract + freq - 1) / freq ); - - time += delay; - } - while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong - - this->wave_pos = wave_pos; - } - this->wave_fract = wave_fract - (end_time - (time - delay)) * freq; - check( this->wave_fract > 0 ); - } - while ( end_time < final_end_time ); - - env_delay = env_time - final_end_time; check( env_delay >= 0 ); - sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 ); - } - last_time = final_end_time; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nes_Fds_Apu.h" + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const fract_range = 65536; + +void Nes_Fds_Apu::reset() +{ + memset( regs_, 0, sizeof regs_ ); + memset( mod_wave, 0, sizeof mod_wave ); + + last_time = 0; + env_delay = 0; + sweep_delay = 0; + wave_pos = 0; + last_amp = 0; + wave_fract = fract_range; + mod_fract = fract_range; + mod_pos = 0; + mod_write_pos = 0; + + static byte const initial_regs [0x0B] = { + 0x80, // disable envelope + 0, 0, 0xC0, // disable wave and lfo + 0x80, // disable sweep + 0, 0, 0x80, // disable modulation + 0, 0, 0xFF // LFO period // TODO: use 0xE8 as FDS ROM does? + }; + for ( int i = 0; i < (int) sizeof initial_regs; i++ ) + { + // two writes to set both gain and period for envelope registers + write_( io_addr + wave_size + i, 0 ); + write_( io_addr + wave_size + i, initial_regs [i] ); + } +} + +void Nes_Fds_Apu::write_( unsigned addr, int data ) +{ + unsigned reg = addr - io_addr; + if ( reg < io_size ) + { + if ( reg < wave_size ) + { + if ( regs (0x4089) & 0x80 ) + regs_ [reg] = data & wave_sample_max; + } + else + { + regs_ [reg] = data; + switch ( addr ) + { + case 0x4080: + if ( data & 0x80 ) + env_gain = data & 0x3F; + else + env_speed = (data & 0x3F) + 1; + break; + + case 0x4084: + if ( data & 0x80 ) + sweep_gain = data & 0x3F; + else + sweep_speed = (data & 0x3F) + 1; + break; + + case 0x4085: + mod_pos = mod_write_pos; + regs (0x4085) = data & 0x7F; + break; + + case 0x4088: + if ( regs (0x4087) & 0x80 ) + { + int pos = mod_write_pos; + data &= 0x07; + mod_wave [pos ] = data; + mod_wave [pos + 1] = data; + mod_write_pos = (pos + 2) & (wave_size - 1); + mod_pos = (mod_pos + 2) & (wave_size - 1); + } + break; + } + } + } +} + +void Nes_Fds_Apu::set_tempo( double t ) +{ + lfo_tempo = lfo_base_tempo; + if ( t != 1.0 ) + { + lfo_tempo = int ((double) lfo_base_tempo / t + 0.5); + if ( lfo_tempo <= 0 ) + lfo_tempo = 1; + } +} + +void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) +{ + int const wave_freq = (regs (0x4083) & 0x0F) * 0x100 + regs (0x4082); + Blip_Buffer* const output_ = this->output_; + if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) ) + { + output_->set_modified(); + + // master_volume + #define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100 + static unsigned char const master_volumes [4] = { + MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 ) + }; + int const master_volume = master_volumes [regs (0x4089) & 0x03]; + + // lfo_period + blip_time_t lfo_period = regs (0x408A) * lfo_tempo; + if ( regs (0x4083) & 0x40 ) + lfo_period = 0; + + // sweep setup + blip_time_t sweep_time = last_time + sweep_delay; + blip_time_t const sweep_period = lfo_period * sweep_speed; + if ( !sweep_period || regs (0x4084) & 0x80 ) + sweep_time = final_end_time; + + // envelope setup + blip_time_t env_time = last_time + env_delay; + blip_time_t const env_period = lfo_period * env_speed; + if ( !env_period || regs (0x4080) & 0x80 ) + env_time = final_end_time; + + // modulation + int mod_freq = 0; + if ( !(regs (0x4087) & 0x80) ) + mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086); + + blip_time_t end_time = last_time; + do + { + // sweep + if ( sweep_time <= end_time ) + { + sweep_time += sweep_period; + int mode = regs (0x4084) >> 5 & 2; + int new_sweep_gain = sweep_gain + mode - 1; + if ( (unsigned) new_sweep_gain <= (unsigned) 0x80 >> mode ) + sweep_gain = new_sweep_gain; + else + regs (0x4084) |= 0x80; // optimization only + } + + // envelope + if ( env_time <= end_time ) + { + env_time += env_period; + int mode = regs (0x4080) >> 5 & 2; + int new_env_gain = env_gain + mode - 1; + if ( (unsigned) new_env_gain <= (unsigned) 0x80 >> mode ) + env_gain = new_env_gain; + else + regs (0x4080) |= 0x80; // optimization only + } + + // new end_time + blip_time_t const start_time = end_time; + end_time = final_end_time; + if ( end_time > env_time ) end_time = env_time; + if ( end_time > sweep_time ) end_time = sweep_time; + + // frequency modulation + int freq = wave_freq; + if ( mod_freq ) + { + // time of next modulation clock + blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq; + if ( end_time > mod_time ) + end_time = mod_time; + + // run modulator up to next clock and save old sweep_bias + int sweep_bias = regs (0x4085); + mod_fract -= (end_time - start_time) * mod_freq; + if ( mod_fract <= 0 ) + { + mod_fract += fract_range; + check( (unsigned) mod_fract <= fract_range ); + + static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 }; + int mod = mod_wave [mod_pos]; + mod_pos = (mod_pos + 1) & (wave_size - 1); + int new_sweep_bias = (sweep_bias + mod_table [mod]) & 0x7F; + if ( mod == 4 ) + new_sweep_bias = 0; + regs (0x4085) = new_sweep_bias; + } + + // apply frequency modulation + sweep_bias = (sweep_bias ^ 0x40) - 0x40; + int factor = sweep_bias * sweep_gain; + int extra = factor & 0x0F; + factor >>= 4; + if ( extra ) + { + factor--; + if ( sweep_bias >= 0 ) + factor += 3; + } + if ( factor > 193 ) factor -= 258; + if ( factor < -64 ) factor += 256; + freq += (freq * factor) >> 6; + if ( freq <= 0 ) + continue; + } + + // wave + int wave_fract = this->wave_fract; + blip_time_t delay = (wave_fract + freq - 1) / freq; + blip_time_t time = start_time + delay; + + if ( time <= end_time ) + { + // at least one wave clock within start_time...end_time + + blip_time_t const min_delay = fract_range / freq; + int wave_pos = this->wave_pos; + + int volume = env_gain; + if ( volume > vol_max ) + volume = vol_max; + volume *= master_volume; + + int const min_fract = min_delay * freq; + + do + { + // clock wave + int amp = regs_ [wave_pos] * volume; + wave_pos = (wave_pos + 1) & (wave_size - 1); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth.offset_inline( time, delta, output_ ); + } + + wave_fract += fract_range - delay * freq; + check( unsigned (fract_range - wave_fract) < freq ); + + // delay until next clock + delay = min_delay; + if ( wave_fract > min_fract ) + delay++; + check( delay && delay == (wave_fract + freq - 1) / freq ); + + time += delay; + } + while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong + + this->wave_pos = wave_pos; + } + this->wave_fract = wave_fract - (end_time - (time - delay)) * freq; + check( this->wave_fract > 0 ); + } + while ( end_time < final_end_time ); + + env_delay = env_time - final_end_time; check( env_delay >= 0 ); + sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 ); + } + last_time = final_end_time; +} diff --git a/Frameworks/GME/gme/Nes_Fds_Apu.h b/Frameworks/GME/gme/Nes_Fds_Apu.h index 21176b2db..1747079ce 100644 --- a/Frameworks/GME/gme/Nes_Fds_Apu.h +++ b/Frameworks/GME/gme/Nes_Fds_Apu.h @@ -1,139 +1,139 @@ -// NES FDS sound chip emulator - -// $package -#ifndef NES_FDS_APU_H -#define NES_FDS_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Nes_Fds_Apu { -public: - // setup - void set_tempo( double ); - enum { osc_count = 1 }; - void set_output( Blip_Buffer* buf ); - void volume( double ); - void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - - // emulation - void reset(); - enum { io_addr = 0x4040 }; - enum { io_size = 0x53 }; - void write( blip_time_t time, unsigned addr, int data ); - int read( blip_time_t time, unsigned addr ); - void end_frame( blip_time_t ); - -public: - Nes_Fds_Apu(); - void write_( unsigned addr, int data ); - BLARGG_DISABLE_NOTHROW - - void set_output( int index, Blip_Buffer* center, - Blip_Buffer* left_ignored = NULL, Blip_Buffer* right_ignored = NULL ); - BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4040 }; ) - BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4092 }; ) - BLARGG_DEPRECATED_TEXT( enum { reg_count = end_addr - start_addr + 1 }; ) - void osc_output( int, Blip_Buffer* ); -private: - enum { wave_size = 0x40 }; - enum { master_vol_max = 10 }; - enum { vol_max = 0x20 }; - enum { wave_sample_max = 0x3F }; - - unsigned char regs_ [io_size];// last written value to registers - - enum { lfo_base_tempo = 8 }; - int lfo_tempo; // normally 8; adjusted by set_tempo() - - int env_delay; - int env_speed; - int env_gain; - - int sweep_delay; - int sweep_speed; - int sweep_gain; - - int wave_pos; - int last_amp; - blip_time_t wave_fract; - - int mod_fract; - int mod_pos; - int mod_write_pos; - unsigned char mod_wave [wave_size]; - - // synthesis - blip_time_t last_time; - Blip_Buffer* output_; - Blip_Synth_Fast synth; - - // allow access to registers by absolute address (i.e. 0x4080) - unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; } - - void run_until( blip_time_t ); -}; - -inline void Nes_Fds_Apu::volume( double v ) -{ - synth.volume( 0.14 / master_vol_max / vol_max / wave_sample_max * v ); -} - -inline void Nes_Fds_Apu::set_output( Blip_Buffer* b ) -{ - output_ = b; -} - -inline void Nes_Fds_Apu::set_output( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) -{ - assert( (unsigned) i < osc_count ); - output_ = buf; -} - -inline void Nes_Fds_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - last_time -= end_time; - assert( last_time >= 0 ); -} - -inline void Nes_Fds_Apu::write( blip_time_t time, unsigned addr, int data ) -{ - run_until( time ); - write_( addr, data ); -} - -inline int Nes_Fds_Apu::read( blip_time_t time, unsigned addr ) -{ - run_until( time ); - - int result = 0xFF; - switch ( addr ) - { - case 0x4090: - result = env_gain; - break; - - case 0x4092: - result = sweep_gain; - break; - - default: - unsigned i = addr - io_addr; - if ( i < wave_size ) - result = regs_ [i]; - } - - return result | 0x40; -} - -inline Nes_Fds_Apu::Nes_Fds_Apu() -{ - lfo_tempo = lfo_base_tempo; - set_output( NULL ); - volume( 1.0 ); - reset(); -} - -#endif +// NES FDS sound chip emulator + +// $package +#ifndef NES_FDS_APU_H +#define NES_FDS_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Nes_Fds_Apu { +public: + // setup + void set_tempo( double ); + enum { osc_count = 1 }; + void set_output( Blip_Buffer* buf ); + void volume( double ); + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + + // emulation + void reset(); + enum { io_addr = 0x4040 }; + enum { io_size = 0x53 }; + void write( blip_time_t time, unsigned addr, int data ); + int read( blip_time_t time, unsigned addr ); + void end_frame( blip_time_t ); + +public: + Nes_Fds_Apu(); + void write_( unsigned addr, int data ); + BLARGG_DISABLE_NOTHROW + + void set_output( int index, Blip_Buffer* center, + Blip_Buffer* left_ignored = NULL, Blip_Buffer* right_ignored = NULL ); + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4040 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4092 }; ) + BLARGG_DEPRECATED_TEXT( enum { reg_count = end_addr - start_addr + 1 }; ) + void osc_output( int, Blip_Buffer* ); +private: + enum { wave_size = 0x40 }; + enum { master_vol_max = 10 }; + enum { vol_max = 0x20 }; + enum { wave_sample_max = 0x3F }; + + unsigned char regs_ [io_size];// last written value to registers + + enum { lfo_base_tempo = 8 }; + int lfo_tempo; // normally 8; adjusted by set_tempo() + + int env_delay; + int env_speed; + int env_gain; + + int sweep_delay; + int sweep_speed; + int sweep_gain; + + int wave_pos; + int last_amp; + blip_time_t wave_fract; + + int mod_fract; + int mod_pos; + int mod_write_pos; + unsigned char mod_wave [wave_size]; + + // synthesis + blip_time_t last_time; + Blip_Buffer* output_; + Blip_Synth_Fast synth; + + // allow access to registers by absolute address (i.e. 0x4080) + unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; } + + void run_until( blip_time_t ); +}; + +inline void Nes_Fds_Apu::volume( double v ) +{ + synth.volume( 0.14 / master_vol_max / vol_max / wave_sample_max * v ); +} + +inline void Nes_Fds_Apu::set_output( Blip_Buffer* b ) +{ + output_ = b; +} + +inline void Nes_Fds_Apu::set_output( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) +{ + assert( (unsigned) i < osc_count ); + output_ = buf; +} + +inline void Nes_Fds_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + last_time -= end_time; + assert( last_time >= 0 ); +} + +inline void Nes_Fds_Apu::write( blip_time_t time, unsigned addr, int data ) +{ + run_until( time ); + write_( addr, data ); +} + +inline int Nes_Fds_Apu::read( blip_time_t time, unsigned addr ) +{ + run_until( time ); + + int result = 0xFF; + switch ( addr ) + { + case 0x4090: + result = env_gain; + break; + + case 0x4092: + result = sweep_gain; + break; + + default: + unsigned i = addr - io_addr; + if ( i < wave_size ) + result = regs_ [i]; + } + + return result | 0x40; +} + +inline Nes_Fds_Apu::Nes_Fds_Apu() +{ + lfo_tempo = lfo_base_tempo; + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +#endif diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp index b3ab6d093..b3be3cedc 100644 --- a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp @@ -1,121 +1,121 @@ -// $package. http://www.slack.net/~ant/ - -#include "Nes_Fme7_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void Nes_Fme7_Apu::reset() -{ - last_time = 0; - - for ( int i = 0; i < osc_count; i++ ) - oscs [i].last_amp = 0; - - fme7_apu_state_t* state = this; - memset( state, 0, sizeof *state ); -} - -unsigned char const Nes_Fme7_Apu::amp_table [16] = -{ - #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5) - ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), - ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), - ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), - ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000) - #undef ENTRY -}; - -void Nes_Fme7_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_time ); - - for ( int index = 0; index < osc_count; index++ ) - { - int mode = regs [7] >> index; - int vol_mode = regs [010 + index]; - int volume = amp_table [vol_mode & 0x0F]; - - Blip_Buffer* const osc_output = oscs [index].output; - if ( !osc_output ) - continue; - - // check for unsupported mode - #ifndef NDEBUG - if ( (mode & 011) <= 001 && vol_mode & 0x1F ) - dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", - mode, vol_mode & 0x1F ); - #endif - - if ( (mode & 001) | (vol_mode & 0x10) ) - volume = 0; // noise and envelope aren't supported - - // period - int const period_factor = 16; - unsigned period = (regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor + - regs [index * 2] * period_factor; - if ( period < 50 ) // around 22 kHz - { - volume = 0; - if ( !period ) // on my AY-3-8910A, period doesn't have extra one added - period = period_factor; - } - - // current amplitude - int amp = volume; - if ( !phases [index] ) - amp = 0; - - { - int delta = amp - oscs [index].last_amp; - if ( delta ) - { - oscs [index].last_amp = amp; - osc_output->set_modified(); - synth.offset( last_time, delta, osc_output ); - } - } - - blip_time_t time = last_time + delays [index]; - if ( time < end_time ) - { - int delta = amp * 2 - volume; - osc_output->set_modified(); - if ( volume ) - { - do - { - delta = -delta; - synth.offset_inline( time, delta, osc_output ); - time += period; - } - while ( time < end_time ); - - oscs [index].last_amp = (delta + volume) >> 1; - phases [index] = (delta > 0); - } - else - { - // maintain phase when silent - int count = (end_time - time + period - 1) / period; - phases [index] ^= count & 1; - time += count * period; - } - } - - delays [index] = time - end_time; - } - - last_time = end_time; -} - +// $package. http://www.slack.net/~ant/ + +#include "Nes_Fme7_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Nes_Fme7_Apu::reset() +{ + last_time = 0; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i].last_amp = 0; + + fme7_apu_state_t* state = this; + memset( state, 0, sizeof *state ); +} + +unsigned char const Nes_Fme7_Apu::amp_table [16] = +{ + #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5) + ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156), + ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624), + ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498), + ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000) + #undef ENTRY +}; + +void Nes_Fme7_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); + + for ( int index = 0; index < osc_count; index++ ) + { + int mode = regs [7] >> index; + int vol_mode = regs [010 + index]; + int volume = amp_table [vol_mode & 0x0F]; + + Blip_Buffer* const osc_output = oscs [index].output; + if ( !osc_output ) + continue; + + // check for unsupported mode + #ifndef NDEBUG + if ( (mode & 011) <= 001 && vol_mode & 0x1F ) + dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", + mode, vol_mode & 0x1F ); + #endif + + if ( (mode & 001) | (vol_mode & 0x10) ) + volume = 0; // noise and envelope aren't supported + + // period + int const period_factor = 16; + unsigned period = (regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor + + regs [index * 2] * period_factor; + if ( period < 50 ) // around 22 kHz + { + volume = 0; + if ( !period ) // on my AY-3-8910A, period doesn't have extra one added + period = period_factor; + } + + // current amplitude + int amp = volume; + if ( !phases [index] ) + amp = 0; + + { + int delta = amp - oscs [index].last_amp; + if ( delta ) + { + oscs [index].last_amp = amp; + osc_output->set_modified(); + synth.offset( last_time, delta, osc_output ); + } + } + + blip_time_t time = last_time + delays [index]; + if ( time < end_time ) + { + int delta = amp * 2 - volume; + osc_output->set_modified(); + if ( volume ) + { + do + { + delta = -delta; + synth.offset_inline( time, delta, osc_output ); + time += period; + } + while ( time < end_time ); + + oscs [index].last_amp = (delta + volume) >> 1; + phases [index] = (delta > 0); + } + else + { + // maintain phase when silent + int count = (end_time - time + period - 1) / period; + phases [index] ^= count & 1; + time += count * period; + } + } + + delays [index] = time - end_time; + } + + last_time = end_time; +} + diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.h b/Frameworks/GME/gme/Nes_Fme7_Apu.h index e7272637a..bc8a1320b 100644 --- a/Frameworks/GME/gme/Nes_Fme7_Apu.h +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.h @@ -1,131 +1,131 @@ -// Sunsoft FME-7 sound emulator - -// $package -#ifndef NES_FME7_APU_H -#define NES_FME7_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct fme7_apu_state_t -{ - enum { reg_count = 14 }; - BOOST::uint8_t regs [reg_count]; - BOOST::uint8_t phases [3]; // 0 or 1 - BOOST::uint8_t latch; - BOOST::uint16_t delays [3]; // a, b, c -}; - -class Nes_Fme7_Apu : private fme7_apu_state_t { -public: - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void set_output( Blip_Buffer* ); - enum { osc_count = 3 }; - void set_output( int index, Blip_Buffer* ); - void end_frame( blip_time_t ); - void save_state( fme7_apu_state_t* ) const; - void load_state( fme7_apu_state_t const& ); - - // Mask and addresses of registers - enum { addr_mask = 0xE000 }; - enum { data_addr = 0xE000 }; - enum { latch_addr = 0xC000 }; - - // (addr & addr_mask) == latch_addr - void write_latch( int ); - - // (addr & addr_mask) == data_addr - void write_data( blip_time_t, int data ); - -public: - Nes_Fme7_Apu(); - BLARGG_DISABLE_NOTHROW -private: - // noncopyable - Nes_Fme7_Apu( const Nes_Fme7_Apu& ); - Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); - - static unsigned char const amp_table [16]; - - struct { - Blip_Buffer* output; - int last_amp; - } oscs [osc_count]; - blip_time_t last_time; - - enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff - Blip_Synth_Norm synth; - - void run_until( blip_time_t ); -}; - -inline void Nes_Fme7_Apu::volume( double v ) -{ - synth.volume( 0.38 / amp_range * v ); // to do: fine-tune -} - -inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq ) -{ - synth.treble_eq( eq ); -} - -inline void Nes_Fme7_Apu::set_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Fme7_Apu::set_output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; ++i ) - set_output( i, buf ); -} - -inline Nes_Fme7_Apu::Nes_Fme7_Apu() -{ - set_output( NULL ); - volume( 1.0 ); - reset(); -} - -inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; } - -inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data ) -{ - if ( (unsigned) latch >= reg_count ) - { - #ifdef dprintf - dprintf( "FME7 write to %02X (past end of sound registers)\n", (int) latch ); - #endif - return; - } - - run_until( time ); - regs [latch] = data; -} - -inline void Nes_Fme7_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const -{ - *out = *this; -} - -inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in ) -{ - reset(); - fme7_apu_state_t* state = this; - *state = in; -} - -#endif +// Sunsoft FME-7 sound emulator + +// $package +#ifndef NES_FME7_APU_H +#define NES_FME7_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct fme7_apu_state_t +{ + enum { reg_count = 14 }; + BOOST::uint8_t regs [reg_count]; + BOOST::uint8_t phases [3]; // 0 or 1 + BOOST::uint8_t latch; + BOOST::uint16_t delays [3]; // a, b, c +}; + +class Nes_Fme7_Apu : private fme7_apu_state_t { +public: + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void set_output( Blip_Buffer* ); + enum { osc_count = 3 }; + void set_output( int index, Blip_Buffer* ); + void end_frame( blip_time_t ); + void save_state( fme7_apu_state_t* ) const; + void load_state( fme7_apu_state_t const& ); + + // Mask and addresses of registers + enum { addr_mask = 0xE000 }; + enum { data_addr = 0xE000 }; + enum { latch_addr = 0xC000 }; + + // (addr & addr_mask) == latch_addr + void write_latch( int ); + + // (addr & addr_mask) == data_addr + void write_data( blip_time_t, int data ); + +public: + Nes_Fme7_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Fme7_Apu( const Nes_Fme7_Apu& ); + Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); + + static unsigned char const amp_table [16]; + + struct { + Blip_Buffer* output; + int last_amp; + } oscs [osc_count]; + blip_time_t last_time; + + enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff + Blip_Synth_Norm synth; + + void run_until( blip_time_t ); +}; + +inline void Nes_Fme7_Apu::volume( double v ) +{ + synth.volume( 0.38 / amp_range * v ); // to do: fine-tune +} + +inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq ) +{ + synth.treble_eq( eq ); +} + +inline void Nes_Fme7_Apu::set_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Fme7_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +inline Nes_Fme7_Apu::Nes_Fme7_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; } + +inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data ) +{ + if ( (unsigned) latch >= reg_count ) + { + #ifdef dprintf + dprintf( "FME7 write to %02X (past end of sound registers)\n", (int) latch ); + #endif + return; + } + + run_until( time ); + regs [latch] = data; +} + +inline void Nes_Fme7_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const +{ + *out = *this; +} + +inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in ) +{ + reset(); + fme7_apu_state_t* state = this; + *state = in; +} + +#endif diff --git a/Frameworks/GME/gme/Nes_Mmc5_Apu.h b/Frameworks/GME/gme/Nes_Mmc5_Apu.h index 717658d3e..f83313130 100644 --- a/Frameworks/GME/gme/Nes_Mmc5_Apu.h +++ b/Frameworks/GME/gme/Nes_Mmc5_Apu.h @@ -1,70 +1,70 @@ -// NES MMC5 sound chip emulator - -// Nes_Snd_Emu $vers -#ifndef NES_MMC5_APU_H -#define NES_MMC5_APU_H - -#include "blargg_common.h" -#include "Nes_Apu.h" - -class Nes_Mmc5_Apu : public Nes_Apu { -public: - enum { regs_addr = 0x5000 }; - enum { regs_size = 0x16 }; - - enum { osc_count = 3 }; - void write_register( blip_time_t, unsigned addr, int data ); - void set_output( Blip_Buffer* ); - void set_output( int index, Blip_Buffer* ); - - enum { exram_size = 1024 }; - unsigned char exram [exram_size]; - - BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x5000 }; ) - BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x5015 }; ) -}; - -inline void Nes_Mmc5_Apu::set_output( int i, Blip_Buffer* b ) -{ - // in: square 1, square 2, PCM - // out: square 1, square 2, skipped, skipped, PCM - if ( i > 1 ) - i += 2; - Nes_Apu::set_output( i, b ); -} - -inline void Nes_Mmc5_Apu::set_output( Blip_Buffer* b ) -{ - set_output( 0, b ); - set_output( 1, b ); - set_output( 2, b ); -} - -inline void Nes_Mmc5_Apu::write_register( blip_time_t time, unsigned addr, int data ) -{ - switch ( addr ) - { - case 0x5015: // channel enables - data &= 0x03; // enable the square waves only - // fall through - case 0x5000: // Square 1 - case 0x5002: - case 0x5003: - case 0x5004: // Square 2 - case 0x5006: - case 0x5007: - case 0x5011: // DAC - Nes_Apu::write_register( time, addr - 0x1000, data ); - break; - - case 0x5010: // some things write to this for some reason - break; - -#ifdef BLARGG_DEBUG_H - default: - dprintf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data ); -#endif - } -} - -#endif +// NES MMC5 sound chip emulator + +// Nes_Snd_Emu $vers +#ifndef NES_MMC5_APU_H +#define NES_MMC5_APU_H + +#include "blargg_common.h" +#include "Nes_Apu.h" + +class Nes_Mmc5_Apu : public Nes_Apu { +public: + enum { regs_addr = 0x5000 }; + enum { regs_size = 0x16 }; + + enum { osc_count = 3 }; + void write_register( blip_time_t, unsigned addr, int data ); + void set_output( Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); + + enum { exram_size = 1024 }; + unsigned char exram [exram_size]; + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x5000 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x5015 }; ) +}; + +inline void Nes_Mmc5_Apu::set_output( int i, Blip_Buffer* b ) +{ + // in: square 1, square 2, PCM + // out: square 1, square 2, skipped, skipped, PCM + if ( i > 1 ) + i += 2; + Nes_Apu::set_output( i, b ); +} + +inline void Nes_Mmc5_Apu::set_output( Blip_Buffer* b ) +{ + set_output( 0, b ); + set_output( 1, b ); + set_output( 2, b ); +} + +inline void Nes_Mmc5_Apu::write_register( blip_time_t time, unsigned addr, int data ) +{ + switch ( addr ) + { + case 0x5015: // channel enables + data &= 0x03; // enable the square waves only + // fall through + case 0x5000: // Square 1 + case 0x5002: + case 0x5003: + case 0x5004: // Square 2 + case 0x5006: + case 0x5007: + case 0x5011: // DAC + Nes_Apu::write_register( time, addr - 0x1000, data ); + break; + + case 0x5010: // some things write to this for some reason + break; + +#ifdef BLARGG_DEBUG_H + default: + dprintf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data ); +#endif + } +} + +#endif diff --git a/Frameworks/GME/gme/Nes_Namco_Apu.h b/Frameworks/GME/gme/Nes_Namco_Apu.h index 2a067d9e9..af68e6417 100644 --- a/Frameworks/GME/gme/Nes_Namco_Apu.h +++ b/Frameworks/GME/gme/Nes_Namco_Apu.h @@ -1,102 +1,102 @@ -// Namco 106 sound chip emulator - -// Nes_Snd_Emu $vers -#ifndef NES_NAMCO_APU_H -#define NES_NAMCO_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct namco_state_t; - -class Nes_Namco_Apu { -public: - // See Nes_Apu.h for reference. - void volume( double ); - void treble_eq( const blip_eq_t& ); - void set_output( Blip_Buffer* ); - enum { osc_count = 8 }; - void set_output( int index, Blip_Buffer* ); - void reset(); - void end_frame( blip_time_t ); - - // Read/write data register is at 0x4800 - enum { data_reg_addr = 0x4800 }; - void write_data( blip_time_t, int ); - int read_data(); - - // Write-only address register is at 0xF800 - enum { addr_reg_addr = 0xF800 }; - void write_addr( int ); - - // to do: implement save/restore - void save_state( namco_state_t* out ) const; - void load_state( namco_state_t const& ); - -public: - Nes_Namco_Apu(); - BLARGG_DISABLE_NOTHROW -private: - // noncopyable - Nes_Namco_Apu( const Nes_Namco_Apu& ); - Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); - - struct Namco_Osc { - int delay; - Blip_Buffer* output; - short last_amp; - short wave_pos; - }; - - Namco_Osc oscs [osc_count]; - - blip_time_t last_time; - int addr_reg; - - enum { reg_count = 0x80 }; - BOOST::uint8_t reg [reg_count]; - Blip_Synth_Norm synth; - - BOOST::uint8_t& access(); - void run_until( blip_time_t ); -}; -/* -struct namco_state_t -{ - BOOST::uint8_t regs [0x80]; - BOOST::uint8_t addr; - BOOST::uint8_t unused; - BOOST::uint8_t positions [8]; - BOOST::uint32_t delays [8]; -}; -*/ - -inline BOOST::uint8_t& Nes_Namco_Apu::access() -{ - int addr = addr_reg & 0x7F; - if ( addr_reg & 0x80 ) - addr_reg = (addr + 1) | 0x80; - return reg [addr]; -} - -inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count / 15 * v ); } - -inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } - -inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; } - -inline int Nes_Namco_Apu::read_data() { return access(); } - -inline void Nes_Namco_Apu::set_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Namco_Apu::write_data( blip_time_t time, int data ) -{ - run_until( time ); - access() = data; -} - -#endif +// Namco 106 sound chip emulator + +// Nes_Snd_Emu $vers +#ifndef NES_NAMCO_APU_H +#define NES_NAMCO_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct namco_state_t; + +class Nes_Namco_Apu { +public: + // See Nes_Apu.h for reference. + void volume( double ); + void treble_eq( const blip_eq_t& ); + void set_output( Blip_Buffer* ); + enum { osc_count = 8 }; + void set_output( int index, Blip_Buffer* ); + void reset(); + void end_frame( blip_time_t ); + + // Read/write data register is at 0x4800 + enum { data_reg_addr = 0x4800 }; + void write_data( blip_time_t, int ); + int read_data(); + + // Write-only address register is at 0xF800 + enum { addr_reg_addr = 0xF800 }; + void write_addr( int ); + + // to do: implement save/restore + void save_state( namco_state_t* out ) const; + void load_state( namco_state_t const& ); + +public: + Nes_Namco_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Namco_Apu( const Nes_Namco_Apu& ); + Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); + + struct Namco_Osc { + int delay; + Blip_Buffer* output; + short last_amp; + short wave_pos; + }; + + Namco_Osc oscs [osc_count]; + + blip_time_t last_time; + int addr_reg; + + enum { reg_count = 0x80 }; + BOOST::uint8_t reg [reg_count]; + Blip_Synth_Norm synth; + + BOOST::uint8_t& access(); + void run_until( blip_time_t ); +}; +/* +struct namco_state_t +{ + BOOST::uint8_t regs [0x80]; + BOOST::uint8_t addr; + BOOST::uint8_t unused; + BOOST::uint8_t positions [8]; + BOOST::uint32_t delays [8]; +}; +*/ + +inline BOOST::uint8_t& Nes_Namco_Apu::access() +{ + int addr = addr_reg & 0x7F; + if ( addr_reg & 0x80 ) + addr_reg = (addr + 1) | 0x80; + return reg [addr]; +} + +inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count / 15 * v ); } + +inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } + +inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; } + +inline int Nes_Namco_Apu::read_data() { return access(); } + +inline void Nes_Namco_Apu::set_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Namco_Apu::write_data( blip_time_t time, int data ) +{ + run_until( time ); + access() = data; +} + +#endif diff --git a/Frameworks/GME/gme/Nes_Oscs.cpp b/Frameworks/GME/gme/Nes_Oscs.cpp index 367744a7a..d3547a8f1 100644 --- a/Frameworks/GME/gme/Nes_Oscs.cpp +++ b/Frameworks/GME/gme/Nes_Oscs.cpp @@ -1,578 +1,578 @@ -// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// Nes_Osc - -void Nes_Osc::clock_length( int halt_mask ) -{ - if ( length_counter && !(regs [0] & halt_mask) ) - length_counter--; -} - -void Nes_Envelope::clock_envelope() -{ - int period = regs [0] & 15; - if ( reg_written [3] ) - { - reg_written [3] = false; - env_delay = period; - envelope = 15; - } - else if ( --env_delay < 0 ) - { - env_delay = period; - if ( envelope | (regs [0] & 0x20) ) - envelope = (envelope - 1) & 15; - } -} - -int Nes_Envelope::volume() const -{ - return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; -} - -// Nes_Square - -void Nes_Square::clock_sweep( int negative_adjust ) -{ - int sweep = regs [1]; - - if ( --sweep_delay < 0 ) - { - reg_written [1] = true; - - int period = this->period(); - int shift = sweep & shift_mask; - if ( shift && (sweep & 0x80) && period >= 8 ) - { - int offset = period >> shift; - - if ( sweep & negate_flag ) - offset = negative_adjust - offset; - - if ( period + offset < 0x800 ) - { - period += offset; - // rewrite period - regs [2] = period & 0xFF; - regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); - } - } - } - - if ( reg_written [1] ) - { - reg_written [1] = false; - sweep_delay = (sweep >> 4) & 7; - } -} - -// TODO: clean up -inline Nes_Square::nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ) -{ - nes_time_t remain = end_time - time; - if ( remain > 0 ) - { - int count = (remain + timer_period - 1) / timer_period; - phase = (phase + count) & (phase_range - 1); - time += count * timer_period; - } - return time; -} - -void Nes_Square::run( nes_time_t time, nes_time_t end_time ) -{ - const int period = this->period(); - const int timer_period = (period + 1) * 2; - - if ( !output ) - { - delay = maintain_phase( time + delay, end_time, timer_period ) - end_time; - return; - } - - int offset = period >> (regs [1] & shift_mask); - if ( regs [1] & negate_flag ) - offset = 0; - - const int volume = this->volume(); - if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) - { - if ( last_amp ) - { - output->set_modified(); - synth.offset( time, -last_amp, output ); - last_amp = 0; - } - - time += delay; - time = maintain_phase( time, end_time, timer_period ); - } - else - { - // handle duty select - int duty_select = (regs [0] >> 6) & 3; - int duty = 1 << duty_select; // 1, 2, 4, 2 - int amp = 0; - if ( duty_select == 3 ) - { - duty = 2; // negated 25% - amp = volume; - } - if ( phase < duty ) - amp ^= volume; - - output->set_modified(); - { - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); - } - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - const Synth& synth = this->synth; - int delta = amp * 2 - volume; - int phase = this->phase; - - do - { - phase = (phase + 1) & (phase_range - 1); - if ( phase == 0 || phase == duty ) - { - delta = -delta; - synth.offset_inline( time, delta, output ); - } - time += timer_period; - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->phase = phase; - } - } - - delay = time - end_time; -} - -// Nes_Triangle - -void Nes_Triangle::clock_linear_counter() -{ - if ( reg_written [3] ) - linear_counter = regs [0] & 0x7F; - else if ( linear_counter ) - linear_counter--; - - if ( !(regs [0] & 0x80) ) - reg_written [3] = false; -} - -inline int Nes_Triangle::calc_amp() const -{ - int amp = phase_range - phase; - if ( amp < 0 ) - amp = phase - (phase_range + 1); - return amp; -} - -// TODO: clean up -inline Nes_Square::nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ) -{ - nes_time_t remain = end_time - time; - if ( remain > 0 ) - { - int count = (remain + timer_period - 1) / timer_period; - phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); - phase++; - time += count * timer_period; - } - return time; -} - -void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) -{ - const int timer_period = period() + 1; - if ( !output ) - { - time += delay; - delay = 0; - if ( length_counter && linear_counter && timer_period >= 3 ) - delay = maintain_phase( time, end_time, timer_period ) - end_time; - return; - } - - // to do: track phase when period < 3 - // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. - - int delta = update_amp( calc_amp() ); - if ( delta ) - { - output->set_modified(); - synth.offset( time, delta, output ); - } - - time += delay; - if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) - { - time = end_time; - } - else if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - - int phase = this->phase; - int volume = 1; - if ( phase > phase_range ) - { - phase -= phase_range; - volume = -volume; - } - output->set_modified(); - - do - { - if ( --phase == 0 ) - { - phase = phase_range; - volume = -volume; - } - else - { - synth.offset_inline( time, volume, output ); - } - - time += timer_period; - } - while ( time < end_time ); - - if ( volume < 0 ) - phase += phase_range; - this->phase = phase; - last_amp = calc_amp(); - } - delay = time - end_time; -} - -// Nes_Dmc - -void Nes_Dmc::reset() -{ - address = 0; - dac = 0; - buf = 0; - bits_remain = 1; - bits = 0; - buf_full = false; - silence = true; - next_irq = Nes_Apu::no_irq; - irq_flag = false; - irq_enabled = false; - - Nes_Osc::reset(); - period = 0x1AC; -} - -void Nes_Dmc::recalc_irq() -{ - nes_time_t irq = Nes_Apu::no_irq; - if ( irq_enabled && length_counter ) - irq = apu->last_dmc_time + delay + - ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; - if ( irq != next_irq ) - { - next_irq = irq; - apu->irq_changed(); - } -} - -int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const -{ - if ( last_read ) - *last_read = time; - - if ( length_counter == 0 ) - return 0; // not reading - - nes_time_t first_read = next_read_time(); - nes_time_t avail = time - first_read; - if ( avail <= 0 ) - return 0; - - int count = (avail - 1) / (period * 8) + 1; - if ( !(regs [0] & loop_flag) && count > length_counter ) - count = length_counter; - - if ( last_read ) - { - *last_read = first_read + (count - 1) * (period * 8) + 1; - check( *last_read <= time ); - check( count == count_reads( *last_read, NULL ) ); - check( count - 1 == count_reads( *last_read - 1, NULL ) ); - } - - return count; -} - -static short const dmc_period_table [2] [16] = { - {428, 380, 340, 320, 286, 254, 226, 214, // NTSC - 190, 160, 142, 128, 106, 84, 72, 54}, - - {398, 354, 316, 298, 276, 236, 210, 198, // PAL - 176, 148, 132, 118, 98, 78, 66, 50} -}; - -inline void Nes_Dmc::reload_sample() -{ - address = 0x4000 + regs [2] * 0x40; - length_counter = regs [3] * 0x10 + 1; -} - -static int const dmc_table [128] = -{ - 0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340, - 361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664, - 683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955, - 972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217, -1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454, -1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670, -1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867, -1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048, -}; - -inline int Nes_Dmc::update_amp_nonlinear( int in ) -{ - if ( !nonlinear ) - in = dmc_table [in]; - int delta = in - last_amp; - last_amp = in; - return delta; -} - -void Nes_Dmc::write_register( int addr, int data ) -{ - if ( addr == 0 ) - { - period = dmc_period_table [pal_mode] [data & 15]; - irq_enabled = (data & 0xC0) == 0x80; // enabled only if loop disabled - irq_flag &= irq_enabled; - recalc_irq(); - } - else if ( addr == 1 ) - { - dac = data & 0x7F; - } -} - -void Nes_Dmc::start() -{ - reload_sample(); - fill_buffer(); - recalc_irq(); -} - -void Nes_Dmc::fill_buffer() -{ - if ( !buf_full && length_counter ) - { - require( apu->dmc_reader.f ); // dmc_reader must be set - buf = apu->dmc_reader.f( apu->dmc_reader.data, 0x8000u + address ); - address = (address + 1) & 0x7FFF; - buf_full = true; - if ( --length_counter == 0 ) - { - if ( regs [0] & loop_flag ) - { - reload_sample(); - } - else - { - apu->osc_enables &= ~0x10; - irq_flag = irq_enabled; - next_irq = Nes_Apu::no_irq; - apu->irq_changed(); - } - } - } -} - -void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) -{ - int delta = update_amp_nonlinear( dac ); - if ( !output ) - { - silence = true; - } - else if ( delta ) - { - output->set_modified(); - synth.offset( time, delta, output ); - } - - time += delay; - if ( time < end_time ) - { - int bits_remain = this->bits_remain; - if ( silence && !buf_full ) - { - int count = (end_time - time + period - 1) / period; - bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; - time += count * period; - } - else - { - Blip_Buffer* const output = this->output; - const int period = this->period; - int bits = this->bits; - int dac = this->dac; - if ( output ) - output->set_modified(); - - do - { - if ( !silence ) - { - int step = (bits & 1) * 4 - 2; - bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) - { - dac += step; - synth.offset_inline( time, update_amp_nonlinear( dac ), output ); - } - } - - time += period; - - if ( --bits_remain == 0 ) - { - bits_remain = 8; - if ( !buf_full ) - { - silence = true; - } - else - { - silence = false; - bits = buf; - buf_full = false; - if ( !output ) - silence = true; - fill_buffer(); - } - } - } - while ( time < end_time ); - - this->dac = dac; - this->bits = bits; - } - this->bits_remain = bits_remain; - } - delay = time - end_time; -} - -// Nes_Noise - -static short const noise_period_table [16] = { - 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, - 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 -}; - -void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) -{ - int period = noise_period_table [regs [2] & 15]; - - if ( !output ) - { - // TODO: clean up - time += delay; - delay = time + (end_time - time + period - 1) / period * period - end_time; - return; - } - - - const int volume = this->volume(); - int amp = (noise & 1) ? volume : 0; - { - int delta = update_amp( amp ); - if ( delta ) - { - output->set_modified(); - synth.offset( time, delta, output ); - } - } - - time += delay; - if ( time < end_time ) - { - const int mode_flag = 0x80; - - if ( !volume ) - { - // round to next multiple of period - time += (end_time - time + period - 1) / period * period; - - // approximate noise cycling while muted, by shuffling up noise register - // to do: precise muted noise cycling? - if ( !(regs [2] & mode_flag) ) - { - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - else - { - Blip_Buffer* const output = this->output; - - // using resampled time avoids conversion in synth.offset() - blip_resampled_time_t rperiod = output->resampled_duration( period ); - blip_resampled_time_t rtime = output->resampled_time( time ); - - int noise = this->noise; - int delta = amp * 2 - volume; - const int tap = (regs [2] & mode_flag ? 8 : 13); - output->set_modified(); - - do - { - int feedback = (noise << tap) ^ (noise << 14); - time += period; - - if ( (noise + 1) & 2 ) - { - // bits 0 and 1 of noise differ - delta = -delta; - synth.offset_resampled( rtime, delta, output ); - } - - rtime += rperiod; - noise = (feedback & 0x4000) | (noise >> 1); - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->noise = noise; - } - } - - delay = time - end_time; -} - +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Nes_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Nes_Osc + +void Nes_Osc::clock_length( int halt_mask ) +{ + if ( length_counter && !(regs [0] & halt_mask) ) + length_counter--; +} + +void Nes_Envelope::clock_envelope() +{ + int period = regs [0] & 15; + if ( reg_written [3] ) + { + reg_written [3] = false; + env_delay = period; + envelope = 15; + } + else if ( --env_delay < 0 ) + { + env_delay = period; + if ( envelope | (regs [0] & 0x20) ) + envelope = (envelope - 1) & 15; + } +} + +int Nes_Envelope::volume() const +{ + return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; +} + +// Nes_Square + +void Nes_Square::clock_sweep( int negative_adjust ) +{ + int sweep = regs [1]; + + if ( --sweep_delay < 0 ) + { + reg_written [1] = true; + + int period = this->period(); + int shift = sweep & shift_mask; + if ( shift && (sweep & 0x80) && period >= 8 ) + { + int offset = period >> shift; + + if ( sweep & negate_flag ) + offset = negative_adjust - offset; + + if ( period + offset < 0x800 ) + { + period += offset; + // rewrite period + regs [2] = period & 0xFF; + regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); + } + } + } + + if ( reg_written [1] ) + { + reg_written [1] = false; + sweep_delay = (sweep >> 4) & 7; + } +} + +// TODO: clean up +inline Nes_Square::nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ) +{ + nes_time_t remain = end_time - time; + if ( remain > 0 ) + { + int count = (remain + timer_period - 1) / timer_period; + phase = (phase + count) & (phase_range - 1); + time += count * timer_period; + } + return time; +} + +void Nes_Square::run( nes_time_t time, nes_time_t end_time ) +{ + const int period = this->period(); + const int timer_period = (period + 1) * 2; + + if ( !output ) + { + delay = maintain_phase( time + delay, end_time, timer_period ) - end_time; + return; + } + + int offset = period >> (regs [1] & shift_mask); + if ( regs [1] & negate_flag ) + offset = 0; + + const int volume = this->volume(); + if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) + { + if ( last_amp ) + { + output->set_modified(); + synth.offset( time, -last_amp, output ); + last_amp = 0; + } + + time += delay; + time = maintain_phase( time, end_time, timer_period ); + } + else + { + // handle duty select + int duty_select = (regs [0] >> 6) & 3; + int duty = 1 << duty_select; // 1, 2, 4, 2 + int amp = 0; + if ( duty_select == 3 ) + { + duty = 2; // negated 25% + amp = volume; + } + if ( phase < duty ) + amp ^= volume; + + output->set_modified(); + { + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + } + + time += delay; + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + const Synth& synth = this->synth; + int delta = amp * 2 - volume; + int phase = this->phase; + + do + { + phase = (phase + 1) & (phase_range - 1); + if ( phase == 0 || phase == duty ) + { + delta = -delta; + synth.offset_inline( time, delta, output ); + } + time += timer_period; + } + while ( time < end_time ); + + last_amp = (delta + volume) >> 1; + this->phase = phase; + } + } + + delay = time - end_time; +} + +// Nes_Triangle + +void Nes_Triangle::clock_linear_counter() +{ + if ( reg_written [3] ) + linear_counter = regs [0] & 0x7F; + else if ( linear_counter ) + linear_counter--; + + if ( !(regs [0] & 0x80) ) + reg_written [3] = false; +} + +inline int Nes_Triangle::calc_amp() const +{ + int amp = phase_range - phase; + if ( amp < 0 ) + amp = phase - (phase_range + 1); + return amp; +} + +// TODO: clean up +inline Nes_Square::nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ) +{ + nes_time_t remain = end_time - time; + if ( remain > 0 ) + { + int count = (remain + timer_period - 1) / timer_period; + phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); + phase++; + time += count * timer_period; + } + return time; +} + +void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) +{ + const int timer_period = period() + 1; + if ( !output ) + { + time += delay; + delay = 0; + if ( length_counter && linear_counter && timer_period >= 3 ) + delay = maintain_phase( time, end_time, timer_period ) - end_time; + return; + } + + // to do: track phase when period < 3 + // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. + + int delta = update_amp( calc_amp() ); + if ( delta ) + { + output->set_modified(); + synth.offset( time, delta, output ); + } + + time += delay; + if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) + { + time = end_time; + } + else if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + + int phase = this->phase; + int volume = 1; + if ( phase > phase_range ) + { + phase -= phase_range; + volume = -volume; + } + output->set_modified(); + + do + { + if ( --phase == 0 ) + { + phase = phase_range; + volume = -volume; + } + else + { + synth.offset_inline( time, volume, output ); + } + + time += timer_period; + } + while ( time < end_time ); + + if ( volume < 0 ) + phase += phase_range; + this->phase = phase; + last_amp = calc_amp(); + } + delay = time - end_time; +} + +// Nes_Dmc + +void Nes_Dmc::reset() +{ + address = 0; + dac = 0; + buf = 0; + bits_remain = 1; + bits = 0; + buf_full = false; + silence = true; + next_irq = Nes_Apu::no_irq; + irq_flag = false; + irq_enabled = false; + + Nes_Osc::reset(); + period = 0x1AC; +} + +void Nes_Dmc::recalc_irq() +{ + nes_time_t irq = Nes_Apu::no_irq; + if ( irq_enabled && length_counter ) + irq = apu->last_dmc_time + delay + + ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; + if ( irq != next_irq ) + { + next_irq = irq; + apu->irq_changed(); + } +} + +int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const +{ + if ( last_read ) + *last_read = time; + + if ( length_counter == 0 ) + return 0; // not reading + + nes_time_t first_read = next_read_time(); + nes_time_t avail = time - first_read; + if ( avail <= 0 ) + return 0; + + int count = (avail - 1) / (period * 8) + 1; + if ( !(regs [0] & loop_flag) && count > length_counter ) + count = length_counter; + + if ( last_read ) + { + *last_read = first_read + (count - 1) * (period * 8) + 1; + check( *last_read <= time ); + check( count == count_reads( *last_read, NULL ) ); + check( count - 1 == count_reads( *last_read - 1, NULL ) ); + } + + return count; +} + +static short const dmc_period_table [2] [16] = { + {428, 380, 340, 320, 286, 254, 226, 214, // NTSC + 190, 160, 142, 128, 106, 84, 72, 54}, + + {398, 354, 316, 298, 276, 236, 210, 198, // PAL + 176, 148, 132, 118, 98, 78, 66, 50} +}; + +inline void Nes_Dmc::reload_sample() +{ + address = 0x4000 + regs [2] * 0x40; + length_counter = regs [3] * 0x10 + 1; +} + +static int const dmc_table [128] = +{ + 0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340, + 361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664, + 683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955, + 972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217, +1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454, +1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670, +1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867, +1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048, +}; + +inline int Nes_Dmc::update_amp_nonlinear( int in ) +{ + if ( !nonlinear ) + in = dmc_table [in]; + int delta = in - last_amp; + last_amp = in; + return delta; +} + +void Nes_Dmc::write_register( int addr, int data ) +{ + if ( addr == 0 ) + { + period = dmc_period_table [pal_mode] [data & 15]; + irq_enabled = (data & 0xC0) == 0x80; // enabled only if loop disabled + irq_flag &= irq_enabled; + recalc_irq(); + } + else if ( addr == 1 ) + { + dac = data & 0x7F; + } +} + +void Nes_Dmc::start() +{ + reload_sample(); + fill_buffer(); + recalc_irq(); +} + +void Nes_Dmc::fill_buffer() +{ + if ( !buf_full && length_counter ) + { + require( apu->dmc_reader.f ); // dmc_reader must be set + buf = apu->dmc_reader.f( apu->dmc_reader.data, 0x8000u + address ); + address = (address + 1) & 0x7FFF; + buf_full = true; + if ( --length_counter == 0 ) + { + if ( regs [0] & loop_flag ) + { + reload_sample(); + } + else + { + apu->osc_enables &= ~0x10; + irq_flag = irq_enabled; + next_irq = Nes_Apu::no_irq; + apu->irq_changed(); + } + } + } +} + +void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) +{ + int delta = update_amp_nonlinear( dac ); + if ( !output ) + { + silence = true; + } + else if ( delta ) + { + output->set_modified(); + synth.offset( time, delta, output ); + } + + time += delay; + if ( time < end_time ) + { + int bits_remain = this->bits_remain; + if ( silence && !buf_full ) + { + int count = (end_time - time + period - 1) / period; + bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; + time += count * period; + } + else + { + Blip_Buffer* const output = this->output; + const int period = this->period; + int bits = this->bits; + int dac = this->dac; + if ( output ) + output->set_modified(); + + do + { + if ( !silence ) + { + int step = (bits & 1) * 4 - 2; + bits >>= 1; + if ( unsigned (dac + step) <= 0x7F ) + { + dac += step; + synth.offset_inline( time, update_amp_nonlinear( dac ), output ); + } + } + + time += period; + + if ( --bits_remain == 0 ) + { + bits_remain = 8; + if ( !buf_full ) + { + silence = true; + } + else + { + silence = false; + bits = buf; + buf_full = false; + if ( !output ) + silence = true; + fill_buffer(); + } + } + } + while ( time < end_time ); + + this->dac = dac; + this->bits = bits; + } + this->bits_remain = bits_remain; + } + delay = time - end_time; +} + +// Nes_Noise + +static short const noise_period_table [16] = { + 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, + 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 +}; + +void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) +{ + int period = noise_period_table [regs [2] & 15]; + + if ( !output ) + { + // TODO: clean up + time += delay; + delay = time + (end_time - time + period - 1) / period * period - end_time; + return; + } + + + const int volume = this->volume(); + int amp = (noise & 1) ? volume : 0; + { + int delta = update_amp( amp ); + if ( delta ) + { + output->set_modified(); + synth.offset( time, delta, output ); + } + } + + time += delay; + if ( time < end_time ) + { + const int mode_flag = 0x80; + + if ( !volume ) + { + // round to next multiple of period + time += (end_time - time + period - 1) / period * period; + + // approximate noise cycling while muted, by shuffling up noise register + // to do: precise muted noise cycling? + if ( !(regs [2] & mode_flag) ) + { + int feedback = (noise << 13) ^ (noise << 14); + noise = (feedback & 0x4000) | (noise >> 1); + } + } + else + { + Blip_Buffer* const output = this->output; + + // using resampled time avoids conversion in synth.offset() + blip_resampled_time_t rperiod = output->resampled_duration( period ); + blip_resampled_time_t rtime = output->resampled_time( time ); + + int noise = this->noise; + int delta = amp * 2 - volume; + const int tap = (regs [2] & mode_flag ? 8 : 13); + output->set_modified(); + + do + { + int feedback = (noise << tap) ^ (noise << 14); + time += period; + + if ( (noise + 1) & 2 ) + { + // bits 0 and 1 of noise differ + delta = -delta; + synth.offset_resampled( rtime, delta, output ); + } + + rtime += rperiod; + noise = (feedback & 0x4000) | (noise >> 1); + } + while ( time < end_time ); + + last_amp = (delta + volume) >> 1; + this->noise = noise; + } + } + + delay = time - end_time; +} + diff --git a/Frameworks/GME/gme/Nes_Oscs.h b/Frameworks/GME/gme/Nes_Oscs.h index 0bf922a82..e7621c3bf 100644 --- a/Frameworks/GME/gme/Nes_Oscs.h +++ b/Frameworks/GME/gme/Nes_Oscs.h @@ -1,147 +1,147 @@ -// Private oscillators used by Nes_Apu - -// Nes_Snd_Emu $vers -#ifndef NES_OSCS_H -#define NES_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Nes_Apu; - -struct Nes_Osc -{ - typedef int nes_time_t; - - unsigned char regs [4]; - bool reg_written [4]; - Blip_Buffer* output; - int length_counter;// length counter (0 if unused by oscillator) - int delay; // delay until next (potential) transition - int last_amp; // last amplitude oscillator was outputting - - void clock_length( int halt_mask ); - int period() const { - return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF); - } - void reset() { - delay = 0; - last_amp = 0; - } - int update_amp( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - return delta; - } -}; - -struct Nes_Envelope : Nes_Osc -{ - int envelope; - int env_delay; - - void clock_envelope(); - int volume() const; - void reset() { - envelope = 0; - env_delay = 0; - Nes_Osc::reset(); - } -}; - -// Nes_Square -struct Nes_Square : Nes_Envelope -{ - enum { negate_flag = 0x08 }; - enum { shift_mask = 0x07 }; - enum { phase_range = 8 }; - int phase; - int sweep_delay; - - typedef Blip_Synth_Norm Synth; - Synth const& synth; // shared between squares - - Nes_Square( Synth const* s ) : synth( *s ) { } - - void clock_sweep( int adjust ); - void run( nes_time_t, nes_time_t ); - void reset() { - sweep_delay = 0; - Nes_Envelope::reset(); - } - nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ); -}; - -// Nes_Triangle -struct Nes_Triangle : Nes_Osc -{ - enum { phase_range = 16 }; - int phase; - int linear_counter; - Blip_Synth_Fast synth; - - int calc_amp() const; - void run( nes_time_t, nes_time_t ); - void clock_linear_counter(); - void reset() { - linear_counter = 0; - phase = 1; - Nes_Osc::reset(); - } - nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, - nes_time_t timer_period ); -}; - -// Nes_Noise -struct Nes_Noise : Nes_Envelope -{ - int noise; - Blip_Synth_Fast synth; - - void run( nes_time_t, nes_time_t ); - void reset() { - noise = 1 << 14; - Nes_Envelope::reset(); - } -}; - -// Nes_Dmc -struct Nes_Dmc : Nes_Osc -{ - int address; // address of next byte to read - int period; - //int length_counter; // bytes remaining to play (already defined in Nes_Osc) - int buf; - int bits_remain; - int bits; - bool buf_full; - bool silence; - - enum { loop_flag = 0x40 }; - - int dac; - - nes_time_t next_irq; - bool irq_enabled; - bool irq_flag; - bool pal_mode; - bool nonlinear; - - Nes_Apu* apu; - - Blip_Synth_Fast synth; - - int update_amp_nonlinear( int dac_in ); - void start(); - void write_register( int, int ); - void run( nes_time_t, nes_time_t ); - void recalc_irq(); - void fill_buffer(); - void reload_sample(); - void reset(); - int count_reads( nes_time_t, nes_time_t* ) const; - nes_time_t next_read_time() const; -}; - -#endif +// Private oscillators used by Nes_Apu + +// Nes_Snd_Emu $vers +#ifndef NES_OSCS_H +#define NES_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Nes_Apu; + +struct Nes_Osc +{ + typedef int nes_time_t; + + unsigned char regs [4]; + bool reg_written [4]; + Blip_Buffer* output; + int length_counter;// length counter (0 if unused by oscillator) + int delay; // delay until next (potential) transition + int last_amp; // last amplitude oscillator was outputting + + void clock_length( int halt_mask ); + int period() const { + return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF); + } + void reset() { + delay = 0; + last_amp = 0; + } + int update_amp( int amp ) { + int delta = amp - last_amp; + last_amp = amp; + return delta; + } +}; + +struct Nes_Envelope : Nes_Osc +{ + int envelope; + int env_delay; + + void clock_envelope(); + int volume() const; + void reset() { + envelope = 0; + env_delay = 0; + Nes_Osc::reset(); + } +}; + +// Nes_Square +struct Nes_Square : Nes_Envelope +{ + enum { negate_flag = 0x08 }; + enum { shift_mask = 0x07 }; + enum { phase_range = 8 }; + int phase; + int sweep_delay; + + typedef Blip_Synth_Norm Synth; + Synth const& synth; // shared between squares + + Nes_Square( Synth const* s ) : synth( *s ) { } + + void clock_sweep( int adjust ); + void run( nes_time_t, nes_time_t ); + void reset() { + sweep_delay = 0; + Nes_Envelope::reset(); + } + nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ); +}; + +// Nes_Triangle +struct Nes_Triangle : Nes_Osc +{ + enum { phase_range = 16 }; + int phase; + int linear_counter; + Blip_Synth_Fast synth; + + int calc_amp() const; + void run( nes_time_t, nes_time_t ); + void clock_linear_counter(); + void reset() { + linear_counter = 0; + phase = 1; + Nes_Osc::reset(); + } + nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time, + nes_time_t timer_period ); +}; + +// Nes_Noise +struct Nes_Noise : Nes_Envelope +{ + int noise; + Blip_Synth_Fast synth; + + void run( nes_time_t, nes_time_t ); + void reset() { + noise = 1 << 14; + Nes_Envelope::reset(); + } +}; + +// Nes_Dmc +struct Nes_Dmc : Nes_Osc +{ + int address; // address of next byte to read + int period; + //int length_counter; // bytes remaining to play (already defined in Nes_Osc) + int buf; + int bits_remain; + int bits; + bool buf_full; + bool silence; + + enum { loop_flag = 0x40 }; + + int dac; + + nes_time_t next_irq; + bool irq_enabled; + bool irq_flag; + bool pal_mode; + bool nonlinear; + + Nes_Apu* apu; + + Blip_Synth_Fast synth; + + int update_amp_nonlinear( int dac_in ); + void start(); + void write_register( int, int ); + void run( nes_time_t, nes_time_t ); + void recalc_irq(); + void fill_buffer(); + void reload_sample(); + void reset(); + int count_reads( nes_time_t, nes_time_t* ) const; + nes_time_t next_read_time() const; +}; + +#endif diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp index 990e60640..0807fb6f8 100644 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp @@ -1,216 +1,216 @@ -// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ - -#include "Nes_Vrc6_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void Nes_Vrc6_Apu::set_output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; ++i ) - set_output( i, buf ); -} - -void Nes_Vrc6_Apu::reset() -{ - last_time = 0; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc& osc = oscs [i]; - for ( int j = 0; j < reg_count; j++ ) - osc.regs [j] = 0; - osc.delay = 0; - osc.last_amp = 0; - osc.phase = 1; - osc.amp = 0; - } -} - -Nes_Vrc6_Apu::Nes_Vrc6_Apu() -{ - set_output( NULL ); - volume( 1.0 ); - reset(); -} - -void Nes_Vrc6_Apu::run_until( blip_time_t time ) -{ - require( time >= last_time ); - run_square( oscs [0], time ); - run_square( oscs [1], time ); - run_saw( time ); - last_time = time; -} - -void Nes_Vrc6_Apu::write_osc( blip_time_t time, int osc_index, int reg, int data ) -{ - require( (unsigned) osc_index < osc_count ); - require( (unsigned) reg < reg_count ); - - run_until( time ); - oscs [osc_index].regs [reg] = data; -} - -void Nes_Vrc6_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const -{ - assert( sizeof (vrc6_apu_state_t) == 20 ); - out->saw_amp = oscs [2].amp; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc const& osc = oscs [i]; - for ( int r = 0; r < reg_count; r++ ) - out->regs [i] [r] = osc.regs [r]; - - out->delays [i] = osc.delay; - out->phases [i] = osc.phase; - } -} - -void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in ) -{ - reset(); - oscs [2].amp = in.saw_amp; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc& osc = oscs [i]; - for ( int r = 0; r < reg_count; r++ ) - osc.regs [r] = in.regs [i] [r]; - - osc.delay = in.delays [i]; - osc.phase = in.phases [i]; - } - if ( !oscs [2].phase ) - oscs [2].phase = 1; -} - -void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) -{ - Blip_Buffer* output = osc.output; - if ( !output ) - return; - - int volume = osc.regs [0] & 15; - if ( !(osc.regs [2] & 0x80) ) - volume = 0; - - int gate = osc.regs [0] & 0x80; - int duty = ((osc.regs [0] >> 4) & 7) + 1; - int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; - blip_time_t time = last_time; - if ( delta ) - { - osc.last_amp += delta; - output->set_modified(); - square_synth.offset( time, delta, output ); - } - - time += osc.delay; - osc.delay = 0; - int period = osc.period(); - if ( volume && !gate && period > 4 ) - { - if ( time < end_time ) - { - int phase = osc.phase; - output->set_modified(); - - do - { - phase++; - if ( phase == 16 ) - { - phase = 0; - osc.last_amp = volume; - square_synth.offset( time, volume, output ); - } - if ( phase == duty ) - { - osc.last_amp = 0; - square_synth.offset( time, -volume, output ); - } - time += period; - } - while ( time < end_time ); - - osc.phase = phase; - } - osc.delay = time - end_time; - } -} - -void Nes_Vrc6_Apu::run_saw( blip_time_t end_time ) -{ - Vrc6_Osc& osc = oscs [2]; - Blip_Buffer* output = osc.output; - if ( !output ) - return; - output->set_modified(); - - int amp = osc.amp; - int amp_step = osc.regs [0] & 0x3F; - blip_time_t time = last_time; - int last_amp = osc.last_amp; - if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) ) - { - osc.delay = 0; - int delta = (amp >> 3) - last_amp; - last_amp = amp >> 3; - saw_synth.offset( time, delta, output ); - } - else - { - time += osc.delay; - if ( time < end_time ) - { - int period = osc.period() * 2; - int phase = osc.phase; - - do - { - if ( --phase == 0 ) - { - phase = 7; - amp = 0; - } - - int delta = (amp >> 3) - last_amp; - if ( delta ) - { - last_amp = amp >> 3; - saw_synth.offset( time, delta, output ); - } - - time += period; - amp = (amp + amp_step) & 0xFF; - } - while ( time < end_time ); - - osc.phase = phase; - osc.amp = amp; - } - - osc.delay = time - end_time; - } - - osc.last_amp = last_amp; -} - +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Nes_Vrc6_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Nes_Vrc6_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +void Nes_Vrc6_Apu::reset() +{ + last_time = 0; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc& osc = oscs [i]; + for ( int j = 0; j < reg_count; j++ ) + osc.regs [j] = 0; + osc.delay = 0; + osc.last_amp = 0; + osc.phase = 1; + osc.amp = 0; + } +} + +Nes_Vrc6_Apu::Nes_Vrc6_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +void Nes_Vrc6_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); + run_square( oscs [0], time ); + run_square( oscs [1], time ); + run_saw( time ); + last_time = time; +} + +void Nes_Vrc6_Apu::write_osc( blip_time_t time, int osc_index, int reg, int data ) +{ + require( (unsigned) osc_index < osc_count ); + require( (unsigned) reg < reg_count ); + + run_until( time ); + oscs [osc_index].regs [reg] = data; +} + +void Nes_Vrc6_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const +{ + assert( sizeof (vrc6_apu_state_t) == 20 ); + out->saw_amp = oscs [2].amp; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc const& osc = oscs [i]; + for ( int r = 0; r < reg_count; r++ ) + out->regs [i] [r] = osc.regs [r]; + + out->delays [i] = osc.delay; + out->phases [i] = osc.phase; + } +} + +void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in ) +{ + reset(); + oscs [2].amp = in.saw_amp; + for ( int i = 0; i < osc_count; i++ ) + { + Vrc6_Osc& osc = oscs [i]; + for ( int r = 0; r < reg_count; r++ ) + osc.regs [r] = in.regs [i] [r]; + + osc.delay = in.delays [i]; + osc.phase = in.phases [i]; + } + if ( !oscs [2].phase ) + oscs [2].phase = 1; +} + +void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) +{ + Blip_Buffer* output = osc.output; + if ( !output ) + return; + + int volume = osc.regs [0] & 15; + if ( !(osc.regs [2] & 0x80) ) + volume = 0; + + int gate = osc.regs [0] & 0x80; + int duty = ((osc.regs [0] >> 4) & 7) + 1; + int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; + blip_time_t time = last_time; + if ( delta ) + { + osc.last_amp += delta; + output->set_modified(); + square_synth.offset( time, delta, output ); + } + + time += osc.delay; + osc.delay = 0; + int period = osc.period(); + if ( volume && !gate && period > 4 ) + { + if ( time < end_time ) + { + int phase = osc.phase; + output->set_modified(); + + do + { + phase++; + if ( phase == 16 ) + { + phase = 0; + osc.last_amp = volume; + square_synth.offset( time, volume, output ); + } + if ( phase == duty ) + { + osc.last_amp = 0; + square_synth.offset( time, -volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.phase = phase; + } + osc.delay = time - end_time; + } +} + +void Nes_Vrc6_Apu::run_saw( blip_time_t end_time ) +{ + Vrc6_Osc& osc = oscs [2]; + Blip_Buffer* output = osc.output; + if ( !output ) + return; + output->set_modified(); + + int amp = osc.amp; + int amp_step = osc.regs [0] & 0x3F; + blip_time_t time = last_time; + int last_amp = osc.last_amp; + if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) ) + { + osc.delay = 0; + int delta = (amp >> 3) - last_amp; + last_amp = amp >> 3; + saw_synth.offset( time, delta, output ); + } + else + { + time += osc.delay; + if ( time < end_time ) + { + int period = osc.period() * 2; + int phase = osc.phase; + + do + { + if ( --phase == 0 ) + { + phase = 7; + amp = 0; + } + + int delta = (amp >> 3) - last_amp; + if ( delta ) + { + last_amp = amp >> 3; + saw_synth.offset( time, delta, output ); + } + + time += period; + amp = (amp + amp_step) & 0xFF; + } + while ( time < end_time ); + + osc.phase = phase; + osc.amp = amp; + } + + osc.delay = time - end_time; + } + + osc.last_amp = last_amp; +} + diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.h b/Frameworks/GME/gme/Nes_Vrc6_Apu.h index 56af076f4..e5b757768 100644 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.h +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.h @@ -1,95 +1,95 @@ -// Konami VRC6 sound chip emulator - -// Nes_Snd_Emu $vers -#ifndef NES_VRC6_APU_H -#define NES_VRC6_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct vrc6_apu_state_t; - -class Nes_Vrc6_Apu { -public: - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void set_output( Blip_Buffer* ); - enum { osc_count = 3 }; - void set_output( int index, Blip_Buffer* ); - void end_frame( blip_time_t ); - void save_state( vrc6_apu_state_t* ) const; - void load_state( vrc6_apu_state_t const& ); - - // Oscillator 0 write-only registers are at $9000-$9002 - // Oscillator 1 write-only registers are at $A000-$A002 - // Oscillator 2 write-only registers are at $B000-$B002 - enum { reg_count = 3 }; - enum { base_addr = 0x9000 }; - enum { addr_step = 0x1000 }; - void write_osc( blip_time_t, int osc, int reg, int data ); - -public: - Nes_Vrc6_Apu(); - BLARGG_DISABLE_NOTHROW -private: - // noncopyable - Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); - Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); - - struct Vrc6_Osc - { - BOOST::uint8_t regs [3]; - Blip_Buffer* output; - int delay; - int last_amp; - int phase; - int amp; // only used by saw - - int period() const - { - return (regs [2] & 0x0F) * 0x100 + regs [1] + 1; - } - }; - - Vrc6_Osc oscs [osc_count]; - blip_time_t last_time; - - Blip_Synth_Fast saw_synth; - Blip_Synth_Norm square_synth; - - void run_until( blip_time_t ); - void run_square( Vrc6_Osc& osc, blip_time_t ); - void run_saw( blip_time_t ); -}; - -struct vrc6_apu_state_t -{ - BOOST::uint8_t regs [3] [3]; - BOOST::uint8_t saw_amp; - BOOST::uint16_t delays [3]; - BOOST::uint8_t phases [3]; - BOOST::uint8_t unused; -}; - -inline void Nes_Vrc6_Apu::set_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Vrc6_Apu::volume( double v ) -{ - double const factor = 0.0967 * 2; - saw_synth.volume( factor / 31 * v ); - square_synth.volume( factor * 0.5 / 15 * v ); -} - -inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq ) -{ - saw_synth.treble_eq( eq ); - square_synth.treble_eq( eq ); -} - -#endif +// Konami VRC6 sound chip emulator + +// Nes_Snd_Emu $vers +#ifndef NES_VRC6_APU_H +#define NES_VRC6_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct vrc6_apu_state_t; + +class Nes_Vrc6_Apu { +public: + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void set_output( Blip_Buffer* ); + enum { osc_count = 3 }; + void set_output( int index, Blip_Buffer* ); + void end_frame( blip_time_t ); + void save_state( vrc6_apu_state_t* ) const; + void load_state( vrc6_apu_state_t const& ); + + // Oscillator 0 write-only registers are at $9000-$9002 + // Oscillator 1 write-only registers are at $A000-$A002 + // Oscillator 2 write-only registers are at $B000-$B002 + enum { reg_count = 3 }; + enum { base_addr = 0x9000 }; + enum { addr_step = 0x1000 }; + void write_osc( blip_time_t, int osc, int reg, int data ); + +public: + Nes_Vrc6_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); + Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); + + struct Vrc6_Osc + { + BOOST::uint8_t regs [3]; + Blip_Buffer* output; + int delay; + int last_amp; + int phase; + int amp; // only used by saw + + int period() const + { + return (regs [2] & 0x0F) * 0x100 + regs [1] + 1; + } + }; + + Vrc6_Osc oscs [osc_count]; + blip_time_t last_time; + + Blip_Synth_Fast saw_synth; + Blip_Synth_Norm square_synth; + + void run_until( blip_time_t ); + void run_square( Vrc6_Osc& osc, blip_time_t ); + void run_saw( blip_time_t ); +}; + +struct vrc6_apu_state_t +{ + BOOST::uint8_t regs [3] [3]; + BOOST::uint8_t saw_amp; + BOOST::uint16_t delays [3]; + BOOST::uint8_t phases [3]; + BOOST::uint8_t unused; +}; + +inline void Nes_Vrc6_Apu::set_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Vrc6_Apu::volume( double v ) +{ + double const factor = 0.0967 * 2; + saw_synth.volume( factor / 31 * v ); + square_synth.volume( factor * 0.5 / 15 * v ); +} + +inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq ) +{ + saw_synth.treble_eq( eq ); + square_synth.treble_eq( eq ); +} + +#endif diff --git a/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp index 033061d6a..1c821c427 100644 --- a/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp @@ -1,10 +1,18 @@ #include "Nes_Vrc7_Apu.h" -#include "ym2413.h" +extern "C" { +#include "../vgmplay/chips/emu2413.h" +} + #include #include "blargg_source.h" +static unsigned char vrc7_inst[(16 + 3) * 8] = +{ +#include "../vgmplay/chips/vrc7tone.h" +}; + int const period = 36; // NES CPU clocks per FM clock Nes_Vrc7_Apu::Nes_Vrc7_Apu() @@ -14,8 +22,10 @@ Nes_Vrc7_Apu::Nes_Vrc7_Apu() blargg_err_t Nes_Vrc7_Apu::init() { - CHECK_ALLOC( opll = ym2413_init( 3579545, 3579545 / 72, 1 ) ); - + CHECK_ALLOC( opll = OPLL_new( 3579545, 3579545 / 72 ) ); + OPLL_SetChipMode((OPLL *) opll, 1); + OPLL_setPatch((OPLL *) opll, vrc7_inst); + set_output( 0 ); volume( 1.0 ); reset(); @@ -25,7 +35,7 @@ blargg_err_t Nes_Vrc7_Apu::init() Nes_Vrc7_Apu::~Nes_Vrc7_Apu() { if ( opll ) - ym2413_shutdown( opll ); + OPLL_delete( (OPLL *) opll ); } void Nes_Vrc7_Apu::set_output( Blip_Buffer* buf ) @@ -46,7 +56,7 @@ void Nes_Vrc7_Apu::output_changed() break; } } - + if ( mono.output ) { for ( int i = osc_count; --i; ) @@ -62,7 +72,7 @@ void Nes_Vrc7_Apu::reset() addr = 0; next_time = 0; mono.last_amp = 0; - + for ( int i = osc_count; --i >= 0; ) { Vrc7_Osc& osc = oscs [i]; @@ -71,7 +81,7 @@ void Nes_Vrc7_Apu::reset() osc.regs [j] = 0; } - ym2413_reset_chip( opll ); + OPLL_reset( (OPLL *) opll ); } void Nes_Vrc7_Apu::write_reg( int data ) @@ -85,21 +95,23 @@ void Nes_Vrc7_Apu::write_data( blip_time_t time, int data ) int chan = addr & 15; if ( (unsigned) type < 3 && chan < osc_count ) oscs [chan].regs [type] = data; - + if ( addr < 0x08 ) + inst [addr] = data; + if ( time > next_time ) run_until( time ); - ym2413_write( opll, 0, addr ); - ym2413_write( opll, 1, data ); + OPLL_writeIO( (OPLL *) opll, 0, addr ); + OPLL_writeIO( (OPLL *) opll, 1, data ); } void Nes_Vrc7_Apu::end_frame( blip_time_t time ) { if ( time > next_time ) run_until( time ); - + next_time -= time; assert( next_time >= 0 ); - + for ( int i = osc_count; --i >= 0; ) { Blip_Buffer* output = oscs [i].output; @@ -117,13 +129,13 @@ void Nes_Vrc7_Apu::save_snapshot( vrc7_snapshot_t* out ) const for ( int j = 0; j < 3; ++j ) out->regs [i] [j] = oscs [i].regs [j]; } - memcpy( out->inst, ym2413_get_inst0( opll ), 8 ); + memcpy( out->inst, inst, 8 ); } void Nes_Vrc7_Apu::load_snapshot( vrc7_snapshot_t const& in ) { assert( offsetof (vrc7_snapshot_t,delay) == 28 - 1 ); - + reset(); next_time = in.delay; write_reg( in.latch ); @@ -134,18 +146,19 @@ void Nes_Vrc7_Apu::load_snapshot( vrc7_snapshot_t const& in ) oscs [i].regs [j] = in.regs [i] [j]; } + memcpy( inst, in.inst, 8 ); for ( i = 0; i < 8; ++i ) { - ym2413_write( opll, 0, i ); - ym2413_write( opll, 1, in.inst [i] ); + OPLL_writeIO( (OPLL *) opll, 0, i ); + OPLL_writeIO( (OPLL *) opll, 1, in.inst [i] ); } for ( i = 0; i < 3; ++i ) { for ( int j = 0; j < 6; ++j ) { - ym2413_write( opll, 0, 0x10 + i * 0x10 + j ); - ym2413_write( opll, 1, oscs [j].regs [i] ); + OPLL_writeIO( (OPLL *) opll, 0, 0x10 + i * 0x10 + j ); + OPLL_writeIO( (OPLL *) opll, 1, oscs [j].regs [i] ); } } } @@ -157,16 +170,15 @@ void Nes_Vrc7_Apu::run_until( blip_time_t end_time ) blip_time_t time = next_time; void* opll = this->opll; // cache Blip_Buffer* const mono_output = mono.output; + e_int32 buffer [2]; + e_int32* buffers[2] = {&buffer[0], &buffer[1]}; if ( mono_output ) { // optimal case do { - ym2413_advance_lfo( opll ); - int amp = 0; - for ( int i = 0; i < osc_count; i++ ) - amp += ym2413_calcch( opll, i ); - ym2413_advance( opll ); + OPLL_calc_stereo( (OPLL *) opll, buffers, 1, -1 ); + int amp = buffer [0] + buffer [1]; int delta = amp - mono.last_amp; if ( delta ) { @@ -182,13 +194,14 @@ void Nes_Vrc7_Apu::run_until( blip_time_t end_time ) mono.last_amp = 0; do { - ym2413_advance_lfo( opll ); + OPLL_advance( (OPLL *) opll ); for ( int i = 0; i < osc_count; ++i ) { Vrc7_Osc& osc = oscs [i]; if ( osc.output ) { - int amp = ym2413_calcch( opll, i ); + OPLL_calc_stereo( (OPLL *) opll, buffers, 1, i ); + int amp = buffer [0] + buffer [1]; int delta = amp - osc.last_amp; if ( delta ) { @@ -197,7 +210,6 @@ void Nes_Vrc7_Apu::run_until( blip_time_t end_time ) } } } - ym2413_advance( opll ); time += period; } while ( time < end_time ); diff --git a/Frameworks/GME/gme/Nes_Vrc7_Apu.h b/Frameworks/GME/gme/Nes_Vrc7_Apu.h index 60ea16312..9ecabae0d 100644 --- a/Frameworks/GME/gme/Nes_Vrc7_Apu.h +++ b/Frameworks/GME/gme/Nes_Vrc7_Apu.h @@ -1,80 +1,82 @@ -// Konami VRC7 sound chip emulator - -#ifndef NES_VRC7_APU_H -#define NES_VRC7_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct vrc7_snapshot_t; - -class Nes_Vrc7_Apu { -public: - blargg_err_t init(); - - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void set_output( Blip_Buffer* ); - enum { osc_count = 6 }; - void set_output( int index, Blip_Buffer* ); - void end_frame( blip_time_t ); - void save_snapshot( vrc7_snapshot_t* ) const; - void load_snapshot( vrc7_snapshot_t const& ); - - void write_reg( int reg ); - void write_data( blip_time_t, int data ); - -public: - Nes_Vrc7_Apu(); - ~Nes_Vrc7_Apu(); - BLARGG_DISABLE_NOTHROW -private: - // noncopyable - Nes_Vrc7_Apu( const Nes_Vrc7_Apu& ); - Nes_Vrc7_Apu& operator = ( const Nes_Vrc7_Apu& ); - - struct Vrc7_Osc - { - BOOST::uint8_t regs [3]; - Blip_Buffer* output; - int last_amp; - }; - - Vrc7_Osc oscs [osc_count]; - void* opll; - int addr; - blip_time_t next_time; - struct { - Blip_Buffer* output; - int last_amp; - } mono; - - Blip_Synth_Fast synth; - - void run_until( blip_time_t ); - void output_changed(); -}; - -struct vrc7_snapshot_t -{ - BOOST::uint8_t latch; - BOOST::uint8_t inst [8]; - BOOST::uint8_t regs [6] [3]; - BOOST::uint8_t delay; -}; - -inline void Nes_Vrc7_Apu::set_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; - output_changed(); -} - -// DB2LIN_AMP_BITS == 11, * 2 -inline void Nes_Vrc7_Apu::volume( double v ) { synth.volume( 1.0 / 3 / 4096 * v ); } - -inline void Nes_Vrc7_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - -#endif +// Konami VRC7 sound chip emulator + +#ifndef NES_VRC7_APU_H +#define NES_VRC7_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct vrc7_snapshot_t; + +class Nes_Vrc7_Apu { +public: + blargg_err_t init(); + + // See Nes_Apu.h for reference + void reset(); + void volume( double ); + void treble_eq( blip_eq_t const& ); + void set_output( Blip_Buffer* ); + enum { osc_count = 6 }; + void set_output( int index, Blip_Buffer* ); + void end_frame( blip_time_t ); + void save_snapshot( vrc7_snapshot_t* ) const; + void load_snapshot( vrc7_snapshot_t const& ); + + void write_reg( int reg ); + void write_data( blip_time_t, int data ); + +public: + Nes_Vrc7_Apu(); + ~Nes_Vrc7_Apu(); + BLARGG_DISABLE_NOTHROW +private: + // noncopyable + Nes_Vrc7_Apu( const Nes_Vrc7_Apu& ); + Nes_Vrc7_Apu& operator = ( const Nes_Vrc7_Apu& ); + + struct Vrc7_Osc + { + BOOST::uint8_t regs [3]; + Blip_Buffer* output; + int last_amp; + }; + + Vrc7_Osc oscs [osc_count]; + BOOST::uint8_t kon; + BOOST::uint8_t inst [8]; + void* opll; + int addr; + blip_time_t next_time; + struct { + Blip_Buffer* output; + int last_amp; + } mono; + + Blip_Synth_Fast synth; + + void run_until( blip_time_t ); + void output_changed(); +}; + +struct vrc7_snapshot_t +{ + BOOST::uint8_t latch; + BOOST::uint8_t inst [8]; + BOOST::uint8_t regs [6] [3]; + BOOST::uint8_t delay; +}; + +inline void Nes_Vrc7_Apu::set_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; + output_changed(); +} + +// DB2LIN_AMP_BITS == 11, * 2 +inline void Nes_Vrc7_Apu::volume( double v ) { synth.volume( 1.0 / 3 / 4096 * v ); } + +inline void Nes_Vrc7_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +#endif diff --git a/Frameworks/GME/gme/Nsf_Core.cpp b/Frameworks/GME/gme/Nsf_Core.cpp index 4fa732bf6..52d6d0728 100644 --- a/Frameworks/GME/gme/Nsf_Core.cpp +++ b/Frameworks/GME/gme/Nsf_Core.cpp @@ -1,302 +1,302 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Nsf_Core.h" - -#include "blargg_endian.h" - -#if !NSF_EMU_APU_ONLY - #include "Nes_Namco_Apu.h" - #include "Nes_Vrc6_Apu.h" - #include "Nes_Fme7_Apu.h" - #include "Nes_Fds_Apu.h" - #include "Nes_Mmc5_Apu.h" - #include "Nes_Vrc7_Apu.h" -#endif - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Nsf_Core::Nsf_Core() -{ - fds = NULL; - fme7 = NULL; - mmc5 = NULL; - namco = NULL; - vrc6 = NULL; - vrc7 = NULL; -} - -Nsf_Core::~Nsf_Core() -{ - unload(); -} - -void Nsf_Core::unload() -{ -#if !NSF_EMU_APU_ONLY - delete fds; - fds = NULL; - - delete fme7; - fme7 = NULL; - - delete namco; - namco = NULL; - - delete mmc5; - mmc5 = NULL; - - delete vrc6; - vrc6 = NULL; - - delete vrc7; - vrc7 = NULL; -#endif - - Nsf_Impl::unload(); -} - -void Nsf_Core::set_tempo( double t ) -{ - set_play_period( (int) (header().play_period() / t) ); - nes_apu()->set_tempo( t ); -#if !NSF_EMU_APU_ONLY - if ( fds ) - fds->set_tempo( t ); -#endif -} - -blargg_err_t Nsf_Core::post_load() -{ - int chip_flags = header().chip_flags; - - #if !NSF_EMU_APU_ONLY - if ( chip_flags & header_t::fds_mask ) - CHECK_ALLOC( fds = BLARGG_NEW Nes_Fds_Apu ); - - if ( chip_flags & header_t::fme7_mask ) - CHECK_ALLOC( fme7 = BLARGG_NEW Nes_Fme7_Apu ); - - if ( chip_flags & header_t::mmc5_mask ) - CHECK_ALLOC( mmc5 = BLARGG_NEW Nes_Mmc5_Apu ); - - if ( chip_flags & header_t::namco_mask ) - CHECK_ALLOC( namco = BLARGG_NEW Nes_Namco_Apu ); - - if ( chip_flags & header_t::vrc6_mask ) - CHECK_ALLOC( vrc6 = BLARGG_NEW Nes_Vrc6_Apu ); - - if ( chip_flags & header_t::vrc7_mask ) - { - #if NSF_EMU_NO_VRC7 - chip_flags = ~chips_mask; // give warning rather than error - #else - CHECK_ALLOC( vrc7 = BLARGG_NEW Nes_Vrc7_Apu ); - RETURN_ERR( vrc7->init() ); - #endif - } - #endif - - set_tempo( 1.0 ); - - if ( chip_flags & ~chips_mask ) - set_warning( "Uses unsupported audio expansion hardware" ); - - return Nsf_Impl::post_load(); -} - -int Nsf_Core::cpu_read( addr_t addr ) -{ - #if !NSF_EMU_APU_ONLY - { - if ( addr == Nes_Namco_Apu::data_reg_addr && namco ) - return namco->read_data(); - - if ( (unsigned) (addr - Nes_Fds_Apu::io_addr) < Nes_Fds_Apu::io_size && fds ) - return fds->read( time(), addr ); - - int i = addr - 0x5C00; - if ( (unsigned) i < mmc5->exram_size && mmc5 ) - return mmc5->exram [i]; - - int m = addr - 0x5205; - if ( (unsigned) m < 2 && mmc5 ) - return (mmc5_mul [0] * mmc5_mul [1]) >> (m * 8) & 0xFF; - } - #endif - - return Nsf_Impl::cpu_read( addr ); -} - -int Nsf_Core::unmapped_read( addr_t addr ) -{ - switch ( addr ) - { - case 0x2002: - case 0x4016: - case 0x4017: - return addr >> 8; - } - - return Nsf_Impl::unmapped_read( addr ); -} - -void Nsf_Core::cpu_write( addr_t addr, int data ) -{ - #if !NSF_EMU_APU_ONLY - { - if ( (unsigned) (addr - fds->io_addr) < fds->io_size && fds ) - { - fds->write( time(), addr, data ); - return; - } - - if ( namco ) - { - if ( addr == namco->addr_reg_addr ) - { - namco->write_addr( data ); - return; - } - - if ( addr == namco->data_reg_addr ) - { - namco->write_data( time(), data ); - return; - } - } - - if ( vrc6 ) - { - int reg = addr & (vrc6->addr_step - 1); - int osc = (unsigned) (addr - vrc6->base_addr) / vrc6->addr_step; - if ( (unsigned) osc < vrc6->osc_count && (unsigned) reg < vrc6->reg_count ) - { - vrc6->write_osc( time(), osc, reg, data ); - return; - } - } - - if ( addr >= fme7->latch_addr && fme7 ) - { - switch ( addr & fme7->addr_mask ) - { - case Nes_Fme7_Apu::latch_addr: - fme7->write_latch( data ); - return; - - case Nes_Fme7_Apu::data_addr: - fme7->write_data( time(), data ); - return; - } - } - - if ( mmc5 ) - { - if ( (unsigned) (addr - mmc5->regs_addr) < mmc5->regs_size ) - { - mmc5->write_register( time(), addr, data ); - return; - } - - int m = addr - 0x5205; - if ( (unsigned) m < 2 ) - { - mmc5_mul [m] = data; - return; - } - - int i = addr - 0x5C00; - if ( (unsigned) i < mmc5->exram_size ) - { - mmc5->exram [i] = data; - return; - } - } - - if ( vrc7 ) - { - if ( addr == 0x9010 ) - { - vrc7->write_reg( data ); - return; - } - - if ( (unsigned) (addr - 0x9028) <= 0x08 ) - { - vrc7->write_data( time(), data ); - return; - } - } - } - #endif - - return Nsf_Impl::cpu_write( addr, data ); -} - -void Nsf_Core::unmapped_write( addr_t addr, int data ) -{ - switch ( addr ) - { - case 0x8000: // some write to $8000 and $8001 repeatedly - case 0x8001: - case 0x4800: // probably namco sound mistakenly turned on in MCK - case 0xF800: - case 0xFFF8: // memory mapper? - return; - } - - if ( mmc5 && addr == 0x5115 ) return; - - // FDS memory - if ( fds && (unsigned) (addr - 0x8000) < 0x6000 ) return; - - Nsf_Impl::unmapped_write( addr, data ); -} - -blargg_err_t Nsf_Core::start_track( int track ) -{ - #if !NSF_EMU_APU_ONLY - if ( mmc5 ) - { - mmc5_mul [0] = 0; - mmc5_mul [1] = 0; - memset( mmc5->exram, 0, mmc5->exram_size ); - } - #endif - - #if !NSF_EMU_APU_ONLY - if ( fds ) fds ->reset(); - if ( fme7 ) fme7 ->reset(); - if ( mmc5 ) mmc5 ->reset(); - if ( namco ) namco->reset(); - if ( vrc6 ) vrc6 ->reset(); - if ( vrc7 ) vrc7 ->reset(); - #endif - - return Nsf_Impl::start_track( track ); -} - -void Nsf_Core::end_frame( time_t end ) -{ - Nsf_Impl::end_frame( end ); - - #if !NSF_EMU_APU_ONLY - if ( fds ) fds ->end_frame( end ); - if ( fme7 ) fme7 ->end_frame( end ); - if ( mmc5 ) mmc5 ->end_frame( end ); - if ( namco ) namco->end_frame( end ); - if ( vrc6 ) vrc6 ->end_frame( end ); - if ( vrc7 ) vrc7 ->end_frame( end ); - #endif -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nsf_Core.h" + +#include "blargg_endian.h" + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" + #include "Nes_Vrc6_Apu.h" + #include "Nes_Fme7_Apu.h" + #include "Nes_Fds_Apu.h" + #include "Nes_Mmc5_Apu.h" + #include "Nes_Vrc7_Apu.h" +#endif + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Nsf_Core::Nsf_Core() +{ + fds = NULL; + fme7 = NULL; + mmc5 = NULL; + namco = NULL; + vrc6 = NULL; + vrc7 = NULL; +} + +Nsf_Core::~Nsf_Core() +{ + unload(); +} + +void Nsf_Core::unload() +{ +#if !NSF_EMU_APU_ONLY + delete fds; + fds = NULL; + + delete fme7; + fme7 = NULL; + + delete namco; + namco = NULL; + + delete mmc5; + mmc5 = NULL; + + delete vrc6; + vrc6 = NULL; + + delete vrc7; + vrc7 = NULL; +#endif + + Nsf_Impl::unload(); +} + +void Nsf_Core::set_tempo( double t ) +{ + set_play_period( (int) (header().play_period() / t) ); + nes_apu()->set_tempo( t ); +#if !NSF_EMU_APU_ONLY + if ( fds ) + fds->set_tempo( t ); +#endif +} + +blargg_err_t Nsf_Core::post_load() +{ + int chip_flags = header().chip_flags; + + #if !NSF_EMU_APU_ONLY + if ( chip_flags & header_t::fds_mask ) + CHECK_ALLOC( fds = BLARGG_NEW Nes_Fds_Apu ); + + if ( chip_flags & header_t::fme7_mask ) + CHECK_ALLOC( fme7 = BLARGG_NEW Nes_Fme7_Apu ); + + if ( chip_flags & header_t::mmc5_mask ) + CHECK_ALLOC( mmc5 = BLARGG_NEW Nes_Mmc5_Apu ); + + if ( chip_flags & header_t::namco_mask ) + CHECK_ALLOC( namco = BLARGG_NEW Nes_Namco_Apu ); + + if ( chip_flags & header_t::vrc6_mask ) + CHECK_ALLOC( vrc6 = BLARGG_NEW Nes_Vrc6_Apu ); + + if ( chip_flags & header_t::vrc7_mask ) + { + #if NSF_EMU_NO_VRC7 + chip_flags = ~chips_mask; // give warning rather than error + #else + CHECK_ALLOC( vrc7 = BLARGG_NEW Nes_Vrc7_Apu ); + RETURN_ERR( vrc7->init() ); + #endif + } + #endif + + set_tempo( 1.0 ); + + if ( chip_flags & ~chips_mask ) + set_warning( "Uses unsupported audio expansion hardware" ); + + return Nsf_Impl::post_load(); +} + +int Nsf_Core::cpu_read( addr_t addr ) +{ + #if !NSF_EMU_APU_ONLY + { + if ( addr == Nes_Namco_Apu::data_reg_addr && namco ) + return namco->read_data(); + + if ( (unsigned) (addr - Nes_Fds_Apu::io_addr) < Nes_Fds_Apu::io_size && fds ) + return fds->read( time(), addr ); + + int i = addr - 0x5C00; + if ( (unsigned) i < mmc5->exram_size && mmc5 ) + return mmc5->exram [i]; + + int m = addr - 0x5205; + if ( (unsigned) m < 2 && mmc5 ) + return (mmc5_mul [0] * mmc5_mul [1]) >> (m * 8) & 0xFF; + } + #endif + + return Nsf_Impl::cpu_read( addr ); +} + +int Nsf_Core::unmapped_read( addr_t addr ) +{ + switch ( addr ) + { + case 0x2002: + case 0x4016: + case 0x4017: + return addr >> 8; + } + + return Nsf_Impl::unmapped_read( addr ); +} + +void Nsf_Core::cpu_write( addr_t addr, int data ) +{ + #if !NSF_EMU_APU_ONLY + { + if ( (unsigned) (addr - fds->io_addr) < fds->io_size && fds ) + { + fds->write( time(), addr, data ); + return; + } + + if ( namco ) + { + if ( addr == namco->addr_reg_addr ) + { + namco->write_addr( data ); + return; + } + + if ( addr == namco->data_reg_addr ) + { + namco->write_data( time(), data ); + return; + } + } + + if ( vrc6 ) + { + int reg = addr & (vrc6->addr_step - 1); + int osc = (unsigned) (addr - vrc6->base_addr) / vrc6->addr_step; + if ( (unsigned) osc < vrc6->osc_count && (unsigned) reg < vrc6->reg_count ) + { + vrc6->write_osc( time(), osc, reg, data ); + return; + } + } + + if ( addr >= fme7->latch_addr && fme7 ) + { + switch ( addr & fme7->addr_mask ) + { + case Nes_Fme7_Apu::latch_addr: + fme7->write_latch( data ); + return; + + case Nes_Fme7_Apu::data_addr: + fme7->write_data( time(), data ); + return; + } + } + + if ( mmc5 ) + { + if ( (unsigned) (addr - mmc5->regs_addr) < mmc5->regs_size ) + { + mmc5->write_register( time(), addr, data ); + return; + } + + int m = addr - 0x5205; + if ( (unsigned) m < 2 ) + { + mmc5_mul [m] = data; + return; + } + + int i = addr - 0x5C00; + if ( (unsigned) i < mmc5->exram_size ) + { + mmc5->exram [i] = data; + return; + } + } + + if ( vrc7 ) + { + if ( addr == 0x9010 ) + { + vrc7->write_reg( data ); + return; + } + + if ( (unsigned) (addr - 0x9028) <= 0x08 ) + { + vrc7->write_data( time(), data ); + return; + } + } + } + #endif + + return Nsf_Impl::cpu_write( addr, data ); +} + +void Nsf_Core::unmapped_write( addr_t addr, int data ) +{ + switch ( addr ) + { + case 0x8000: // some write to $8000 and $8001 repeatedly + case 0x8001: + case 0x4800: // probably namco sound mistakenly turned on in MCK + case 0xF800: + case 0xFFF8: // memory mapper? + return; + } + + if ( mmc5 && addr == 0x5115 ) return; + + // FDS memory + if ( fds && (unsigned) (addr - 0x8000) < 0x6000 ) return; + + Nsf_Impl::unmapped_write( addr, data ); +} + +blargg_err_t Nsf_Core::start_track( int track ) +{ + #if !NSF_EMU_APU_ONLY + if ( mmc5 ) + { + mmc5_mul [0] = 0; + mmc5_mul [1] = 0; + memset( mmc5->exram, 0, mmc5->exram_size ); + } + #endif + + #if !NSF_EMU_APU_ONLY + if ( fds ) fds ->reset(); + if ( fme7 ) fme7 ->reset(); + if ( mmc5 ) mmc5 ->reset(); + if ( namco ) namco->reset(); + if ( vrc6 ) vrc6 ->reset(); + if ( vrc7 ) vrc7 ->reset(); + #endif + + return Nsf_Impl::start_track( track ); +} + +void Nsf_Core::end_frame( time_t end ) +{ + Nsf_Impl::end_frame( end ); + + #if !NSF_EMU_APU_ONLY + if ( fds ) fds ->end_frame( end ); + if ( fme7 ) fme7 ->end_frame( end ); + if ( mmc5 ) mmc5 ->end_frame( end ); + if ( namco ) namco->end_frame( end ); + if ( vrc6 ) vrc6 ->end_frame( end ); + if ( vrc7 ) vrc7 ->end_frame( end ); + #endif +} diff --git a/Frameworks/GME/gme/Nsf_Core.h b/Frameworks/GME/gme/Nsf_Core.h index 4e5fd9e5a..a4816feaa 100644 --- a/Frameworks/GME/gme/Nsf_Core.h +++ b/Frameworks/GME/gme/Nsf_Core.h @@ -1,68 +1,68 @@ -// Loads NSF file and emulates CPU and sound chips - -// Game_Music_Emu $vers -#ifndef NSF_CORE_H -#define NSF_CORE_H - -#include "Nsf_Impl.h" - -class Nes_Namco_Apu; -class Nes_Vrc6_Apu; -class Nes_Fme7_Apu; -class Nes_Mmc5_Apu; -class Nes_Vrc7_Apu; -class Nes_Fds_Apu; - -class Nsf_Core : public Nsf_Impl { -public: - - // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. - // Loading a file resets tempo to 1.0. - void set_tempo( double ); - - // Pointer to sound chip, or NULL if not used by current file. - // Must be assigned to a Blip_Buffer to get any sound. - Nes_Fds_Apu * fds_apu () { return fds; } - Nes_Fme7_Apu * fme7_apu () { return fme7; } - Nes_Mmc5_Apu * mmc5_apu () { return mmc5; } - Nes_Namco_Apu* namco_apu() { return namco; } - Nes_Vrc6_Apu * vrc6_apu () { return vrc6; } - Nes_Vrc7_Apu * vrc7_apu () { return vrc7; } - - // Mask for which chips are supported - #if NSF_EMU_APU_ONLY - enum { chips_mask = 0 }; - #else - enum { chips_mask = header_t::all_mask }; - #endif - -protected: - virtual int unmapped_read( addr_t ); - virtual void unmapped_write( addr_t, int data ); - - -// Implementation -public: - Nsf_Core(); - ~Nsf_Core(); - virtual void unload(); - virtual blargg_err_t start_track( int ); - virtual void end_frame( time_t ); - -protected: - virtual blargg_err_t post_load(); - virtual int cpu_read( addr_t ); - virtual void cpu_write( addr_t, int ); - -private: - byte mmc5_mul [2]; - - Nes_Fds_Apu* fds; - Nes_Fme7_Apu* fme7; - Nes_Mmc5_Apu* mmc5; - Nes_Namco_Apu* namco; - Nes_Vrc6_Apu* vrc6; - Nes_Vrc7_Apu* vrc7; -}; - -#endif +// Loads NSF file and emulates CPU and sound chips + +// Game_Music_Emu $vers +#ifndef NSF_CORE_H +#define NSF_CORE_H + +#include "Nsf_Impl.h" + +class Nes_Namco_Apu; +class Nes_Vrc6_Apu; +class Nes_Fme7_Apu; +class Nes_Mmc5_Apu; +class Nes_Vrc7_Apu; +class Nes_Fds_Apu; + +class Nsf_Core : public Nsf_Impl { +public: + + // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. + // Loading a file resets tempo to 1.0. + void set_tempo( double ); + + // Pointer to sound chip, or NULL if not used by current file. + // Must be assigned to a Blip_Buffer to get any sound. + Nes_Fds_Apu * fds_apu () { return fds; } + Nes_Fme7_Apu * fme7_apu () { return fme7; } + Nes_Mmc5_Apu * mmc5_apu () { return mmc5; } + Nes_Namco_Apu* namco_apu() { return namco; } + Nes_Vrc6_Apu * vrc6_apu () { return vrc6; } + Nes_Vrc7_Apu * vrc7_apu () { return vrc7; } + + // Mask for which chips are supported + #if NSF_EMU_APU_ONLY + enum { chips_mask = 0 }; + #else + enum { chips_mask = header_t::all_mask }; + #endif + +protected: + virtual int unmapped_read( addr_t ); + virtual void unmapped_write( addr_t, int data ); + + +// Implementation +public: + Nsf_Core(); + ~Nsf_Core(); + virtual void unload(); + virtual blargg_err_t start_track( int ); + virtual void end_frame( time_t ); + +protected: + virtual blargg_err_t post_load(); + virtual int cpu_read( addr_t ); + virtual void cpu_write( addr_t, int ); + +private: + byte mmc5_mul [2]; + + Nes_Fds_Apu* fds; + Nes_Fme7_Apu* fme7; + Nes_Mmc5_Apu* mmc5; + Nes_Namco_Apu* namco; + Nes_Vrc6_Apu* vrc6; + Nes_Vrc7_Apu* vrc7; +}; + +#endif diff --git a/Frameworks/GME/gme/Nsf_Cpu.cpp b/Frameworks/GME/gme/Nsf_Cpu.cpp index 3bb5984b2..79b8fecc2 100644 --- a/Frameworks/GME/gme/Nsf_Cpu.cpp +++ b/Frameworks/GME/gme/Nsf_Cpu.cpp @@ -1,116 +1,116 @@ -// Normal CPU for NSF emulator - -// $package. http://www.slack.net/~ant/ - -#include "Nsf_Impl.h" - -#include "blargg_endian.h" - -#ifdef BLARGG_DEBUG_H - //#define CPU_LOG_START 1000000 - //#include "nes_cpu_log.h" - #undef LOG_MEM -#endif - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifndef LOG_MEM - #define LOG_MEM( addr, str, data ) data -#endif - -int Nsf_Impl::read_mem( addr_t addr ) -{ - int result = low_ram [addr & (low_ram_size-1)]; // also handles wrap-around - if ( addr & 0xE000 ) - { - result = *cpu.get_code( addr ); - if ( addr < sram_addr ) - { - if ( addr == apu.status_addr ) - result = apu.read_status( time() ); - else - result = cpu_read( addr ); - } - } - return LOG_MEM( addr, ">", result ); -} - -void Nsf_Impl::write_mem( addr_t addr, int data ) -{ - (void) LOG_MEM( addr, "<", data ); - - int offset = addr - sram_addr; - if ( (unsigned) offset < sram_size ) - { - sram() [offset] = data; - } - else - { - // after sram because CPU handles most low_ram accesses internally already - int temp = addr & (low_ram_size-1); // also handles wrap-around - if ( !(addr & 0xE000) ) - { - low_ram [temp] = data; - } - else - { - int bank = addr - banks_addr; - if ( (unsigned) bank < bank_count ) - { - write_bank( bank, data ); - } - else if ( (unsigned) (addr - apu.io_addr) < apu.io_size ) - { - apu.write_register( time(), addr, data ); - } - else - { - #if !NSF_EMU_APU_ONLY - // 0x8000-0xDFFF is writable - int i = addr - 0x8000; - if ( (unsigned) i < fdsram_size && fds_enabled() ) - fdsram() [i] = data; - else - #endif - cpu_write( addr, data ); - } - } - } -} - -#define READ_LOW( addr ) (LOG_MEM( addr, ">", low_ram [addr] )) -#define WRITE_LOW( addr, data ) (LOG_MEM( addr, "<", low_ram [addr] = data )) - -#define CAN_WRITE_FAST( addr ) (addr < low_ram_size) -#define WRITE_FAST WRITE_LOW - -// addr < 0x2000 || addr >= 0x8000 -#define CAN_READ_FAST( addr ) ((addr ^ 0x8000) < 0xA000) -#define READ_FAST( addr, out ) (LOG_MEM( addr, ">", out = READ_CODE( addr ) )) - -#define READ_MEM( addr ) read_mem( addr ) -#define WRITE_MEM( addr, data ) write_mem( addr, data ) - -#define CPU cpu - -#define CPU_BEGIN \ -bool Nsf_Impl::run_cpu_until( time_t end )\ -{\ - cpu.set_end_time( end );\ - if ( *cpu.get_code( cpu.r.pc ) != cpu.halt_opcode )\ - { - #include "Nes_Cpu_run.h" - } - return cpu.time_past_end() < 0; -} +// Normal CPU for NSF emulator + +// $package. http://www.slack.net/~ant/ + +#include "Nsf_Impl.h" + +#include "blargg_endian.h" + +#ifdef BLARGG_DEBUG_H + //#define CPU_LOG_START 1000000 + //#include "nes_cpu_log.h" + #undef LOG_MEM +#endif + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifndef LOG_MEM + #define LOG_MEM( addr, str, data ) data +#endif + +int Nsf_Impl::read_mem( addr_t addr ) +{ + int result = low_ram [addr & (low_ram_size-1)]; // also handles wrap-around + if ( addr & 0xE000 ) + { + result = *cpu.get_code( addr ); + if ( addr < sram_addr ) + { + if ( addr == apu.status_addr ) + result = apu.read_status( time() ); + else + result = cpu_read( addr ); + } + } + return LOG_MEM( addr, ">", result ); +} + +void Nsf_Impl::write_mem( addr_t addr, int data ) +{ + (void) LOG_MEM( addr, "<", data ); + + int offset = addr - sram_addr; + if ( (unsigned) offset < sram_size ) + { + sram() [offset] = data; + } + else + { + // after sram because CPU handles most low_ram accesses internally already + int temp = addr & (low_ram_size-1); // also handles wrap-around + if ( !(addr & 0xE000) ) + { + low_ram [temp] = data; + } + else + { + int bank = addr - banks_addr; + if ( (unsigned) bank < bank_count ) + { + write_bank( bank, data ); + } + else if ( (unsigned) (addr - apu.io_addr) < apu.io_size ) + { + apu.write_register( time(), addr, data ); + } + else + { + #if !NSF_EMU_APU_ONLY + // 0x8000-0xDFFF is writable + int i = addr - 0x8000; + if ( (unsigned) i < fdsram_size && fds_enabled() ) + fdsram() [i] = data; + else + #endif + cpu_write( addr, data ); + } + } + } +} + +#define READ_LOW( addr ) (LOG_MEM( addr, ">", low_ram [addr] )) +#define WRITE_LOW( addr, data ) (LOG_MEM( addr, "<", low_ram [addr] = data )) + +#define CAN_WRITE_FAST( addr ) (addr < low_ram_size) +#define WRITE_FAST WRITE_LOW + +// addr < 0x2000 || addr >= 0x8000 +#define CAN_READ_FAST( addr ) ((addr ^ 0x8000) < 0xA000) +#define READ_FAST( addr, out ) (LOG_MEM( addr, ">", out = READ_CODE( addr ) )) + +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) write_mem( addr, data ) + +#define CPU cpu + +#define CPU_BEGIN \ +bool Nsf_Impl::run_cpu_until( time_t end )\ +{\ + cpu.set_end_time( end );\ + if ( *cpu.get_code( cpu.r.pc ) != cpu.halt_opcode )\ + { + #include "Nes_Cpu_run.h" + } + return cpu.time_past_end() < 0; +} diff --git a/Frameworks/GME/gme/Nsf_Emu.cpp b/Frameworks/GME/gme/Nsf_Emu.cpp index 449d43a6d..5d88b14eb 100644 --- a/Frameworks/GME/gme/Nsf_Emu.cpp +++ b/Frameworks/GME/gme/Nsf_Emu.cpp @@ -21,14 +21,14 @@ FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80, 0,0,0,0,0,0,0,0 }; -Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80, 0,0,0,0,0,0,0,0 }; - -Nsf_Emu::Nsf_Emu() -{ + +#include "blargg_source.h" + +Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80, 0,0,0,0,0,0,0,0 }; +Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80, 0,0,0,0,0,0,0,0 }; + +Nsf_Emu::Nsf_Emu() +{ set_type( gme_nsf_type ); set_silence_lookahead( 6 ); set_gain( 1.4 ); diff --git a/Frameworks/GME/gme/Nsf_Emu.h b/Frameworks/GME/gme/Nsf_Emu.h index 9599a2a25..f1e533b75 100644 --- a/Frameworks/GME/gme/Nsf_Emu.h +++ b/Frameworks/GME/gme/Nsf_Emu.h @@ -1,55 +1,55 @@ -// Nintendo NES/Famicom NSF music file emulator - -// Game_Music_Emu $vers -#ifndef NSF_EMU_H -#define NSF_EMU_H - -#include "Classic_Emu.h" -#include "Nsf_Core.h" - -void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out ); - -class Nsf_Emu : public Classic_Emu { -public: - // Equalizer profiles for US NES and Japanese Famicom - static equalizer_t const nes_eq; - static equalizer_t const famicom_eq; - - // NSF file header (see Nsf_Impl.h) - typedef Nsf_Core::header_t header_t; - - // Header for currently loaded file - header_t const& header() const { return core_.header(); } - - blargg_err_t hash_( Hash_Function& ) const; - - static gme_type_t static_type() { return gme_nsf_type; } - - Nsf_Core& core() { return core_; } - -public: - Nsf_Emu(); - ~Nsf_Emu(); - virtual void unload(); - -protected: - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t load_( Data_Reader& ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - -private: - enum { max_voices = 32 }; - const char* voice_names_ [32]; - int voice_types_ [32]; - int voice_count_; - Nsf_Core core_; - - blargg_err_t init_sound(); - void append_voices( const char* const names [], int const types [], int count ); -}; - -#endif +// Nintendo NES/Famicom NSF music file emulator + +// Game_Music_Emu $vers +#ifndef NSF_EMU_H +#define NSF_EMU_H + +#include "Classic_Emu.h" +#include "Nsf_Core.h" + +void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out ); + +class Nsf_Emu : public Classic_Emu { +public: + // Equalizer profiles for US NES and Japanese Famicom + static equalizer_t const nes_eq; + static equalizer_t const famicom_eq; + + // NSF file header (see Nsf_Impl.h) + typedef Nsf_Core::header_t header_t; + + // Header for currently loaded file + header_t const& header() const { return core_.header(); } + + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_nsf_type; } + + Nsf_Core& core() { return core_; } + +public: + Nsf_Emu(); + ~Nsf_Emu(); + virtual void unload(); + +protected: + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + +private: + enum { max_voices = 32 }; + const char* voice_names_ [32]; + int voice_types_ [32]; + int voice_count_; + Nsf_Core core_; + + blargg_err_t init_sound(); + void append_voices( const char* const names [], int const types [], int count ); +}; + +#endif diff --git a/Frameworks/GME/gme/Nsf_Impl.cpp b/Frameworks/GME/gme/Nsf_Impl.cpp index 62c255152..a2dbfe44b 100644 --- a/Frameworks/GME/gme/Nsf_Impl.cpp +++ b/Frameworks/GME/gme/Nsf_Impl.cpp @@ -1,328 +1,328 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Nsf_Impl.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// number of frames until play interrupts init -int const initial_play_delay = 7; // KikiKaikai needed this to work -int const bank_size = 0x1000; -int const rom_addr = 0x8000; - -int Nsf_Impl::read_code( addr_t addr ) const -{ - return *cpu.get_code( addr ); -} - -int Nsf_Impl::pcm_read( void* self, int addr ) -{ - return STATIC_CAST(Nsf_Impl*,self)->read_code( addr ); -} - -Nsf_Impl::Nsf_Impl() : rom( bank_size ), enable_w4011( true ) -{ - apu.dmc_reader( pcm_read, this ); - assert( offsetof (header_t,unused [4]) == header_t::size ); -} - -void Nsf_Impl::unload() -{ - rom.clear(); - high_ram.clear(); - Gme_Loader::unload(); -} - -Nsf_Impl::~Nsf_Impl() { unload(); } - -bool nsf_header_t::valid_tag() const -{ - return 0 == memcmp( tag, "NESM\x1A", 5 ); -} - -double nsf_header_t::clock_rate() const -{ - return pal_only() ? 1662607.125 : 1789772.727272727; -} - -int nsf_header_t::play_period() const -{ - // NTSC - int clocks = 29780; - int value = 0x411A; - byte const* rate_ptr = ntsc_speed; - - // PAL - if ( pal_only() ) - { - clocks = 33247; - value = 0x4E20; - rate_ptr = pal_speed; - } - - // Default rate - int rate = get_le16( rate_ptr ); - if ( rate == 0 ) - rate = value; - - // Custom rate - if ( rate != value ) - clocks = (int) (rate * clock_rate() * (1.0/1000000.0)); - - return clocks; -} - -// Gets address, given pointer to it in file header. If zero, returns rom_addr. -Nsf_Impl::addr_t Nsf_Impl::get_addr( byte const in [] ) -{ - addr_t addr = get_le16( in ); - if ( addr == 0 ) - addr = rom_addr; - return addr; -} - -blargg_err_t Nsf_Impl::load_( Data_Reader& in ) -{ - // pad ROM data with 0 - RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) ); - - if ( !header_.valid_tag() ) - return blargg_err_file_type; - - RETURN_ERR( high_ram.resize( (fds_enabled() ? fdsram_offset + fdsram_size : fdsram_offset) ) ); - - addr_t load_addr = get_addr( header_.load_addr ); - if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) ) - set_warning( "Load address is too low" ); - - rom.set_addr( load_addr % bank_size ); - - if ( header_.vers != 1 ) - set_warning( "Unknown file version" ); - - set_play_period( header_.play_period() ); - - return blargg_ok; -} - -void Nsf_Impl::write_bank( int bank, int data ) -{ - // Find bank in ROM - int offset = rom.mask_addr( data * bank_size ); - if ( offset >= rom.size() ) - special_event( "invalid bank" ); - void const* rom_data = rom.at_addr( offset ); - - #if !NSF_EMU_APU_ONLY - if ( bank < bank_count - fds_banks && fds_enabled() ) - { - // TODO: FDS bank switching is kind of hacky, might need to - // treat ROM as RAM so changes won't get lost when switching. - byte* out = sram(); - if ( bank >= fds_banks ) - { - out = fdsram(); - bank -= fds_banks; - } - memcpy( &out [bank * bank_size], rom_data, bank_size ); - return; - } - #endif - - if ( bank >= fds_banks ) - cpu.map_code( (bank + 6) * bank_size, bank_size, rom_data ); -} - -void Nsf_Impl::map_memory() -{ - // Map standard things - cpu.reset( unmapped_code() ); - cpu.map_code( 0, 0x2000, low_ram, low_ram_size ); // mirrored four times - cpu.map_code( sram_addr, sram_size, sram() ); - - // Determine initial banks - byte banks [bank_count]; - static byte const zero_banks [sizeof header_.banks] = { 0 }; - if ( memcmp( header_.banks, zero_banks, sizeof zero_banks ) ) - { - banks [0] = header_.banks [6]; - banks [1] = header_.banks [7]; - memcpy( banks + fds_banks, header_.banks, sizeof header_.banks ); - } - else - { - // No initial banks, so assign them based on load_addr - int first_bank = (get_addr( header_.load_addr ) - sram_addr) / bank_size; - unsigned total_banks = rom.size() / bank_size; - for ( int i = bank_count; --i >= 0; ) - { - int bank = i - first_bank; - if ( (unsigned) bank >= total_banks ) - bank = 0; - banks [i] = bank; - } - } - - // Map banks - for ( int i = (fds_enabled() ? 0 : fds_banks); i < bank_count; ++i ) - write_bank( i, banks [i] ); - - // Map FDS RAM - if ( fds_enabled() ) - cpu.map_code( rom_addr, fdsram_size, fdsram() ); -} - -inline void Nsf_Impl::push_byte( int b ) -{ - low_ram [0x100 + cpu.r.sp--] = b; -} - -// Jumps to routine, given pointer to address in file header. Pushes idle_addr -// as return address, NOT old PC. -void Nsf_Impl::jsr_then_stop( byte const addr [] ) -{ - cpu.r.pc = get_addr( addr ); - push_byte( (idle_addr - 1) >> 8 ); - push_byte( (idle_addr - 1) ); -} - -blargg_err_t Nsf_Impl::start_track( int track ) -{ - int speed_flags = 0; - #if NSF_EMU_EXTRA_FLAGS - speed_flags = header().speed_flags; - #endif - - apu.reset( header().pal_only(), (speed_flags & 0x20) ? 0x3F : 0 ); - apu.enable_w4011_( enable_w4011 ); - apu.write_register( 0, 0x4015, 0x0F ); - apu.write_register( 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 ); - - // Clear memory - memset( unmapped_code(), Nes_Cpu::halt_opcode, unmapped_size ); - memset( low_ram, 0, low_ram_size ); - memset( sram(), 0, sram_size ); - - map_memory(); - - // Arrange time of first call to play routine - play_extra = 0; - next_play = play_period; - - play_delay = initial_play_delay; - saved_state.pc = idle_addr; - - // Setup for call to init routine - cpu.r.a = track; - cpu.r.x = header_.pal_only(); - cpu.r.sp = 0xFF; - jsr_then_stop( header_.init_addr ); - if ( cpu.r.pc < get_addr( header_.load_addr ) ) - set_warning( "Init address < load address" ); - - return blargg_ok; -} - -void Nsf_Impl::unmapped_write( addr_t addr, int data ) -{ - dprintf( "Unmapped write $%04X <- %02X\n", (int) addr, data ); -} - -int Nsf_Impl::unmapped_read( addr_t addr ) -{ - dprintf( "Unmapped read $%04X\n", (int) addr ); - return addr >> 8; -} - -void Nsf_Impl::special_event( const char str [] ) -{ - dprintf( "%s\n", str ); -} - -void Nsf_Impl::run_once( time_t end ) -{ - // Emulate until next play call if possible - if ( run_cpu_until( min( next_play, end ) ) ) - { - // Halt instruction encountered - - if ( cpu.r.pc != idle_addr ) - { - special_event( "illegal instruction" ); - cpu.count_error(); - cpu.set_time( cpu.end_time() ); - return; - } - - // Init/play routine returned - play_delay = 1; // play can now be called regularly - - if ( saved_state.pc == idle_addr ) - { - // nothing to run - time_t t = cpu.end_time(); - if ( cpu.time() < t ) - cpu.set_time( t ); - } - else - { - // continue init routine that was interrupted by play routine - cpu.r = saved_state; - saved_state.pc = idle_addr; - } - } - - if ( time() >= next_play ) - { - // Calculate time of next call to play routine - play_extra ^= 1; // extra clock every other call - next_play += play_period + play_extra; - - // Call routine if ready - if ( play_delay && !--play_delay ) - { - // Save state if init routine is still running - if ( cpu.r.pc != idle_addr ) - { - check( saved_state.pc == idle_addr ); - saved_state = cpu.r; - special_event( "play called during init" ); - } - - jsr_then_stop( header_.play_addr ); - } - } -} - -void Nsf_Impl::run_until( time_t end ) -{ - while ( time() < end ) - run_once( end ); -} - -void Nsf_Impl::end_frame( time_t end ) -{ - if ( time() < end ) - run_until( end ); - cpu.adjust_time( -end ); - - // Localize to new time frame - next_play -= end; - check( next_play >= 0 ); - if ( next_play < 0 ) - next_play = 0; - - apu.end_frame( end ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nsf_Impl.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// number of frames until play interrupts init +int const initial_play_delay = 7; // KikiKaikai needed this to work +int const bank_size = 0x1000; +int const rom_addr = 0x8000; + +int Nsf_Impl::read_code( addr_t addr ) const +{ + return *cpu.get_code( addr ); +} + +int Nsf_Impl::pcm_read( void* self, int addr ) +{ + return STATIC_CAST(Nsf_Impl*,self)->read_code( addr ); +} + +Nsf_Impl::Nsf_Impl() : rom( bank_size ), enable_w4011( true ) +{ + apu.dmc_reader( pcm_read, this ); + assert( offsetof (header_t,unused [4]) == header_t::size ); +} + +void Nsf_Impl::unload() +{ + rom.clear(); + high_ram.clear(); + Gme_Loader::unload(); +} + +Nsf_Impl::~Nsf_Impl() { unload(); } + +bool nsf_header_t::valid_tag() const +{ + return 0 == memcmp( tag, "NESM\x1A", 5 ); +} + +double nsf_header_t::clock_rate() const +{ + return pal_only() ? 1662607.125 : 1789772.727272727; +} + +int nsf_header_t::play_period() const +{ + // NTSC + int clocks = 29780; + int value = 0x411A; + byte const* rate_ptr = ntsc_speed; + + // PAL + if ( pal_only() ) + { + clocks = 33247; + value = 0x4E20; + rate_ptr = pal_speed; + } + + // Default rate + int rate = get_le16( rate_ptr ); + if ( rate == 0 ) + rate = value; + + // Custom rate + if ( rate != value ) + clocks = (int) (rate * clock_rate() * (1.0/1000000.0)); + + return clocks; +} + +// Gets address, given pointer to it in file header. If zero, returns rom_addr. +Nsf_Impl::addr_t Nsf_Impl::get_addr( byte const in [] ) +{ + addr_t addr = get_le16( in ); + if ( addr == 0 ) + addr = rom_addr; + return addr; +} + +blargg_err_t Nsf_Impl::load_( Data_Reader& in ) +{ + // pad ROM data with 0 + RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) ); + + if ( !header_.valid_tag() ) + return blargg_err_file_type; + + RETURN_ERR( high_ram.resize( (fds_enabled() ? fdsram_offset + fdsram_size : fdsram_offset) ) ); + + addr_t load_addr = get_addr( header_.load_addr ); + if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) ) + set_warning( "Load address is too low" ); + + rom.set_addr( load_addr % bank_size ); + + if ( header_.vers != 1 ) + set_warning( "Unknown file version" ); + + set_play_period( header_.play_period() ); + + return blargg_ok; +} + +void Nsf_Impl::write_bank( int bank, int data ) +{ + // Find bank in ROM + int offset = rom.mask_addr( data * bank_size ); + if ( offset >= rom.size() ) + special_event( "invalid bank" ); + void const* rom_data = rom.at_addr( offset ); + + #if !NSF_EMU_APU_ONLY + if ( bank < bank_count - fds_banks && fds_enabled() ) + { + // TODO: FDS bank switching is kind of hacky, might need to + // treat ROM as RAM so changes won't get lost when switching. + byte* out = sram(); + if ( bank >= fds_banks ) + { + out = fdsram(); + bank -= fds_banks; + } + memcpy( &out [bank * bank_size], rom_data, bank_size ); + return; + } + #endif + + if ( bank >= fds_banks ) + cpu.map_code( (bank + 6) * bank_size, bank_size, rom_data ); +} + +void Nsf_Impl::map_memory() +{ + // Map standard things + cpu.reset( unmapped_code() ); + cpu.map_code( 0, 0x2000, low_ram, low_ram_size ); // mirrored four times + cpu.map_code( sram_addr, sram_size, sram() ); + + // Determine initial banks + byte banks [bank_count]; + static byte const zero_banks [sizeof header_.banks] = { 0 }; + if ( memcmp( header_.banks, zero_banks, sizeof zero_banks ) ) + { + banks [0] = header_.banks [6]; + banks [1] = header_.banks [7]; + memcpy( banks + fds_banks, header_.banks, sizeof header_.banks ); + } + else + { + // No initial banks, so assign them based on load_addr + int first_bank = (get_addr( header_.load_addr ) - sram_addr) / bank_size; + unsigned total_banks = rom.size() / bank_size; + for ( int i = bank_count; --i >= 0; ) + { + int bank = i - first_bank; + if ( (unsigned) bank >= total_banks ) + bank = 0; + banks [i] = bank; + } + } + + // Map banks + for ( int i = (fds_enabled() ? 0 : fds_banks); i < bank_count; ++i ) + write_bank( i, banks [i] ); + + // Map FDS RAM + if ( fds_enabled() ) + cpu.map_code( rom_addr, fdsram_size, fdsram() ); +} + +inline void Nsf_Impl::push_byte( int b ) +{ + low_ram [0x100 + cpu.r.sp--] = b; +} + +// Jumps to routine, given pointer to address in file header. Pushes idle_addr +// as return address, NOT old PC. +void Nsf_Impl::jsr_then_stop( byte const addr [] ) +{ + cpu.r.pc = get_addr( addr ); + push_byte( (idle_addr - 1) >> 8 ); + push_byte( (idle_addr - 1) ); +} + +blargg_err_t Nsf_Impl::start_track( int track ) +{ + int speed_flags = 0; + #if NSF_EMU_EXTRA_FLAGS + speed_flags = header().speed_flags; + #endif + + apu.reset( header().pal_only(), (speed_flags & 0x20) ? 0x3F : 0 ); + apu.enable_w4011_( enable_w4011 ); + apu.write_register( 0, 0x4015, 0x0F ); + apu.write_register( 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 ); + + // Clear memory + memset( unmapped_code(), Nes_Cpu::halt_opcode, unmapped_size ); + memset( low_ram, 0, low_ram_size ); + memset( sram(), 0, sram_size ); + + map_memory(); + + // Arrange time of first call to play routine + play_extra = 0; + next_play = play_period; + + play_delay = initial_play_delay; + saved_state.pc = idle_addr; + + // Setup for call to init routine + cpu.r.a = track; + cpu.r.x = header_.pal_only(); + cpu.r.sp = 0xFF; + jsr_then_stop( header_.init_addr ); + if ( cpu.r.pc < get_addr( header_.load_addr ) ) + set_warning( "Init address < load address" ); + + return blargg_ok; +} + +void Nsf_Impl::unmapped_write( addr_t addr, int data ) +{ + dprintf( "Unmapped write $%04X <- %02X\n", (int) addr, data ); +} + +int Nsf_Impl::unmapped_read( addr_t addr ) +{ + dprintf( "Unmapped read $%04X\n", (int) addr ); + return addr >> 8; +} + +void Nsf_Impl::special_event( const char str [] ) +{ + dprintf( "%s\n", str ); +} + +void Nsf_Impl::run_once( time_t end ) +{ + // Emulate until next play call if possible + if ( run_cpu_until( min( next_play, end ) ) ) + { + // Halt instruction encountered + + if ( cpu.r.pc != idle_addr ) + { + special_event( "illegal instruction" ); + cpu.count_error(); + cpu.set_time( cpu.end_time() ); + return; + } + + // Init/play routine returned + play_delay = 1; // play can now be called regularly + + if ( saved_state.pc == idle_addr ) + { + // nothing to run + time_t t = cpu.end_time(); + if ( cpu.time() < t ) + cpu.set_time( t ); + } + else + { + // continue init routine that was interrupted by play routine + cpu.r = saved_state; + saved_state.pc = idle_addr; + } + } + + if ( time() >= next_play ) + { + // Calculate time of next call to play routine + play_extra ^= 1; // extra clock every other call + next_play += play_period + play_extra; + + // Call routine if ready + if ( play_delay && !--play_delay ) + { + // Save state if init routine is still running + if ( cpu.r.pc != idle_addr ) + { + check( saved_state.pc == idle_addr ); + saved_state = cpu.r; + special_event( "play called during init" ); + } + + jsr_then_stop( header_.play_addr ); + } + } +} + +void Nsf_Impl::run_until( time_t end ) +{ + while ( time() < end ) + run_once( end ); +} + +void Nsf_Impl::end_frame( time_t end ) +{ + if ( time() < end ) + run_until( end ); + cpu.adjust_time( -end ); + + // Localize to new time frame + next_play -= end; + check( next_play >= 0 ); + if ( next_play < 0 ) + next_play = 0; + + apu.end_frame( end ); +} diff --git a/Frameworks/GME/gme/Nsf_Impl.h b/Frameworks/GME/gme/Nsf_Impl.h index fcb28f7f7..1c69c4f06 100644 --- a/Frameworks/GME/gme/Nsf_Impl.h +++ b/Frameworks/GME/gme/Nsf_Impl.h @@ -1,194 +1,194 @@ -// Loads NSF file and emulates CPU and RAM, no sound chips - -// Game_Music_Emu $vers -#ifndef NSF_IMPL_H -#define NSF_IMPL_H - -#include "Gme_Loader.h" -#include "Nes_Cpu.h" -#include "Rom_Data.h" -#include "Nes_Apu.h" - -// NSF file header -struct nsf_header_t -{ - typedef unsigned char byte; - enum { size = 0x80 }; - - char tag [ 5]; - byte vers; - byte track_count; - byte first_track; - byte load_addr [ 2]; - byte init_addr [ 2]; - byte play_addr [ 2]; - char game [32]; // NOT null-terminated if 32 chars in length - char author [32]; - char copyright [32]; - byte ntsc_speed [ 2]; - byte banks [ 8]; - byte pal_speed [ 2]; - byte speed_flags; - byte chip_flags; - byte unused [ 4]; - - // Sound chip masks - enum { - vrc6_mask = 1 << 0, - vrc7_mask = 1 << 1, - fds_mask = 1 << 2, - mmc5_mask = 1 << 3, - namco_mask = 1 << 4, - fme7_mask = 1 << 5, - all_mask = (1 << 6) - 1 - }; - - // True if header has proper NSF file signature - bool valid_tag() const; - - // True if file supports only PAL speed - bool pal_only() const { return (speed_flags & 3) == 1; } - - // Clocks per second - double clock_rate() const; - - // Clocks between calls to play routine - int play_period() const; -}; - -/* Loads NSF file into memory, then emulates CPU, RAM, and ROM. -Non-memory accesses are routed through cpu_read() and cpu_write(). */ -class Nsf_Impl : public Gme_Loader { -public: - - // Sound chip - Nes_Apu* nes_apu() { return &apu; } - - // Starts track, where 0 is the first - virtual blargg_err_t start_track( int ); - - // Emulates to at least time t, then begins new time frame at - // time t. Might emulate a few clocks extra, so after returning, - // time() may not be zero. - typedef int time_t; // clock count - virtual void end_frame( time_t n ); - -// Finer control - - // Header for currently loaded file - typedef nsf_header_t header_t; - header_t const& header() const { return header_; } - - // Sets clocks between calls to play routine to p + 1/2 clock - void set_play_period( int p ) { play_period = p; } - - // Time play routine will next be called - time_t play_time() const { return next_play; } - - // Emulates to at least time t. Might emulate a few clocks extra. - virtual void run_until( time_t t ); - - // Time emulated to - time_t time() const { return cpu.time(); } - - void enable_w4011_(bool enable = true) { enable_w4011 = enable; } - - Rom_Data const& rom_() const { return rom; } - -protected: -// Nsf_Core use - - typedef int addr_t; - - // Called for unmapped accesses. Default just prints info if debugging. - virtual void unmapped_write( addr_t, int data ); - virtual int unmapped_read( addr_t ); - - // Override in derived class - // Bank writes and RAM at 0-$7FF and $6000-$7FFF are handled internally - virtual int cpu_read( addr_t a ) { return unmapped_read( a ); } - virtual void cpu_write( addr_t a, int data ){ unmapped_write( a, data ); } - - // Reads byte as CPU would when executing code. Only works for RAM/ROM, - // NOT I/O like sound chips. - int read_code( addr_t addr ) const; - -// Debugger services - - enum { mem_size = 0x10000 }; - - // CPU sits here when waiting for next call to play routine - enum { idle_addr = 0x5FF6 }; - - Nes_Cpu cpu; - - // Runs CPU to at least time t and returns false, or returns true - // if it encounters illegal instruction (halt). - virtual bool run_cpu_until( time_t t ); - - // CPU calls through to these to access memory (except instructions) - int read_mem( addr_t ); - void write_mem( addr_t, int ); - - // Address of play routine - addr_t play_addr() const { return get_addr( header_.play_addr ); } - - // Same as run_until, except emulation stops for any event (routine returned, - // play routine called, illegal instruction). - void run_once( time_t ); - - // Make a note of event - virtual void special_event( const char str [] ); - - -// Implementation -public: - Nsf_Impl(); - ~Nsf_Impl(); - -protected: - virtual blargg_err_t load_( Data_Reader& ); - virtual void unload(); - -private: - enum { low_ram_size = 0x800 }; - enum { fdsram_size = 0x6000 }; - enum { sram_size = 0x2000 }; - enum { unmapped_size= Nes_Cpu::page_size + 8 }; - enum { fds_banks = 2 }; - enum { bank_count = fds_banks + 8 }; - enum { banks_addr = idle_addr }; - enum { sram_addr = 0x6000 }; - - blargg_vector high_ram; - Rom_Data rom; - - // Play routine timing - time_t next_play; - time_t play_period; - int play_extra; - int play_delay; - bool enable_w4011; - Nes_Cpu::registers_t saved_state; // of interrupted init routine - - // Large objects after others - header_t header_; - Nes_Apu apu; - byte low_ram [low_ram_size]; - - // Larger RAM areas allocated separately - enum { fdsram_offset = sram_size + unmapped_size }; - byte* sram() { return high_ram.begin(); } - byte* unmapped_code() { return &high_ram [sram_size]; } - byte* fdsram() { return &high_ram [fdsram_offset]; } - int fds_enabled() const { return header_.chip_flags & header_t::fds_mask; } - - void map_memory(); - void write_bank( int index, int data ); - void jsr_then_stop( byte const addr [] ); - void push_byte( int ); - static addr_t get_addr( byte const [] ); - static int pcm_read( void*, int ); -}; - -#endif +// Loads NSF file and emulates CPU and RAM, no sound chips + +// Game_Music_Emu $vers +#ifndef NSF_IMPL_H +#define NSF_IMPL_H + +#include "Gme_Loader.h" +#include "Nes_Cpu.h" +#include "Rom_Data.h" +#include "Nes_Apu.h" + +// NSF file header +struct nsf_header_t +{ + typedef unsigned char byte; + enum { size = 0x80 }; + + char tag [ 5]; + byte vers; + byte track_count; + byte first_track; + byte load_addr [ 2]; + byte init_addr [ 2]; + byte play_addr [ 2]; + char game [32]; // NOT null-terminated if 32 chars in length + char author [32]; + char copyright [32]; + byte ntsc_speed [ 2]; + byte banks [ 8]; + byte pal_speed [ 2]; + byte speed_flags; + byte chip_flags; + byte unused [ 4]; + + // Sound chip masks + enum { + vrc6_mask = 1 << 0, + vrc7_mask = 1 << 1, + fds_mask = 1 << 2, + mmc5_mask = 1 << 3, + namco_mask = 1 << 4, + fme7_mask = 1 << 5, + all_mask = (1 << 6) - 1 + }; + + // True if header has proper NSF file signature + bool valid_tag() const; + + // True if file supports only PAL speed + bool pal_only() const { return (speed_flags & 3) == 1; } + + // Clocks per second + double clock_rate() const; + + // Clocks between calls to play routine + int play_period() const; +}; + +/* Loads NSF file into memory, then emulates CPU, RAM, and ROM. +Non-memory accesses are routed through cpu_read() and cpu_write(). */ +class Nsf_Impl : public Gme_Loader { +public: + + // Sound chip + Nes_Apu* nes_apu() { return &apu; } + + // Starts track, where 0 is the first + virtual blargg_err_t start_track( int ); + + // Emulates to at least time t, then begins new time frame at + // time t. Might emulate a few clocks extra, so after returning, + // time() may not be zero. + typedef int time_t; // clock count + virtual void end_frame( time_t n ); + +// Finer control + + // Header for currently loaded file + typedef nsf_header_t header_t; + header_t const& header() const { return header_; } + + // Sets clocks between calls to play routine to p + 1/2 clock + void set_play_period( int p ) { play_period = p; } + + // Time play routine will next be called + time_t play_time() const { return next_play; } + + // Emulates to at least time t. Might emulate a few clocks extra. + virtual void run_until( time_t t ); + + // Time emulated to + time_t time() const { return cpu.time(); } + + void enable_w4011_(bool enable = true) { enable_w4011 = enable; } + + Rom_Data const& rom_() const { return rom; } + +protected: +// Nsf_Core use + + typedef int addr_t; + + // Called for unmapped accesses. Default just prints info if debugging. + virtual void unmapped_write( addr_t, int data ); + virtual int unmapped_read( addr_t ); + + // Override in derived class + // Bank writes and RAM at 0-$7FF and $6000-$7FFF are handled internally + virtual int cpu_read( addr_t a ) { return unmapped_read( a ); } + virtual void cpu_write( addr_t a, int data ){ unmapped_write( a, data ); } + + // Reads byte as CPU would when executing code. Only works for RAM/ROM, + // NOT I/O like sound chips. + int read_code( addr_t addr ) const; + +// Debugger services + + enum { mem_size = 0x10000 }; + + // CPU sits here when waiting for next call to play routine + enum { idle_addr = 0x5FF6 }; + + Nes_Cpu cpu; + + // Runs CPU to at least time t and returns false, or returns true + // if it encounters illegal instruction (halt). + virtual bool run_cpu_until( time_t t ); + + // CPU calls through to these to access memory (except instructions) + int read_mem( addr_t ); + void write_mem( addr_t, int ); + + // Address of play routine + addr_t play_addr() const { return get_addr( header_.play_addr ); } + + // Same as run_until, except emulation stops for any event (routine returned, + // play routine called, illegal instruction). + void run_once( time_t ); + + // Make a note of event + virtual void special_event( const char str [] ); + + +// Implementation +public: + Nsf_Impl(); + ~Nsf_Impl(); + +protected: + virtual blargg_err_t load_( Data_Reader& ); + virtual void unload(); + +private: + enum { low_ram_size = 0x800 }; + enum { fdsram_size = 0x6000 }; + enum { sram_size = 0x2000 }; + enum { unmapped_size= Nes_Cpu::page_size + 8 }; + enum { fds_banks = 2 }; + enum { bank_count = fds_banks + 8 }; + enum { banks_addr = idle_addr }; + enum { sram_addr = 0x6000 }; + + blargg_vector high_ram; + Rom_Data rom; + + // Play routine timing + time_t next_play; + time_t play_period; + int play_extra; + int play_delay; + bool enable_w4011; + Nes_Cpu::registers_t saved_state; // of interrupted init routine + + // Large objects after others + header_t header_; + Nes_Apu apu; + byte low_ram [low_ram_size]; + + // Larger RAM areas allocated separately + enum { fdsram_offset = sram_size + unmapped_size }; + byte* sram() { return high_ram.begin(); } + byte* unmapped_code() { return &high_ram [sram_size]; } + byte* fdsram() { return &high_ram [fdsram_offset]; } + int fds_enabled() const { return header_.chip_flags & header_t::fds_mask; } + + void map_memory(); + void write_bank( int index, int data ); + void jsr_then_stop( byte const addr [] ); + void push_byte( int ); + static addr_t get_addr( byte const [] ); + static int pcm_read( void*, int ); +}; + +#endif diff --git a/Frameworks/GME/gme/Nsfe_Emu.h b/Frameworks/GME/gme/Nsfe_Emu.h index a166bfc66..db84568ec 100644 --- a/Frameworks/GME/gme/Nsfe_Emu.h +++ b/Frameworks/GME/gme/Nsfe_Emu.h @@ -1,74 +1,74 @@ -// Nintendo NES/Famicom NSFE music file emulator - -// Game_Music_Emu $vers -#ifndef NSFE_EMU_H -#define NSFE_EMU_H - -#include "blargg_common.h" -#include "Nsf_Emu.h" -class Nsfe_Emu; - -// Allows reading info from NSFE file without creating emulator -class Nsfe_Info { -public: - blargg_err_t load( Data_Reader&, Nsfe_Emu* ); - - struct info_t : Nsf_Emu::header_t - { - char game [256]; - char author [256]; - char copyright [256]; - char dumper [256]; - } info; - - blargg_vector data; - - void disable_playlist( bool = true ); - - blargg_err_t track_info_( track_info_t* out, int track ) const; - - int remap_track( int i ) const; - - void unload(); - -// Implementation -public: - Nsfe_Info(); - ~Nsfe_Info(); - BLARGG_DISABLE_NOTHROW -private: - blargg_vector track_name_data; - blargg_vector track_names; - blargg_vector playlist; - blargg_vector track_times; - int actual_track_count_; - bool playlist_disabled; -}; - -class Nsfe_Emu : public Nsf_Emu { -public: - static gme_type_t static_type() { return gme_nsfe_type; } - - struct header_t { char tag [4]; }; - - -// Implementation -public: - Nsfe_Emu(); - ~Nsfe_Emu(); - virtual void unload(); - -protected: - virtual blargg_err_t load_( Data_Reader& ); - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t start_track_( int ); - virtual void clear_playlist_(); - -private: - Nsfe_Info info; - - void disable_playlist_( bool b ); - friend class Nsfe_Info; -}; - -#endif +// Nintendo NES/Famicom NSFE music file emulator + +// Game_Music_Emu $vers +#ifndef NSFE_EMU_H +#define NSFE_EMU_H + +#include "blargg_common.h" +#include "Nsf_Emu.h" +class Nsfe_Emu; + +// Allows reading info from NSFE file without creating emulator +class Nsfe_Info { +public: + blargg_err_t load( Data_Reader&, Nsfe_Emu* ); + + struct info_t : Nsf_Emu::header_t + { + char game [256]; + char author [256]; + char copyright [256]; + char dumper [256]; + } info; + + blargg_vector data; + + void disable_playlist( bool = true ); + + blargg_err_t track_info_( track_info_t* out, int track ) const; + + int remap_track( int i ) const; + + void unload(); + +// Implementation +public: + Nsfe_Info(); + ~Nsfe_Info(); + BLARGG_DISABLE_NOTHROW +private: + blargg_vector track_name_data; + blargg_vector track_names; + blargg_vector playlist; + blargg_vector track_times; + int actual_track_count_; + bool playlist_disabled; +}; + +class Nsfe_Emu : public Nsf_Emu { +public: + static gme_type_t static_type() { return gme_nsfe_type; } + + struct header_t { char tag [4]; }; + + +// Implementation +public: + Nsfe_Emu(); + ~Nsfe_Emu(); + virtual void unload(); + +protected: + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t start_track_( int ); + virtual void clear_playlist_(); + +private: + Nsfe_Info info; + + void disable_playlist_( bool b ); + friend class Nsfe_Info; +}; + +#endif diff --git a/Frameworks/GME/gme/Okim6258_Emu.cpp b/Frameworks/GME/gme/Okim6258_Emu.cpp deleted file mode 100644 index ff4f5d631..000000000 --- a/Frameworks/GME/gme/Okim6258_Emu.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Okim6258_Emu.h" -#include "okim6258.h" - -Okim6258_Emu::Okim6258_Emu() { chip = 0; } - -Okim6258_Emu::~Okim6258_Emu() -{ - if ( chip ) device_stop_okim6258( chip ); -} - -int Okim6258_Emu::set_rate( int clock, int divider, int adpcm_type, int output_12bits ) -{ - if ( chip ) - { - device_stop_okim6258( chip ); - chip = 0; - } - - chip = device_start_okim6258( clock, divider, adpcm_type, output_12bits ); - if ( !chip ) - return 0; - - reset(); - return okim6258_get_vclk( chip ); -} - -void Okim6258_Emu::reset() -{ - device_reset_okim6258( chip ); -} - -void Okim6258_Emu::write( int addr, int data ) -{ - okim6258_write( chip, addr, data ); -} - -int Okim6258_Emu::get_clock() -{ - return okim6258_get_vclk( chip ); -} - -void Okim6258_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - okim6258_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Okim6258_Emu.h b/Frameworks/GME/gme/Okim6258_Emu.h deleted file mode 100644 index 5de3ba4f3..000000000 --- a/Frameworks/GME/gme/Okim6258_Emu.h +++ /dev/null @@ -1,32 +0,0 @@ -// OKIM6258 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef OKIM6258_EMU_H -#define OKIM6258_EMU_H - -class Okim6258_Emu { - void* chip; -public: - Okim6258_Emu(); - ~Okim6258_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock, int divider, int adpcm_type, int output_12bits ); - - // Resets to power-up state - void reset(); - - // Returns current sample rate of the chip - int get_clock(); - - // Writes data to addr - void write( int addr, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Okim6295_Emu.cpp b/Frameworks/GME/gme/Okim6295_Emu.cpp deleted file mode 100644 index 9d9c3e6c2..000000000 --- a/Frameworks/GME/gme/Okim6295_Emu.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Okim6295_Emu.h" -#include "okim6295.h" - -Okim6295_Emu::Okim6295_Emu() { chip = 0; } - -Okim6295_Emu::~Okim6295_Emu() -{ - if ( chip ) device_stop_okim6295( chip ); -} - -int Okim6295_Emu::set_rate( int clock_rate ) -{ - if ( chip ) - { - device_stop_okim6295( chip ); - chip = 0; - } - - chip = device_start_okim6295( clock_rate ); - if ( !chip ) - return 0; - - reset(); - return (clock_rate & 0x7FFFFFFF) / ((clock_rate & 0x80000000) ? 132 : 165); -} - -void Okim6295_Emu::reset() -{ - device_reset_okim6295( chip ); - okim6295_set_mute_mask( chip, 0 ); -} - -void Okim6295_Emu::write( int addr, int data ) -{ - okim6295_w( chip, addr, data ); -} - -void Okim6295_Emu::write_rom( int size, int start, int length, void * data ) -{ - okim6295_write_rom( chip, size, start, length, (const UINT8 *) data ); -} - -void Okim6295_Emu::mute_voices( int mask ) -{ - okim6295_set_mute_mask( chip, mask ); -} - -void Okim6295_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - okim6295_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Okim6295_Emu.h b/Frameworks/GME/gme/Okim6295_Emu.h deleted file mode 100644 index 3f307b085..000000000 --- a/Frameworks/GME/gme/Okim6295_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// OKIM6295 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef OKIM6295_EMU_H -#define OKIM6295_EMU_H - -class Okim6295_Emu { - void* chip; -public: - Okim6295_Emu(); - ~Okim6295_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 4 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Opl_Apu.cpp b/Frameworks/GME/gme/Opl_Apu.cpp index 0a3c45dd5..7253f1f11 100644 --- a/Frameworks/GME/gme/Opl_Apu.cpp +++ b/Frameworks/GME/gme/Opl_Apu.cpp @@ -2,8 +2,16 @@ #include "blargg_source.h" -#include "ym2413.h" -#include "fmopl.h" +extern "C" { +#include "../vgmplay/chips/mamedef.h" +#include "../vgmplay/chips/emu2413.h" +#include "../vgmplay/chips/fmopl.h" +} + +static unsigned char vrc7_inst[(16 + 3) * 8] = +{ +#include "../vgmplay/chips/vrc7tone.h" +}; Opl_Apu::Opl_Apu() { opl = 0; opl_memory = 0; } @@ -20,26 +28,29 @@ blargg_err_t Opl_Apu::init( long clock, long rate, blip_time_t period, type_t ty case type_opll: case type_msxmusic: case type_smsfmunit: - opl = ym2413_init( clock, rate, 0 ); + opl = OPLL_new( (BOOST::uint32_t) clock, (BOOST::uint32_t) rate ); + OPLL_SetChipMode( (OPLL *) opl, 0); break; case type_vrc7: - opl = ym2413_init( clock, rate, 1 ); + opl = OPLL_new( (BOOST::uint32_t) clock, (BOOST::uint32_t) rate ); + OPLL_SetChipMode((OPLL *) opl, 1 ); + OPLL_setPatch((OPLL *) opl, vrc7_inst); break; case type_opl: - opl = ym3526_init( clock, rate ); + opl = ym3526_init( (BOOST::uint32_t) clock, (BOOST::uint32_t) rate ); break; case type_msxaudio: //logfile = fopen("c:\\temp\\msxaudio.log", "wb"); - opl = y8950_init( clock, rate ); + opl = y8950_init( (BOOST::uint32_t) clock, (BOOST::uint32_t) rate ); opl_memory = malloc( 32768 ); y8950_set_delta_t_memory( opl, opl_memory, 32768 ); break; case type_opl2: - opl = ym3812_init( clock, rate ); + opl = ym3812_init( (BOOST::uint32_t) clock, (BOOST::uint32_t) rate ); break; } reset(); @@ -56,7 +67,7 @@ Opl_Apu::~Opl_Apu() case type_msxmusic: case type_smsfmunit: case type_vrc7: - ym2413_shutdown( opl ); + OPLL_delete( (OPLL *) opl ); break; case type_opl: @@ -88,7 +99,7 @@ void Opl_Apu::reset() case type_msxmusic: case type_smsfmunit: case type_vrc7: - ym2413_reset_chip( opl ); + OPLL_reset( (OPLL *) opl ); break; case type_opl: @@ -114,8 +125,8 @@ void Opl_Apu::write_data( blip_time_t time, int data ) case type_msxmusic: case type_smsfmunit: case type_vrc7: - ym2413_write( opl, 0, addr ); - ym2413_write( opl, 1, data ); + OPLL_writeIO( (OPLL *) opl, 0, addr ); + OPLL_writeIO( (OPLL *) opl, 1, data ); break; case type_opl: @@ -149,7 +160,7 @@ int Opl_Apu::read( blip_time_t time, int port ) case type_msxmusic: case type_smsfmunit: case type_vrc7: - return ym2413_read( opl, port ); + return port ? 0xFF : 0; case type_opl: return ym3526_read( opl, port ); @@ -192,15 +203,15 @@ void Opl_Apu::run_until( blip_time_t end_time ) case type_smsfmunit: case type_vrc7: { - SAMP bufMO[ 1024 ]; - SAMP bufRO[ 1024 ]; - SAMP * buffers[2] = { bufMO, bufRO }; + e_int32 bufMO[ 1024 ]; + e_int32 bufRO[ 1024 ]; + e_int32 * buffers[2] = { bufMO, bufRO }; while ( count > 0 ) { unsigned todo = count; if ( todo > 1024 ) todo = 1024; - ym2413_update_one( opl, buffers, todo ); + OPLL_calc_stereo( (OPLL *) opl, buffers, todo, -1 ); if ( output_ ) { @@ -229,7 +240,9 @@ void Opl_Apu::run_until( blip_time_t end_time ) case type_msxaudio: case type_opl2: { - OPLSAMPLE buffer[ 1024 ]; + OPLSAMPLE bufL[ 1024 ]; + OPLSAMPLE bufR[ 1024 ]; + OPLSAMPLE* buffers[2] = {bufL, bufR}; while ( count > 0 ) { @@ -237,18 +250,18 @@ void Opl_Apu::run_until( blip_time_t end_time ) if ( todo > 1024 ) todo = 1024; switch (type_) { - case type_opl: ym3526_update_one( opl, buffer, todo ); break; - case type_msxaudio: y8950_update_one( opl, buffer, todo ); break; - case type_opl2: ym3812_update_one( opl, buffer, todo ); break; - default: break; - } - - if ( output_ ) + case type_opl: ym3526_update_one( opl, buffers, todo ); break; + case type_msxaudio: y8950_update_one( opl, buffers, todo ); break; + case type_opl2: ym3812_update_one( opl, buffers, todo ); break; + default: break; + } + + if ( output_ ) { int last_amp = this->last_amp; for ( unsigned i = 0; i < todo; i++ ) { - int amp = buffer [i]; + int amp = bufL [i] + bufR [i]; int delta = amp - last_amp; if ( delta ) { diff --git a/Frameworks/GME/gme/Pwm_Emu.cpp b/Frameworks/GME/gme/Pwm_Emu.cpp deleted file mode 100644 index 7bb9722d1..000000000 --- a/Frameworks/GME/gme/Pwm_Emu.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Pwm_Emu.h" -#include "pwm.h" - -Pwm_Emu::Pwm_Emu() { chip = 0; } - -Pwm_Emu::~Pwm_Emu() -{ - if ( chip ) device_stop_pwm( chip ); -} - -int Pwm_Emu::set_rate( int clock ) -{ - if ( chip ) - { - device_stop_pwm( chip ); - chip = 0; - } - - chip = device_start_pwm( clock ); - if ( !chip ) - return 1; - - reset(); - return 0; -} - -void Pwm_Emu::reset() -{ - device_reset_pwm( chip ); -} - -void Pwm_Emu::write( int channel, int data ) -{ - pwm_chn_w( chip, channel, data ); -} - -void Pwm_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - pwm_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Pwm_Emu.h b/Frameworks/GME/gme/Pwm_Emu.h deleted file mode 100644 index 1ba1074cd..000000000 --- a/Frameworks/GME/gme/Pwm_Emu.h +++ /dev/null @@ -1,33 +0,0 @@ -// PWM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef PWM_EMU_H -#define PWM_EMU_H - -class Pwm_Emu { - void* chip; -public: - Pwm_Emu(); - ~Pwm_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 24 }; - void mute_voices( int mask ); - - // Writes data to channel - void write( int channel, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Qsound_Apu.cpp b/Frameworks/GME/gme/Qsound_Apu.cpp deleted file mode 100644 index 8a530dc81..000000000 --- a/Frameworks/GME/gme/Qsound_Apu.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Qsound_Apu.h" -#include "qmix.h" - -Qsound_Apu::Qsound_Apu() { chip = 0; rom = 0; rom_size = 0; sample_rate = 0; } - -Qsound_Apu::~Qsound_Apu() -{ - if ( chip ) free( chip ); - if ( rom ) free( rom ); -} - -int Qsound_Apu::set_rate( int clock_rate ) -{ - if ( chip ) - { - free( chip ); - chip = 0; - } - - chip = malloc( _qmix_get_state_size() ); - if ( !chip ) - return 0; - - reset(); - - return clock_rate / 166; -} - -void Qsound_Apu::set_sample_rate( int sample_rate ) -{ - this->sample_rate = sample_rate; - if ( chip ) _qmix_set_sample_rate( chip, sample_rate ); -} - -void Qsound_Apu::reset() -{ - _qmix_clear_state( chip ); - _qmix_set_sample_rate( chip, sample_rate ); - if ( rom ) _qmix_set_sample_rom( chip, rom, rom_size ); -} - -void Qsound_Apu::write( int addr, int data ) -{ - _qmix_command( chip, addr, data ); -} - -void Qsound_Apu::write_rom( int size, int start, int length, void const* data ) -{ - if ( size > rom_size ) - { - rom_size = size; - rom = realloc( rom, size ); - } - if ( start > size ) start = size; - if ( start + length > size ) length = size - start; - memcpy( (uint8*)rom + start, data, length ); - if ( chip ) _qmix_set_sample_rom( chip, rom, rom_size ); -} - -void Qsound_Apu::run( int pair_count, sample_t* out ) -{ - sint16 buf[ 1024 * 2 ]; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - _qmix_render( chip, buf, todo ); - - for (int i = 0; i < todo * 2; i++) - { - int output = buf [i]; - output += out [0]; - if ( (short)output != output ) output = 0x7FFF ^ ( output >> 31 ); - out [0] = output; - out++; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Qsound_Apu.h b/Frameworks/GME/gme/Qsound_Apu.h deleted file mode 100644 index 8cbea22b1..000000000 --- a/Frameworks/GME/gme/Qsound_Apu.h +++ /dev/null @@ -1,36 +0,0 @@ -// Capcom QSound sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef QSOUND_APU_H -#define QSOUND_APU_H - -class Qsound_Apu { - void* chip; - void* rom; - int rom_size; - int sample_rate; -public: - Qsound_Apu(); - ~Qsound_Apu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock_rate ); - void set_sample_rate( int sample_rate ); - - // Resets to power-up state - void reset(); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void const* data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Resampler.cpp b/Frameworks/GME/gme/Resampler.cpp index 16a0626b8..5fbb39107 100644 --- a/Frameworks/GME/gme/Resampler.cpp +++ b/Frameworks/GME/gme/Resampler.cpp @@ -1,79 +1,79 @@ -// $package. http://www.slack.net/~ant/ - -#include "Resampler.h" - -/* Copyright (C) 2004-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Resampler::Resampler() -{ - write_pos = 0; - rate_ = 0; -} - -Resampler::~Resampler() { } - -void Resampler::clear() -{ - write_pos = 0; - clear_(); -} - -inline int Resampler::resample_wrapper( sample_t out [], int* out_size, - sample_t const in [], int in_size ) -{ - assert( rate() ); - - sample_t* out_ = out; - int result = resample_( &out_, out + *out_size, in, in_size ) - in; - assert( out_ <= out + *out_size ); - assert( result <= in_size ); - - *out_size = out_ - out; - return result; -} - -int Resampler::resample( sample_t out [], int out_size, sample_t const in [], int* in_size ) -{ - *in_size = resample_wrapper( out, &out_size, in, *in_size ); - return out_size; -} - - -//// Buffering - -blargg_err_t Resampler::resize_buffer( int new_size ) -{ - RETURN_ERR( buf.resize( new_size ) ); - clear(); - return blargg_ok; -} - -int Resampler::skip_input( int count ) -{ - write_pos -= count; - if ( write_pos < 0 ) // occurs when downsampling - { - count += write_pos; - write_pos = 0; - } - memmove( buf.begin(), &buf [count], write_pos * sizeof buf [0] ); - return count; -} - -int Resampler::read( sample_t out [], int out_size ) -{ - if ( out_size ) - skip_input( resample_wrapper( out, &out_size, buf.begin(), write_pos ) ); - return out_size; -} +// $package. http://www.slack.net/~ant/ + +#include "Resampler.h" + +/* Copyright (C) 2004-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Resampler::Resampler() +{ + write_pos = 0; + rate_ = 0; +} + +Resampler::~Resampler() { } + +void Resampler::clear() +{ + write_pos = 0; + clear_(); +} + +inline int Resampler::resample_wrapper( sample_t out [], int* out_size, + sample_t const in [], int in_size ) +{ + assert( rate() ); + + sample_t* out_ = out; + int result = resample_( &out_, out + *out_size, in, in_size ) - in; + assert( out_ <= out + *out_size ); + assert( result <= in_size ); + + *out_size = out_ - out; + return result; +} + +int Resampler::resample( sample_t out [], int out_size, sample_t const in [], int* in_size ) +{ + *in_size = resample_wrapper( out, &out_size, in, *in_size ); + return out_size; +} + + +//// Buffering + +blargg_err_t Resampler::resize_buffer( int new_size ) +{ + RETURN_ERR( buf.resize( new_size ) ); + clear(); + return blargg_ok; +} + +int Resampler::skip_input( int count ) +{ + write_pos -= count; + if ( write_pos < 0 ) // occurs when downsampling + { + count += write_pos; + write_pos = 0; + } + memmove( buf.begin(), &buf [count], write_pos * sizeof buf [0] ); + return count; +} + +int Resampler::read( sample_t out [], int out_size ) +{ + if ( out_size ) + skip_input( resample_wrapper( out, &out_size, buf.begin(), write_pos ) ); + return out_size; +} diff --git a/Frameworks/GME/gme/Resampler.h b/Frameworks/GME/gme/Resampler.h index ff285121d..2ed806afd 100644 --- a/Frameworks/GME/gme/Resampler.h +++ b/Frameworks/GME/gme/Resampler.h @@ -1,110 +1,110 @@ -// Common interface for resamplers - -// $package -#ifndef RESAMPLER_H -#define RESAMPLER_H - -#include "blargg_common.h" - -class Resampler { -public: - - virtual ~Resampler(); - - // Sets input/output resampling ratio - blargg_err_t set_rate( double ); - - // Current input/output ratio - double rate() const { return rate_; } - - // Samples are 16-bit signed - typedef short sample_t; - -// One of two different buffering schemes can be used, as decided by the caller: - -// External buffering (caller provides input buffer) - - // Resamples in to at most n out samples and returns number of samples actually - // written. Sets *in_size to number of input samples that aren't needed anymore - // and should be removed from input. - int resample( sample_t out [], int n, sample_t const in [], int* in_size ); - -// Internal buffering (resampler manages buffer) - - // Resizes input buffer to n samples, then clears it - blargg_err_t resize_buffer( int n ); - - // Clears input buffer - void clear(); - - // Writes at most n samples to input buffer and returns number actually written. - // Result will be less than n if there isn't enough free space in buffer. - int write( sample_t const in [], int n ); - - // Number of input samples in buffer - int written() const { return write_pos; } - - // Removes first n input samples from buffer, fewer if there aren't that many. - // Returns number of samples actually removed. - int skip_input( int n ); - - // Resamples input to at most n output samples. Returns number of samples - // actually written to out. Result will be less than n if there aren't - // enough input samples in buffer. - int read( sample_t out [], int n ); - -// Direct writing to input buffer, instead of using write( in, n ) above - - // Pointer to place to write input samples - sample_t* buffer() { return &buf [write_pos]; } - - // Number of samples that can be written to buffer() - int buffer_free() const { return buf.size() - write_pos; } - - // Notifies resampler that n input samples have been written to buffer(). - // N must not be greater than buffer_free(). - void write( int n ); - -// Derived interface -protected: - virtual blargg_err_t set_rate_( double rate ) BLARGG_PURE( ; ) - - virtual void clear_() { } - - // Resample as many available in samples as will fit within out_size and - // return pointer past last input sample read and set *out just past - // the last output sample. - virtual sample_t const* resample_( sample_t** out, sample_t const* out_end, - sample_t const in [], int in_size ) BLARGG_PURE( { return in; } ) - -// Implementation -public: - Resampler(); - -private: - blargg_vector buf; - int write_pos; - double rate_; - - int resample_wrapper( sample_t out [], int* out_size, - sample_t const in [], int in_size ); -}; - -inline void Resampler::write( int count ) -{ - write_pos += count; - assert( (unsigned) write_pos <= buf.size() ); -} - -inline blargg_err_t Resampler::set_rate_( double r ) -{ - rate_ = r; - return blargg_ok; -} - -inline blargg_err_t Resampler::set_rate( double r ) -{ - return set_rate_( r ); -} - -#endif +// Common interface for resamplers + +// $package +#ifndef RESAMPLER_H +#define RESAMPLER_H + +#include "blargg_common.h" + +class Resampler { +public: + + virtual ~Resampler(); + + // Sets input/output resampling ratio + blargg_err_t set_rate( double ); + + // Current input/output ratio + double rate() const { return rate_; } + + // Samples are 16-bit signed + typedef short sample_t; + +// One of two different buffering schemes can be used, as decided by the caller: + +// External buffering (caller provides input buffer) + + // Resamples in to at most n out samples and returns number of samples actually + // written. Sets *in_size to number of input samples that aren't needed anymore + // and should be removed from input. + int resample( sample_t out [], int n, sample_t const in [], int* in_size ); + +// Internal buffering (resampler manages buffer) + + // Resizes input buffer to n samples, then clears it + blargg_err_t resize_buffer( int n ); + + // Clears input buffer + void clear(); + + // Writes at most n samples to input buffer and returns number actually written. + // Result will be less than n if there isn't enough free space in buffer. + int write( sample_t const in [], int n ); + + // Number of input samples in buffer + int written() const { return write_pos; } + + // Removes first n input samples from buffer, fewer if there aren't that many. + // Returns number of samples actually removed. + int skip_input( int n ); + + // Resamples input to at most n output samples. Returns number of samples + // actually written to out. Result will be less than n if there aren't + // enough input samples in buffer. + int read( sample_t out [], int n ); + +// Direct writing to input buffer, instead of using write( in, n ) above + + // Pointer to place to write input samples + sample_t* buffer() { return &buf [write_pos]; } + + // Number of samples that can be written to buffer() + int buffer_free() const { return buf.size() - write_pos; } + + // Notifies resampler that n input samples have been written to buffer(). + // N must not be greater than buffer_free(). + void write( int n ); + +// Derived interface +protected: + virtual blargg_err_t set_rate_( double rate ) BLARGG_PURE( ; ) + + virtual void clear_() { } + + // Resample as many available in samples as will fit within out_size and + // return pointer past last input sample read and set *out just past + // the last output sample. + virtual sample_t const* resample_( sample_t** out, sample_t const* out_end, + sample_t const in [], int in_size ) BLARGG_PURE( { return in; } ) + +// Implementation +public: + Resampler(); + +private: + blargg_vector buf; + int write_pos; + double rate_; + + int resample_wrapper( sample_t out [], int* out_size, + sample_t const in [], int in_size ); +}; + +inline void Resampler::write( int count ) +{ + write_pos += count; + assert( (unsigned) write_pos <= buf.size() ); +} + +inline blargg_err_t Resampler::set_rate_( double r ) +{ + rate_ = r; + return blargg_ok; +} + +inline blargg_err_t Resampler::set_rate( double r ) +{ + return set_rate_( r ); +} + +#endif diff --git a/Frameworks/GME/gme/Rf5C164_Emu.cpp b/Frameworks/GME/gme/Rf5C164_Emu.cpp deleted file mode 100644 index 605729c6d..000000000 --- a/Frameworks/GME/gme/Rf5C164_Emu.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Rf5C164_Emu.h" -#include "scd_pcm.h" - -Rf5C164_Emu::Rf5C164_Emu() { chip = 0; } - -Rf5C164_Emu::~Rf5C164_Emu() -{ - if ( chip ) device_stop_rf5c164( chip ); -} - -int Rf5C164_Emu::set_rate( int clock ) -{ - if ( chip ) - { - device_stop_rf5c164( chip ); - chip = 0; - } - - chip = device_start_rf5c164( clock ); - if ( !chip ) - return 1; - - reset(); - return 0; -} - -void Rf5C164_Emu::reset() -{ - device_reset_rf5c164( chip ); - rf5c164_set_mute_mask( chip, 0 ); -} - -void Rf5C164_Emu::write( int addr, int data ) -{ - rf5c164_w( chip, addr, data ); -} - -void Rf5C164_Emu::write_mem( int addr, int data ) -{ - rf5c164_mem_w( chip, addr, data ); -} - -void Rf5C164_Emu::write_ram( int start, int length, void * data ) -{ - rf5c164_write_ram( chip, start, length, (const UINT8 *) data ); -} - -void Rf5C164_Emu::mute_voices( int mask ) -{ - rf5c164_set_mute_mask( chip, mask ); -} - -void Rf5C164_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - rf5c164_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Rf5C164_Emu.h b/Frameworks/GME/gme/Rf5C164_Emu.h deleted file mode 100644 index 25abf8dac..000000000 --- a/Frameworks/GME/gme/Rf5C164_Emu.h +++ /dev/null @@ -1,39 +0,0 @@ -// RF5C164 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef RF5C164_EMU_H -#define RF5C164_EMU_H - -class Rf5C164_Emu { - void* chip; -public: - Rf5C164_Emu(); - ~Rf5C164_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 8 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Writes to memory - void write_mem( int addr, int data ); - - // Writes length bytes from data at start offset in RAM - void write_ram( int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Rf5C68_Emu.cpp b/Frameworks/GME/gme/Rf5C68_Emu.cpp deleted file mode 100644 index 46d452438..000000000 --- a/Frameworks/GME/gme/Rf5C68_Emu.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Rf5C68_Emu.h" -#include "rf5c68.h" - -Rf5C68_Emu::Rf5C68_Emu() { chip = 0; } - -Rf5C68_Emu::~Rf5C68_Emu() -{ - if ( chip ) device_stop_rf5c68( chip ); -} - -int Rf5C68_Emu::set_rate() -{ - if ( chip ) - { - device_stop_rf5c68( chip ); - chip = 0; - } - - chip = device_start_rf5c68(); - if ( !chip ) - return 1; - - reset(); - return 0; -} - -void Rf5C68_Emu::reset() -{ - device_reset_rf5c68( chip ); - rf5c68_set_mute_mask( chip, 0 ); -} - -void Rf5C68_Emu::write( int addr, int data ) -{ - rf5c68_w( chip, addr, data ); -} - -void Rf5C68_Emu::write_mem( int addr, int data ) -{ - rf5c68_mem_w( chip, addr, data ); -} - -void Rf5C68_Emu::write_ram( int start, int length, void * data ) -{ - rf5c68_write_ram( chip, start, length, (const UINT8 *) data ); -} - -void Rf5C68_Emu::mute_voices( int mask ) -{ - rf5c68_set_mute_mask( chip, mask ); -} - -void Rf5C68_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - rf5c68_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Rf5C68_Emu.h b/Frameworks/GME/gme/Rf5C68_Emu.h deleted file mode 100644 index e03332d9e..000000000 --- a/Frameworks/GME/gme/Rf5C68_Emu.h +++ /dev/null @@ -1,39 +0,0 @@ -// RF5C68 sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef RF5C68_EMU_H -#define RF5C68_EMU_H - -class Rf5C68_Emu { - void* chip; -public: - Rf5C68_Emu(); - ~Rf5C68_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate(); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 8 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Writes to memory - void write_mem( int addr, int data ); - - // Writes length bytes from data at start offset in RAM - void write_ram( int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Sap_Apu.cpp b/Frameworks/GME/gme/Sap_Apu.cpp index 6c7a202a4..df38ab72b 100644 --- a/Frameworks/GME/gme/Sap_Apu.cpp +++ b/Frameworks/GME/gme/Sap_Apu.cpp @@ -1,339 +1,339 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Sap_Apu.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const max_frequency = 12000; // pure waves above this frequency are silenced - -static void gen_poly( unsigned mask, int count, byte out [] ) -{ - unsigned n = 1; - do - { - int bits = 0; - int b = 0; - do - { - // implemented using "Galios configuration" - bits |= (n & 1) << b; - n = (n >> 1) ^ (mask * (n & 1)); - } - while ( b++ < 7 ); - *out++ = bits; - } - while ( --count ); -} - -// poly5 -int const poly5_len = (1 << 5) - 1; -unsigned const poly5_mask = (1U << poly5_len) - 1; -unsigned const poly5 = 0x167C6EA1; - -inline unsigned run_poly5( unsigned in, int shift ) -{ - return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); -} - -#define POLY_MASK( width, tap1, tap2 ) \ - ((1U << (width - 1 - tap1)) | (1U << (width - 1 - tap2))) - -Sap_Apu_Impl::Sap_Apu_Impl() -{ - gen_poly( POLY_MASK( 4, 1, 0 ), sizeof poly4, poly4 ); - gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 ); - gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 ); - - if ( 0 ) // comment out to recauculate poly5 constant - { - byte poly5 [4]; - gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 ); - unsigned n = poly5 [3] * 0x1000000 + poly5 [2] * 0x10000 + - poly5 [1] * 0x100 + poly5 [0]; - unsigned rev = n & 1; - for ( int i = 1; i < poly5_len; i++ ) - rev |= (n >> i & 1) << (poly5_len - i); - dprintf( "poly5: 0x%08lX\n", rev ); - } -} - -void Sap_Apu::set_output( Blip_Buffer* b ) -{ - for ( int i = 0; i < osc_count; ++i ) - set_output( i, b ); -} - -Sap_Apu::Sap_Apu() -{ - impl = NULL; - set_output( NULL ); -} - -void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) -{ - impl = new_impl; - last_time = 0; - poly5_pos = 0; - poly4_pos = 0; - polym_pos = 0; - control = 0; - - for ( int i = 0; i < osc_count; i++ ) - memset( &oscs [i], 0, offsetof (osc_t,output) ); -} - -inline void Sap_Apu::calc_periods() -{ - // 15/64 kHz clock - int divider = 28; - if ( this->control & 1 ) - divider = 114; - - for ( int i = 0; i < osc_count; i++ ) - { - osc_t* const osc = &oscs [i]; - - int const osc_reload = osc->regs [0]; // cache - int period = (osc_reload + 1) * divider; - static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 }; - if ( this->control & fast_bits [i] ) - { - period = osc_reload + 4; - if ( i & 1 ) - { - period = osc_reload * 0x100 + osc [-1].regs [0] + 7; - if ( !(this->control & fast_bits [i - 1]) ) - period = (period - 6) * divider; - - if ( (osc [-1].regs [1] & 0x1F) > 0x10 ) - dprintf( "Use of slave channel in 16-bit mode not supported\n" ); - } - } - osc->period = period; - } -} - -void Sap_Apu::run_until( blip_time_t end_time ) -{ - calc_periods(); - Sap_Apu_Impl* const impl = this->impl; // cache - - // 17/9-bit poly selection - byte const* polym = impl->poly17; - int polym_len = poly17_len; - if ( this->control & 0x80 ) - { - polym_len = poly9_len; - polym = impl->poly9; - } - polym_pos %= polym_len; - - for ( int i = 0; i < osc_count; i++ ) - { - osc_t* const osc = &oscs [i]; - blip_time_t time = last_time + osc->delay; - blip_time_t const period = osc->period; - - // output - Blip_Buffer* output = osc->output; - if ( output ) - { - int const osc_control = osc->regs [1]; // cache - int volume = (osc_control & 0x0F) * 2; - if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency - ((osc_control & 0xA0) == 0xA0 && period < 1789773 / 2 / max_frequency) ) - { - if ( !(osc_control & 0x10) ) - volume >>= 1; // inaudible frequency = half volume - - int delta = volume - osc->last_amp; - if ( delta ) - { - osc->last_amp = volume; - output->set_modified(); - impl->synth.offset( last_time, delta, output ); - } - - // TODO: doesn't maintain high pass flip-flop (very minor issue) - } - else - { - // high pass - static byte const hipass_bits [osc_count] = { 1 << 2, 1 << 1, 0, 0 }; - blip_time_t period2 = 0; // unused if no high pass - blip_time_t time2 = end_time; - if ( this->control & hipass_bits [i] ) - { - period2 = osc [2].period; - time2 = last_time + osc [2].delay; - if ( osc->invert ) - { - // trick inner wave loop into inverting output - osc->last_amp -= volume; - volume = -volume; - } - } - - if ( time < end_time || time2 < end_time ) - { - // poly source - static byte const poly1 [] = { 0x55, 0x55 }; // square wave - byte const* poly = poly1; - int poly_len = 8 * sizeof poly1; // can be just 2 bits, but this is faster - int poly_pos = osc->phase & 1; - int poly_inc = 1; - if ( !(osc_control & 0x20) ) - { - poly = polym; - poly_len = polym_len; - poly_pos = polym_pos; - if ( osc_control & 0x40 ) - { - poly = impl->poly4; - poly_len = poly4_len; - poly_pos = poly4_pos; - } - poly_inc = period % poly_len; - poly_pos = (poly_pos + osc->delay) % poly_len; - } - poly_inc -= poly_len; // allows more optimized inner loop below - - // square/poly5 wave - unsigned wave = poly5; - check( poly5 & 1 ); // low bit is set for pure wave - int poly5_inc = 0; - if ( !(osc_control & 0x80) ) - { - wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len ); - poly5_inc = period % poly5_len; - } - - output->set_modified(); - - // Run wave and high pass interleved with each catching up to the other. - // Disabled high pass has no performance effect since inner wave loop - // makes no compromise for high pass, and only runs once in that case. - int osc_last_amp = osc->last_amp; - do - { - // run high pass - if ( time2 < time ) - { - int delta = -osc_last_amp; - if ( volume < 0 ) - delta += volume; - if ( delta ) - { - osc_last_amp += delta - volume; - volume = -volume; - impl->synth.offset( time2, delta, output ); - } - } - while ( time2 <= time ) // must advance *past* time to avoid hang - time2 += period2; - - // run wave - blip_time_t end = end_time; - if ( end > time2 ) - end = time2; - while ( time < end ) - { - if ( wave & 1 ) - { - int amp = volume * (poly [poly_pos >> 3] >> (poly_pos & 7) & 1); - if ( (poly_pos += poly_inc) < 0 ) - poly_pos += poly_len; - int delta = amp - osc_last_amp; - if ( delta ) - { - osc_last_amp = amp; - impl->synth.offset( time, delta, output ); - } - } - wave = run_poly5( wave, poly5_inc ); - time += period; - } - } - while ( time < end_time || time2 < end_time ); - - osc->phase = poly_pos; - osc->last_amp = osc_last_amp; - } - - osc->invert = 0; - if ( volume < 0 ) - { - // undo inversion trickery - osc->last_amp -= volume; - osc->invert = 1; - } - } - } - - // maintain divider - blip_time_t remain = end_time - time; - if ( remain > 0 ) - { - int count = (remain + period - 1) / period; - osc->phase ^= count; - time += count * period; - } - osc->delay = time - end_time; - } - - // advance polies - blip_time_t duration = end_time - last_time; - last_time = end_time; - poly4_pos = (poly4_pos + duration) % poly4_len; - poly5_pos = (poly5_pos + duration) % poly5_len; - polym_pos += duration; // will get %'d on next call -} - -void Sap_Apu::write_data( blip_time_t time, int addr, int data ) -{ - run_until( time ); - int i = (addr - 0xD200) >> 1; - if ( (unsigned) i < osc_count ) - { - oscs [i].regs [addr & 1] = data; - } - else if ( addr == 0xD208 ) - { - control = data; - } - else if ( addr == 0xD209 ) - { - oscs [0].delay = 0; - oscs [1].delay = 0; - oscs [2].delay = 0; - oscs [3].delay = 0; - } - /* - // TODO: are polynomials reset in this case? - else if ( addr == 0xD20F ) - { - if ( (data & 3) == 0 ) - polym_pos = 0; - } - */ -} - -void Sap_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - last_time -= end_time; - assert( last_time >= 0 ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Sap_Apu.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const max_frequency = 12000; // pure waves above this frequency are silenced + +static void gen_poly( unsigned mask, int count, byte out [] ) +{ + unsigned n = 1; + do + { + int bits = 0; + int b = 0; + do + { + // implemented using "Galios configuration" + bits |= (n & 1) << b; + n = (n >> 1) ^ (mask * (n & 1)); + } + while ( b++ < 7 ); + *out++ = bits; + } + while ( --count ); +} + +// poly5 +int const poly5_len = (1 << 5) - 1; +unsigned const poly5_mask = (1U << poly5_len) - 1; +unsigned const poly5 = 0x167C6EA1; + +inline unsigned run_poly5( unsigned in, int shift ) +{ + return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); +} + +#define POLY_MASK( width, tap1, tap2 ) \ + ((1U << (width - 1 - tap1)) | (1U << (width - 1 - tap2))) + +Sap_Apu_Impl::Sap_Apu_Impl() +{ + gen_poly( POLY_MASK( 4, 1, 0 ), sizeof poly4, poly4 ); + gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 ); + gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 ); + + if ( 0 ) // comment out to recauculate poly5 constant + { + byte poly5 [4]; + gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 ); + unsigned n = poly5 [3] * 0x1000000 + poly5 [2] * 0x10000 + + poly5 [1] * 0x100 + poly5 [0]; + unsigned rev = n & 1; + for ( int i = 1; i < poly5_len; i++ ) + rev |= (n >> i & 1) << (poly5_len - i); + dprintf( "poly5: 0x%08lX\n", rev ); + } +} + +void Sap_Apu::set_output( Blip_Buffer* b ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, b ); +} + +Sap_Apu::Sap_Apu() +{ + impl = NULL; + set_output( NULL ); +} + +void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) +{ + impl = new_impl; + last_time = 0; + poly5_pos = 0; + poly4_pos = 0; + polym_pos = 0; + control = 0; + + for ( int i = 0; i < osc_count; i++ ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); +} + +inline void Sap_Apu::calc_periods() +{ + // 15/64 kHz clock + int divider = 28; + if ( this->control & 1 ) + divider = 114; + + for ( int i = 0; i < osc_count; i++ ) + { + osc_t* const osc = &oscs [i]; + + int const osc_reload = osc->regs [0]; // cache + int period = (osc_reload + 1) * divider; + static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 }; + if ( this->control & fast_bits [i] ) + { + period = osc_reload + 4; + if ( i & 1 ) + { + period = osc_reload * 0x100 + osc [-1].regs [0] + 7; + if ( !(this->control & fast_bits [i - 1]) ) + period = (period - 6) * divider; + + if ( (osc [-1].regs [1] & 0x1F) > 0x10 ) + dprintf( "Use of slave channel in 16-bit mode not supported\n" ); + } + } + osc->period = period; + } +} + +void Sap_Apu::run_until( blip_time_t end_time ) +{ + calc_periods(); + Sap_Apu_Impl* const impl = this->impl; // cache + + // 17/9-bit poly selection + byte const* polym = impl->poly17; + int polym_len = poly17_len; + if ( this->control & 0x80 ) + { + polym_len = poly9_len; + polym = impl->poly9; + } + polym_pos %= polym_len; + + for ( int i = 0; i < osc_count; i++ ) + { + osc_t* const osc = &oscs [i]; + blip_time_t time = last_time + osc->delay; + blip_time_t const period = osc->period; + + // output + Blip_Buffer* output = osc->output; + if ( output ) + { + int const osc_control = osc->regs [1]; // cache + int volume = (osc_control & 0x0F) * 2; + if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency + ((osc_control & 0xA0) == 0xA0 && period < 1789773 / 2 / max_frequency) ) + { + if ( !(osc_control & 0x10) ) + volume >>= 1; // inaudible frequency = half volume + + int delta = volume - osc->last_amp; + if ( delta ) + { + osc->last_amp = volume; + output->set_modified(); + impl->synth.offset( last_time, delta, output ); + } + + // TODO: doesn't maintain high pass flip-flop (very minor issue) + } + else + { + // high pass + static byte const hipass_bits [osc_count] = { 1 << 2, 1 << 1, 0, 0 }; + blip_time_t period2 = 0; // unused if no high pass + blip_time_t time2 = end_time; + if ( this->control & hipass_bits [i] ) + { + period2 = osc [2].period; + time2 = last_time + osc [2].delay; + if ( osc->invert ) + { + // trick inner wave loop into inverting output + osc->last_amp -= volume; + volume = -volume; + } + } + + if ( time < end_time || time2 < end_time ) + { + // poly source + static byte const poly1 [] = { 0x55, 0x55 }; // square wave + byte const* poly = poly1; + int poly_len = 8 * sizeof poly1; // can be just 2 bits, but this is faster + int poly_pos = osc->phase & 1; + int poly_inc = 1; + if ( !(osc_control & 0x20) ) + { + poly = polym; + poly_len = polym_len; + poly_pos = polym_pos; + if ( osc_control & 0x40 ) + { + poly = impl->poly4; + poly_len = poly4_len; + poly_pos = poly4_pos; + } + poly_inc = period % poly_len; + poly_pos = (poly_pos + osc->delay) % poly_len; + } + poly_inc -= poly_len; // allows more optimized inner loop below + + // square/poly5 wave + unsigned wave = poly5; + check( poly5 & 1 ); // low bit is set for pure wave + int poly5_inc = 0; + if ( !(osc_control & 0x80) ) + { + wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len ); + poly5_inc = period % poly5_len; + } + + output->set_modified(); + + // Run wave and high pass interleved with each catching up to the other. + // Disabled high pass has no performance effect since inner wave loop + // makes no compromise for high pass, and only runs once in that case. + int osc_last_amp = osc->last_amp; + do + { + // run high pass + if ( time2 < time ) + { + int delta = -osc_last_amp; + if ( volume < 0 ) + delta += volume; + if ( delta ) + { + osc_last_amp += delta - volume; + volume = -volume; + impl->synth.offset( time2, delta, output ); + } + } + while ( time2 <= time ) // must advance *past* time to avoid hang + time2 += period2; + + // run wave + blip_time_t end = end_time; + if ( end > time2 ) + end = time2; + while ( time < end ) + { + if ( wave & 1 ) + { + int amp = volume * (poly [poly_pos >> 3] >> (poly_pos & 7) & 1); + if ( (poly_pos += poly_inc) < 0 ) + poly_pos += poly_len; + int delta = amp - osc_last_amp; + if ( delta ) + { + osc_last_amp = amp; + impl->synth.offset( time, delta, output ); + } + } + wave = run_poly5( wave, poly5_inc ); + time += period; + } + } + while ( time < end_time || time2 < end_time ); + + osc->phase = poly_pos; + osc->last_amp = osc_last_amp; + } + + osc->invert = 0; + if ( volume < 0 ) + { + // undo inversion trickery + osc->last_amp -= volume; + osc->invert = 1; + } + } + } + + // maintain divider + blip_time_t remain = end_time - time; + if ( remain > 0 ) + { + int count = (remain + period - 1) / period; + osc->phase ^= count; + time += count * period; + } + osc->delay = time - end_time; + } + + // advance polies + blip_time_t duration = end_time - last_time; + last_time = end_time; + poly4_pos = (poly4_pos + duration) % poly4_len; + poly5_pos = (poly5_pos + duration) % poly5_len; + polym_pos += duration; // will get %'d on next call +} + +void Sap_Apu::write_data( blip_time_t time, int addr, int data ) +{ + run_until( time ); + int i = (addr - 0xD200) >> 1; + if ( (unsigned) i < osc_count ) + { + oscs [i].regs [addr & 1] = data; + } + else if ( addr == 0xD208 ) + { + control = data; + } + else if ( addr == 0xD209 ) + { + oscs [0].delay = 0; + oscs [1].delay = 0; + oscs [2].delay = 0; + oscs [3].delay = 0; + } + /* + // TODO: are polynomials reset in this case? + else if ( addr == 0xD20F ) + { + if ( (data & 3) == 0 ) + polym_pos = 0; + } + */ +} + +void Sap_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + last_time -= end_time; + assert( last_time >= 0 ); +} diff --git a/Frameworks/GME/gme/Sap_Apu.h b/Frameworks/GME/gme/Sap_Apu.h index e53834332..f4c93963f 100644 --- a/Frameworks/GME/gme/Sap_Apu.h +++ b/Frameworks/GME/gme/Sap_Apu.h @@ -1,103 +1,103 @@ -// Atari POKEY sound chip emulator - -// Game_Music_Emu $vers -#ifndef SAP_APU_H -#define SAP_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Sap_Apu_Impl; - -class Sap_Apu { -public: -// Basics - - // Sets buffer to generate sound into, or 0 to mute - void set_output( Blip_Buffer* ); - - // Emulates to time t, then writes data to addr - void write_data( blip_time_t t, int addr, int data ); - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Same as set_output(), but for a particular channel - enum { osc_count = 4 }; - void set_output( int index, Blip_Buffer* ); - - // Resets sound chip and sets Sap_Apu_Impl - void reset( Sap_Apu_Impl* impl ); - - // Registers are at io_addr to io_addr+io_size-1 - enum { io_addr = 0xD200 }; - enum { io_size = 0x0A }; - -private: - // noncopyable - Sap_Apu( const Sap_Apu& ); - Sap_Apu& operator = ( const Sap_Apu& ); - -// Implementation -public: - Sap_Apu(); - -private: - struct osc_t - { - unsigned char regs [2]; - unsigned char phase; - unsigned char invert; - int last_amp; - blip_time_t delay; - blip_time_t period; // always recalculated before use; here for convenience - Blip_Buffer* output; - }; - osc_t oscs [osc_count]; - Sap_Apu_Impl* impl; - blip_time_t last_time; - int poly5_pos; - int poly4_pos; - int polym_pos; - int control; - - void calc_periods(); - void run_until( blip_time_t ); - - enum { poly4_len = (1 << 4) - 1 }; - enum { poly9_len = (1 << 9) - 1 }; - enum { poly17_len = (1 << 17) - 1 }; - friend class Sap_Apu_Impl; -}; - -// Common tables and Blip_Synth that can be shared among multiple Sap_Apu objects -class Sap_Apu_Impl { -public: - // Set treble with synth.treble_eq() - Blip_Synth_Norm synth; - - // Sets overall volume, where 1.0is normal - void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } - - -// Implementation -public: - Sap_Apu_Impl(); - -private: - BOOST::uint8_t poly4 [Sap_Apu::poly4_len /8 + 1]; - BOOST::uint8_t poly9 [Sap_Apu::poly9_len /8 + 1]; - BOOST::uint8_t poly17 [Sap_Apu::poly17_len/8 + 1]; - friend class Sap_Apu; -}; - -inline void Sap_Apu::set_output( int i, Blip_Buffer* b ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = b; -} - -#endif +// Atari POKEY sound chip emulator + +// Game_Music_Emu $vers +#ifndef SAP_APU_H +#define SAP_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Sap_Apu_Impl; + +class Sap_Apu { +public: +// Basics + + // Sets buffer to generate sound into, or 0 to mute + void set_output( Blip_Buffer* ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Same as set_output(), but for a particular channel + enum { osc_count = 4 }; + void set_output( int index, Blip_Buffer* ); + + // Resets sound chip and sets Sap_Apu_Impl + void reset( Sap_Apu_Impl* impl ); + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0xD200 }; + enum { io_size = 0x0A }; + +private: + // noncopyable + Sap_Apu( const Sap_Apu& ); + Sap_Apu& operator = ( const Sap_Apu& ); + +// Implementation +public: + Sap_Apu(); + +private: + struct osc_t + { + unsigned char regs [2]; + unsigned char phase; + unsigned char invert; + int last_amp; + blip_time_t delay; + blip_time_t period; // always recalculated before use; here for convenience + Blip_Buffer* output; + }; + osc_t oscs [osc_count]; + Sap_Apu_Impl* impl; + blip_time_t last_time; + int poly5_pos; + int poly4_pos; + int polym_pos; + int control; + + void calc_periods(); + void run_until( blip_time_t ); + + enum { poly4_len = (1 << 4) - 1 }; + enum { poly9_len = (1 << 9) - 1 }; + enum { poly17_len = (1 << 17) - 1 }; + friend class Sap_Apu_Impl; +}; + +// Common tables and Blip_Synth that can be shared among multiple Sap_Apu objects +class Sap_Apu_Impl { +public: + // Set treble with synth.treble_eq() + Blip_Synth_Norm synth; + + // Sets overall volume, where 1.0is normal + void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } + + +// Implementation +public: + Sap_Apu_Impl(); + +private: + BOOST::uint8_t poly4 [Sap_Apu::poly4_len /8 + 1]; + BOOST::uint8_t poly9 [Sap_Apu::poly9_len /8 + 1]; + BOOST::uint8_t poly17 [Sap_Apu::poly17_len/8 + 1]; + friend class Sap_Apu; +}; + +inline void Sap_Apu::set_output( int i, Blip_Buffer* b ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = b; +} + +#endif diff --git a/Frameworks/GME/gme/Sap_Core.cpp b/Frameworks/GME/gme/Sap_Core.cpp index daaaf4ddc..2aaad5083 100644 --- a/Frameworks/GME/gme/Sap_Core.cpp +++ b/Frameworks/GME/gme/Sap_Core.cpp @@ -1,192 +1,192 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Sap_Core.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const idle_addr = 0xD2D2; - -Sap_Core::Sap_Core() -{ - set_tempo( 1 ); -} - -void Sap_Core::push( int b ) -{ - mem.ram [0x100 + cpu.r.sp--] = (byte) b; -} - -void Sap_Core::jsr_then_stop( addr_t addr ) -{ - cpu.r.pc = addr; - - // Some rips pop three bytes off stack before RTS. - push( (idle_addr - 1) >> 8 ); - push( idle_addr - 1 ); - - // 3 bytes so that RTI or RTS will jump to idle_addr. - // RTI will use the first two bytes as the address, 0xD2D2. - // RTS will use the last two bytes, 0xD2D1, which it internally increments. - push( (idle_addr - 1) >> 8 ); - push( (idle_addr - 1) >> 8 ); - push( idle_addr - 1 ); -} - -// Runs routine and allows it up to one second to return -void Sap_Core::run_routine( addr_t addr ) -{ - jsr_then_stop( addr ); - run_cpu( lines_per_frame * base_scanline_period * 60 ); - check( cpu.r.pc == idle_addr ); - check( cpu.r.sp >= 0xFF - 6 ); -} - -inline void Sap_Core::call_init( int track ) -{ - cpu.r.a = track; - - switch ( info.type ) - { - case 'B': - run_routine( info.init_addr ); - break; - - case 'C': - cpu.r.a = 0x70; - cpu.r.x = info.music_addr&0xFF; - cpu.r.y = info.music_addr >> 8; - run_routine( info.play_addr + 3 ); - cpu.r.a = 0; - cpu.r.x = track; - run_routine( info.play_addr + 3 ); - break; - - case 'D': - check( info.fastplay == lines_per_frame ); - jsr_then_stop( info.init_addr ); - break; - } -} - -void Sap_Core::setup_ram() -{ - memset( &mem, 0, sizeof mem ); - - ram() [idle_addr] = cpu.halt_opcode; - - addr_t const irq_addr = idle_addr - 1; - ram() [irq_addr] = cpu.halt_opcode; - ram() [0xFFFE] = (byte) irq_addr; - ram() [0xFFFF] = irq_addr >> 8; -} - -blargg_err_t Sap_Core::start_track( int track, info_t const& new_info ) -{ - info = new_info; - - check( ram() [idle_addr] == cpu.halt_opcode ); - - apu_ .reset( &apu_impl_ ); - apu2_.reset( &apu_impl_ ); - - cpu.reset( ram() ); - - frame_start = 0; - next_play = play_period() * 4; - saved_state.pc = idle_addr; - - time_mask = 0; // disables sound during init - call_init( track ); - time_mask = ~0; - - return blargg_ok; -} - -blargg_err_t Sap_Core::run_until( time_t end ) -{ - while ( cpu.time() < end ) - { - time_t next = min( next_play, end ); - if ( (run_cpu( next ) && cpu.r.pc != idle_addr) || cpu.error_count() ) - // TODO: better error - return BLARGG_ERR( BLARGG_ERR_GENERIC, "Emulation error (illegal instruction)" ); - - if ( cpu.r.pc == idle_addr ) - { - if ( saved_state.pc == idle_addr ) - { - // no code to run until next play call - cpu.set_time( next ); - } - else - { - // play had interrupted non-returning init, so restore registers - // init routine was running - check( cpu.r.sp == saved_state.sp - 3 ); - cpu.r = saved_state; - saved_state.pc = idle_addr; - } - } - - if ( cpu.time() >= next_play ) - { - next_play += play_period(); - - if ( cpu.r.pc == idle_addr || info.type == 'D' ) - { - // Save state if init routine is still running - if ( cpu.r.pc != idle_addr ) - { - check( info.type == 'D' ); - check( saved_state.pc == idle_addr ); - saved_state = cpu.r; - } - - addr_t addr = info.play_addr; - if ( info.type == 'C' ) - addr += 6; - jsr_then_stop( addr ); - } - else - { - dprintf( "init/play hadn't returned before next play call\n" ); - } - } - } - return blargg_ok; -} - -blargg_err_t Sap_Core::end_frame( time_t end ) -{ - RETURN_ERR( run_until( end ) ); - - cpu.adjust_time( -end ); - - time_t frame_time = lines_per_frame * scanline_period; - while ( frame_start < end ) - frame_start += frame_time; - frame_start -= end + frame_time; - - if ( (next_play -= end) < 0 ) - { - next_play = 0; - check( false ); - } - - apu_.end_frame( end ); - if ( info.stereo ) - apu2_.end_frame( end ); - - return blargg_ok; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Sap_Core.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const idle_addr = 0xD2D2; + +Sap_Core::Sap_Core() +{ + set_tempo( 1 ); +} + +void Sap_Core::push( int b ) +{ + mem.ram [0x100 + cpu.r.sp--] = (byte) b; +} + +void Sap_Core::jsr_then_stop( addr_t addr ) +{ + cpu.r.pc = addr; + + // Some rips pop three bytes off stack before RTS. + push( (idle_addr - 1) >> 8 ); + push( idle_addr - 1 ); + + // 3 bytes so that RTI or RTS will jump to idle_addr. + // RTI will use the first two bytes as the address, 0xD2D2. + // RTS will use the last two bytes, 0xD2D1, which it internally increments. + push( (idle_addr - 1) >> 8 ); + push( (idle_addr - 1) >> 8 ); + push( idle_addr - 1 ); +} + +// Runs routine and allows it up to one second to return +void Sap_Core::run_routine( addr_t addr ) +{ + jsr_then_stop( addr ); + run_cpu( lines_per_frame * base_scanline_period * 60 ); + check( cpu.r.pc == idle_addr ); + check( cpu.r.sp >= 0xFF - 6 ); +} + +inline void Sap_Core::call_init( int track ) +{ + cpu.r.a = track; + + switch ( info.type ) + { + case 'B': + run_routine( info.init_addr ); + break; + + case 'C': + cpu.r.a = 0x70; + cpu.r.x = info.music_addr&0xFF; + cpu.r.y = info.music_addr >> 8; + run_routine( info.play_addr + 3 ); + cpu.r.a = 0; + cpu.r.x = track; + run_routine( info.play_addr + 3 ); + break; + + case 'D': + check( info.fastplay == lines_per_frame ); + jsr_then_stop( info.init_addr ); + break; + } +} + +void Sap_Core::setup_ram() +{ + memset( &mem, 0, sizeof mem ); + + ram() [idle_addr] = cpu.halt_opcode; + + addr_t const irq_addr = idle_addr - 1; + ram() [irq_addr] = cpu.halt_opcode; + ram() [0xFFFE] = (byte) irq_addr; + ram() [0xFFFF] = irq_addr >> 8; +} + +blargg_err_t Sap_Core::start_track( int track, info_t const& new_info ) +{ + info = new_info; + + check( ram() [idle_addr] == cpu.halt_opcode ); + + apu_ .reset( &apu_impl_ ); + apu2_.reset( &apu_impl_ ); + + cpu.reset( ram() ); + + frame_start = 0; + next_play = play_period() * 4; + saved_state.pc = idle_addr; + + time_mask = 0; // disables sound during init + call_init( track ); + time_mask = ~0; + + return blargg_ok; +} + +blargg_err_t Sap_Core::run_until( time_t end ) +{ + while ( cpu.time() < end ) + { + time_t next = min( next_play, end ); + if ( (run_cpu( next ) && cpu.r.pc != idle_addr) || cpu.error_count() ) + // TODO: better error + return BLARGG_ERR( BLARGG_ERR_GENERIC, "Emulation error (illegal instruction)" ); + + if ( cpu.r.pc == idle_addr ) + { + if ( saved_state.pc == idle_addr ) + { + // no code to run until next play call + cpu.set_time( next ); + } + else + { + // play had interrupted non-returning init, so restore registers + // init routine was running + check( cpu.r.sp == saved_state.sp - 3 ); + cpu.r = saved_state; + saved_state.pc = idle_addr; + } + } + + if ( cpu.time() >= next_play ) + { + next_play += play_period(); + + if ( cpu.r.pc == idle_addr || info.type == 'D' ) + { + // Save state if init routine is still running + if ( cpu.r.pc != idle_addr ) + { + check( info.type == 'D' ); + check( saved_state.pc == idle_addr ); + saved_state = cpu.r; + } + + addr_t addr = info.play_addr; + if ( info.type == 'C' ) + addr += 6; + jsr_then_stop( addr ); + } + else + { + dprintf( "init/play hadn't returned before next play call\n" ); + } + } + } + return blargg_ok; +} + +blargg_err_t Sap_Core::end_frame( time_t end ) +{ + RETURN_ERR( run_until( end ) ); + + cpu.adjust_time( -end ); + + time_t frame_time = lines_per_frame * scanline_period; + while ( frame_start < end ) + frame_start += frame_time; + frame_start -= end + frame_time; + + if ( (next_play -= end) < 0 ) + { + next_play = 0; + check( false ); + } + + apu_.end_frame( end ); + if ( info.stereo ) + apu2_.end_frame( end ); + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Sap_Core.h b/Frameworks/GME/gme/Sap_Core.h index e5703575a..1c1908824 100644 --- a/Frameworks/GME/gme/Sap_Core.h +++ b/Frameworks/GME/gme/Sap_Core.h @@ -1,91 +1,91 @@ -// Atari XL/XE SAP core CPU and RAM emulator - -// Game_Music_Emu $vers -#ifndef SAP_CORE_H -#define SAP_CORE_H - -#include "Sap_Apu.h" -#include "Nes_Cpu.h" - -class Sap_Core { -public: - - // Sound chips and common state - Sap_Apu& apu() { return apu_; } - Sap_Apu& apu2() { return apu2_; } - Sap_Apu_Impl& apu_impl() { return apu_impl_; } - - // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. - void set_tempo( double ); - - // Clears RAM and sets up default vectors, etc. - void setup_ram(); - - // 64K RAM to load file data blocks into - BOOST::uint8_t* ram() { return mem.ram; } - - // Calls init routine and configures playback. RAM must have been - // set up already. - struct info_t { - int init_addr; - int play_addr; - int music_addr; - int type; - int fastplay; - bool stereo; - }; - blargg_err_t start_track( int track, info_t const& ); - - // Ends time frame at time t, then begins new at time 0 - typedef Nes_Cpu::time_t time_t; // Clock count - blargg_err_t end_frame( time_t t ); - - -// Implementation -public: - Sap_Core(); - -private: - enum { base_scanline_period = 114 }; - enum { lines_per_frame = 312 }; - typedef Nes_Cpu::addr_t addr_t; - - time_t scanline_period; - time_t next_play; - time_t time_mask; - time_t frame_start; - Nes_Cpu cpu; - Nes_Cpu::registers_t saved_state; - info_t info; - Sap_Apu apu_; - Sap_Apu apu2_; - - // large items - struct { - BOOST::uint8_t padding1 [ 0x100]; - BOOST::uint8_t ram [0x10000]; - BOOST::uint8_t padding2 [ 0x100]; - } mem; // TODO: put on freestore - Sap_Apu_Impl apu_impl_; - - void push( int b ); - void jsr_then_stop( addr_t ); - void run_routine( addr_t ); - void call_init( int track ); - bool run_cpu( time_t end ); - int play_addr(); - int read_d40b(); - int read_mem( addr_t ); - void write_D2xx( int d2xx, int data ); - - time_t time() const { return cpu.time() & time_mask; } - blargg_err_t run_until( time_t t ); - time_t play_period() const { return info.fastplay * scanline_period; } -}; - -inline void Sap_Core::set_tempo( double t ) -{ - scanline_period = (int) (base_scanline_period / t + 0.5); -} - -#endif +// Atari XL/XE SAP core CPU and RAM emulator + +// Game_Music_Emu $vers +#ifndef SAP_CORE_H +#define SAP_CORE_H + +#include "Sap_Apu.h" +#include "Nes_Cpu.h" + +class Sap_Core { +public: + + // Sound chips and common state + Sap_Apu& apu() { return apu_; } + Sap_Apu& apu2() { return apu2_; } + Sap_Apu_Impl& apu_impl() { return apu_impl_; } + + // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. + void set_tempo( double ); + + // Clears RAM and sets up default vectors, etc. + void setup_ram(); + + // 64K RAM to load file data blocks into + BOOST::uint8_t* ram() { return mem.ram; } + + // Calls init routine and configures playback. RAM must have been + // set up already. + struct info_t { + int init_addr; + int play_addr; + int music_addr; + int type; + int fastplay; + bool stereo; + }; + blargg_err_t start_track( int track, info_t const& ); + + // Ends time frame at time t, then begins new at time 0 + typedef Nes_Cpu::time_t time_t; // Clock count + blargg_err_t end_frame( time_t t ); + + +// Implementation +public: + Sap_Core(); + +private: + enum { base_scanline_period = 114 }; + enum { lines_per_frame = 312 }; + typedef Nes_Cpu::addr_t addr_t; + + time_t scanline_period; + time_t next_play; + time_t time_mask; + time_t frame_start; + Nes_Cpu cpu; + Nes_Cpu::registers_t saved_state; + info_t info; + Sap_Apu apu_; + Sap_Apu apu2_; + + // large items + struct { + BOOST::uint8_t padding1 [ 0x100]; + BOOST::uint8_t ram [0x10000]; + BOOST::uint8_t padding2 [ 0x100]; + } mem; // TODO: put on freestore + Sap_Apu_Impl apu_impl_; + + void push( int b ); + void jsr_then_stop( addr_t ); + void run_routine( addr_t ); + void call_init( int track ); + bool run_cpu( time_t end ); + int play_addr(); + int read_d40b(); + int read_mem( addr_t ); + void write_D2xx( int d2xx, int data ); + + time_t time() const { return cpu.time() & time_mask; } + blargg_err_t run_until( time_t t ); + time_t play_period() const { return info.fastplay * scanline_period; } +}; + +inline void Sap_Core::set_tempo( double t ) +{ + scanline_period = (int) (base_scanline_period / t + 0.5); +} + +#endif diff --git a/Frameworks/GME/gme/Sap_Cpu.cpp b/Frameworks/GME/gme/Sap_Cpu.cpp index 24bd9b202..69028e73b 100644 --- a/Frameworks/GME/gme/Sap_Cpu.cpp +++ b/Frameworks/GME/gme/Sap_Cpu.cpp @@ -1,96 +1,96 @@ -// $package. http://www.slack.net/~ant/ - -#include "Sap_Core.h" - -#include "blargg_endian.h" - -//#define CPU_LOG_MAX 100000 -//#include "nes_cpu_log.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// functions defined in same file as CPU emulator to help compiler's optimizer - -int Sap_Core::read_d40b() -{ - //dprintf( "D40B read\n" ); - check( cpu.time() >= frame_start ); - return ((unsigned) (cpu.time() - frame_start) / scanline_period % lines_per_frame) / 2; -} - -void Sap_Core::write_D2xx( int d2xx, int data ) -{ - addr_t const base = 0xD200; - - if ( d2xx < apu_.io_size ) - { - apu_.write_data( time(), d2xx + base, data ); - return; - } - - if ( (unsigned) (d2xx - 0x10) < apu2_.io_size && info.stereo ) - { - apu2_.write_data( time(), d2xx + (base - 0x10), data ); - return; - } - - if ( d2xx == 0xD40A - base ) - { - dprintf( "D40A write\n" ); - time_t t = cpu.time(); - time_t into_line = (t - frame_start) % scanline_period; - cpu.set_end_time( t - into_line + scanline_period ); - return; - } - - if ( (d2xx & ~0x0010) != 0x0F || data != 0x03 ) - dprintf( "Unmapped write $%04X <- $%02X\n", d2xx + base, data ); -} - -inline int Sap_Core::read_mem( addr_t addr ) -{ - int result = mem.ram [addr]; - if ( addr == 0xD40B ) - result = read_d40b(); - else if ( (addr & 0xF900) == 0xD000 ) - dprintf( "Unmapped read $%04X\n", addr ); - return result; -} - - -#define READ_LOW( addr ) (ram [addr]) -#define WRITE_LOW( addr, data ) (ram [addr] = data) - -#define READ_MEM( addr ) read_mem( addr ) -#define WRITE_MEM( addr, data ) \ -{\ - ram [addr] = data;\ - int d2xx = addr - 0xD200;\ - if ( (unsigned) d2xx < 0x100 )\ - write_D2xx( d2xx, data );\ -} - -#define CPU cpu -#define FLAT_MEM ram - -#define CPU_BEGIN \ -bool Sap_Core::run_cpu( time_t end )\ -{\ - CPU.set_end_time( end );\ - byte* const ram = this->mem.ram; /* cache */ - - #include "Nes_Cpu_run.h" - - return cpu.time_past_end() < 0; -} +// $package. http://www.slack.net/~ant/ + +#include "Sap_Core.h" + +#include "blargg_endian.h" + +//#define CPU_LOG_MAX 100000 +//#include "nes_cpu_log.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// functions defined in same file as CPU emulator to help compiler's optimizer + +int Sap_Core::read_d40b() +{ + //dprintf( "D40B read\n" ); + check( cpu.time() >= frame_start ); + return ((unsigned) (cpu.time() - frame_start) / scanline_period % lines_per_frame) / 2; +} + +void Sap_Core::write_D2xx( int d2xx, int data ) +{ + addr_t const base = 0xD200; + + if ( d2xx < apu_.io_size ) + { + apu_.write_data( time(), d2xx + base, data ); + return; + } + + if ( (unsigned) (d2xx - 0x10) < apu2_.io_size && info.stereo ) + { + apu2_.write_data( time(), d2xx + (base - 0x10), data ); + return; + } + + if ( d2xx == 0xD40A - base ) + { + dprintf( "D40A write\n" ); + time_t t = cpu.time(); + time_t into_line = (t - frame_start) % scanline_period; + cpu.set_end_time( t - into_line + scanline_period ); + return; + } + + if ( (d2xx & ~0x0010) != 0x0F || data != 0x03 ) + dprintf( "Unmapped write $%04X <- $%02X\n", d2xx + base, data ); +} + +inline int Sap_Core::read_mem( addr_t addr ) +{ + int result = mem.ram [addr]; + if ( addr == 0xD40B ) + result = read_d40b(); + else if ( (addr & 0xF900) == 0xD000 ) + dprintf( "Unmapped read $%04X\n", addr ); + return result; +} + + +#define READ_LOW( addr ) (ram [addr]) +#define WRITE_LOW( addr, data ) (ram [addr] = data) + +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) \ +{\ + ram [addr] = data;\ + int d2xx = addr - 0xD200;\ + if ( (unsigned) d2xx < 0x100 )\ + write_D2xx( d2xx, data );\ +} + +#define CPU cpu +#define FLAT_MEM ram + +#define CPU_BEGIN \ +bool Sap_Core::run_cpu( time_t end )\ +{\ + CPU.set_end_time( end );\ + byte* const ram = this->mem.ram; /* cache */ + + #include "Nes_Cpu_run.h" + + return cpu.time_past_end() < 0; +} diff --git a/Frameworks/GME/gme/Sap_Emu.h b/Frameworks/GME/gme/Sap_Emu.h index aac1acc5f..397c52710 100644 --- a/Frameworks/GME/gme/Sap_Emu.h +++ b/Frameworks/GME/gme/Sap_Emu.h @@ -1,53 +1,53 @@ -// Atari XL/XE SAP music file emulator - -// Game_Music_Emu $vers -#ifndef SAP_EMU_H -#define SAP_EMU_H - -#include "Classic_Emu.h" -#include "Sap_Apu.h" -#include "Sap_Core.h" - -class Sap_Emu : public Classic_Emu { -public: - enum { max_tracks = 32 }; // TODO: no fixed limit - - // SAP file info (see Sap_Core.h for more) - struct info_t : Sap_Core::info_t { - byte const* rom_data; - const char* warning; - int track_count; - int track_times [max_tracks]; - char author [256]; - char name [256]; - char copyright [ 32]; - }; - - // Info for currently loaded file - info_t const& info() const { return info_; } - - blargg_err_t hash_( Hash_Function& ) const; - - static gme_type_t static_type() { return gme_sap_type; } - -// Implementation -public: - Sap_Emu(); - ~Sap_Emu(); - -protected: - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t load_mem_( byte const [], int ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - -private: - info_t info_; - byte const* file_end; - Sap_Core core; -}; - -#endif +// Atari XL/XE SAP music file emulator + +// Game_Music_Emu $vers +#ifndef SAP_EMU_H +#define SAP_EMU_H + +#include "Classic_Emu.h" +#include "Sap_Apu.h" +#include "Sap_Core.h" + +class Sap_Emu : public Classic_Emu { +public: + enum { max_tracks = 32 }; // TODO: no fixed limit + + // SAP file info (see Sap_Core.h for more) + struct info_t : Sap_Core::info_t { + byte const* rom_data; + const char* warning; + int track_count; + int track_times [max_tracks]; + char author [256]; + char name [256]; + char copyright [ 32]; + }; + + // Info for currently loaded file + info_t const& info() const { return info_; } + + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_sap_type; } + +// Implementation +public: + Sap_Emu(); + ~Sap_Emu(); + +protected: + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + +private: + info_t info_; + byte const* file_end; + Sap_Core core; +}; + +#endif diff --git a/Frameworks/GME/gme/SegaPcm_Emu.cpp b/Frameworks/GME/gme/SegaPcm_Emu.cpp deleted file mode 100644 index 8c18898dc..000000000 --- a/Frameworks/GME/gme/SegaPcm_Emu.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "SegaPcm_Emu.h" -#include "segapcm.h" - -SegaPcm_Emu::SegaPcm_Emu() { chip = 0; } - -SegaPcm_Emu::~SegaPcm_Emu() -{ - if ( chip ) device_stop_segapcm( chip ); -} - -int SegaPcm_Emu::set_rate( int intf_type ) -{ - if ( chip ) - { - device_stop_segapcm( chip ); - chip = 0; - } - - chip = device_start_segapcm( intf_type ); - if ( !chip ) - return 1; - - reset(); - return 0; -} - -void SegaPcm_Emu::reset() -{ - device_reset_segapcm( chip ); - segapcm_set_mute_mask( chip, 0 ); -} - -void SegaPcm_Emu::write( int addr, int data ) -{ - sega_pcm_w( chip, addr, data ); -} - -void SegaPcm_Emu::write_rom( int size, int start, int length, void * data ) -{ - sega_pcm_write_rom( chip, size, start, length, (const UINT8 *) data ); -} - -void SegaPcm_Emu::mute_voices( int mask ) -{ - segapcm_set_mute_mask( chip, mask ); -} - -void SegaPcm_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - SEGAPCM_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/SegaPcm_Emu.h b/Frameworks/GME/gme/SegaPcm_Emu.h deleted file mode 100644 index ee2722b42..000000000 --- a/Frameworks/GME/gme/SegaPcm_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// Sega PCM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef SEGAPCM_EMU_H -#define SEGAPCM_EMU_H - -class SegaPcm_Emu { - void* chip; -public: - SegaPcm_Emu(); - ~SegaPcm_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int intf_type ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 16 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Sms_Apu.cpp b/Frameworks/GME/gme/Sms_Apu.cpp index 3a50434cd..dbe07045c 100644 --- a/Frameworks/GME/gme/Sms_Apu.cpp +++ b/Frameworks/GME/gme/Sms_Apu.cpp @@ -1,371 +1,371 @@ -// Sms_Snd_Emu $vers. http://www.slack.net/~ant/ - -#include "Sms_Apu.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const noise_osc = 3; - -void Sms_Apu::volume( double vol ) -{ - vol *= 0.85 / osc_count / 64; - norm_synth.volume( vol ); - fast_synth.volume( vol ); -} - -void Sms_Apu::treble_eq( blip_eq_t const& eq ) -{ - norm_synth.treble_eq( eq ); - fast_synth.treble_eq( eq ); -} - -inline int Sms_Apu::calc_output( int i ) const -{ - int flags = ggstereo >> i; - return (flags >> 3 & 2) | (flags & 1); -} - -void Sms_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) - require( !center || (center && !left && !right) || (center && left && right) ); - require( (unsigned) i < osc_count ); // fails if you pass invalid osc index - - if ( center ) - { - unsigned const divisor = 16384 * 16 * 2; - min_tone_period = ((unsigned) center->clock_rate() + divisor/2) / divisor; - } - - if ( !center || !left || !right ) - { - left = center; - right = center; - } - - Osc& o = oscs [i]; - o.outputs [0] = NULL; - o.outputs [1] = right; - o.outputs [2] = left; - o.outputs [3] = center; - o.output = o.outputs [calc_output( i )]; -} - -void Sms_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - for ( int i = osc_count; --i >= 0; ) - set_output( i, c, l, r ); -} - -static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width ) -{ - unsigned galois = 0; - while ( --width >= 0 ) - { - galois = (galois << 1) | (fibonacci & 1); - fibonacci >>= 1; - } - return galois; -} - -void Sms_Apu::reset( unsigned feedback, int noise_width ) -{ - last_time = 0; - latch = 0; - ggstereo = 0; - - // Calculate noise feedback values - if ( !feedback || !noise_width ) - { - feedback = 0x0009; - noise_width = 16; - } - looped_feedback = 1 << (noise_width - 1); - noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width ); - - // Reset oscs - for ( int i = osc_count; --i >= 0; ) - { - Osc& o = oscs [i]; - o.output = NULL; - o.last_amp = 0; - o.delay = 0; - o.phase = 0; - o.period = 0; - o.volume = 15; // silent - } - - oscs [noise_osc].phase = 0x8000; - write_ggstereo( 0, 0xFF ); -} - -Sms_Apu::Sms_Apu() -{ - min_tone_period = 7; - - // Clear outputs to NULL FIRST - ggstereo = 0; - set_output( NULL ); - - volume( 1.0 ); - reset(); -} - -void Sms_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_time ); - if ( end_time <= last_time ) - return; - - // Synthesize each oscillator - for ( int idx = osc_count; --idx >= 0; ) - { - Osc& osc = oscs [idx]; - int vol = 0; - int amp = 0; - - // Determine what will be generated - Blip_Buffer* const out = osc.output; - if ( out ) - { - // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) - static unsigned char const volumes [16] = { - 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0 - }; - - vol = volumes [osc.volume]; - amp = (osc.phase & 1) * vol; - - // Square freq above 16 kHz yields constant amplitude at half volume - if ( idx != noise_osc && osc.period < min_tone_period ) - { - amp = vol >> 1; - vol = 0; - } - - // Update amplitude - int delta = amp - osc.last_amp; - if ( delta ) - { - osc.last_amp = amp; - norm_synth.offset( last_time, delta, out ); - out->set_modified(); - } - } - - // Generate wave - blip_time_t time = last_time + osc.delay; - if ( time < end_time ) - { - // Calculate actual period - int period = osc.period; - if ( idx == noise_osc ) - { - period = 0x20 << (period & 3); - if ( period == 0x100 ) - period = oscs [2].period * 2; - } - period *= 0x10; - if ( !period ) - period = 0x10; - - // Maintain phase when silent - int phase = osc.phase; - if ( !vol ) - { - int count = (end_time - time + period - 1) / period; - time += count * period; - if ( idx != noise_osc ) // TODO: maintain noise LFSR phase? - phase ^= count & 1; - } - else - { - int delta = amp * 2 - vol; - - if ( idx != noise_osc ) - { - // Square - do - { - delta = -delta; - norm_synth.offset( time, delta, out ); - time += period; - } - while ( time < end_time ); - phase = (delta >= 0); - } - else - { - // Noise - unsigned const feedback = (osc.period & 4 ? noise_feedback : looped_feedback); - do - { - unsigned changed = phase + 1; - phase = ((phase & 1) * feedback) ^ (phase >> 1); - if ( changed & 2 ) // true if bits 0 and 1 differ - { - delta = -delta; - fast_synth.offset_inline( time, delta, out ); - } - time += period; - } - while ( time < end_time ); - check( phase ); - } - osc.last_amp = (phase & 1) * vol; - out->set_modified(); - } - osc.phase = phase; - } - osc.delay = time - end_time; - } - last_time = end_time; -} - -void Sms_Apu::write_ggstereo( blip_time_t time, int data ) -{ - require( (unsigned) data <= 0xFF ); - - run_until( time ); - ggstereo = data; - - for ( int i = osc_count; --i >= 0; ) - { - Osc& osc = oscs [i]; - - Blip_Buffer* old = osc.output; - osc.output = osc.outputs [calc_output( i )]; - if ( osc.output != old ) - { - int delta = -osc.last_amp; - if ( delta ) - { - osc.last_amp = 0; - if ( old ) - { - old->set_modified(); - fast_synth.offset( last_time, delta, old ); - } - } - } - } -} - -void Sms_Apu::write_data( blip_time_t time, int data ) -{ - require( (unsigned) data <= 0xFF ); - - run_until( time ); - - if ( data & 0x80 ) - latch = data; - - // We want the raw values written so our save state format can be - // as close to hardware as possible and unspecific to any emulator. - int idx = latch >> 5 & 3; - Osc& osc = oscs [idx]; - if ( latch & 0x10 ) - { - osc.volume = data & 0x0F; - } - else - { - if ( idx == noise_osc ) - osc.phase = 0x8000; // reset noise LFSR - - // Replace high 6 bits/low 4 bits of register with data - int lo = osc.period; - int hi = data << 4; - if ( idx == noise_osc || (data & 0x80) ) - { - hi = lo; - lo = data; - } - osc.period = (hi & 0x3F0) | (lo & 0x00F); - } -} - -void Sms_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - last_time -= end_time; - assert( last_time >= 0 ); -} - -#if SMS_APU_CUSTOM_STATE - #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) ) -#else - #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y ))) - - static unsigned get_val( byte const p [] ) - { - return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0]; - } - - static void set_val( byte p [], unsigned n ) - { - p [0] = (byte) (n ); - p [1] = (byte) (n >> 8); - p [2] = (byte) (n >> 16); - p [3] = (byte) (n >> 24); - } -#endif - -inline const char* Sms_Apu::save_load( sms_apu_state_t* io, bool save ) -{ - #if !SMS_APU_CUSTOM_STATE - assert( sizeof (sms_apu_state_t) == 128 ); - #endif - - // Format of data, where later format is incompatible with earlier - int format = io->format0; - REFLECT( format, format ); - if ( format != io->format0 ) - return "Unsupported sound save state format"; - - // Version of data, where later versions just add fields to the end - int version = 0; - REFLECT( version, version ); - - REFLECT( latch, latch ); - REFLECT( ggstereo, ggstereo ); - - for ( int i = osc_count; --i >= 0; ) - { - Osc& osc = oscs [i]; - REFLECT( osc.period, periods [i] ); - REFLECT( osc.volume, volumes [i] ); - REFLECT( osc.delay, delays [i] ); - REFLECT( osc.phase, phases [i] ); - } - - return 0; -} - -void Sms_Apu::save_state( sms_apu_state_t* out ) -{ - save_load( out, true ); - #if !SMS_APU_CUSTOM_STATE - memset( out->unused, 0, sizeof out->unused ); - #endif -} - -blargg_err_t Sms_Apu::load_state( sms_apu_state_t const& in ) -{ - RETURN_ERR( save_load( CONST_CAST(sms_apu_state_t*,&in), false ) ); - write_ggstereo( 0, ggstereo ); - return blargg_ok; -} +// Sms_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Sms_Apu.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const noise_osc = 3; + +void Sms_Apu::volume( double vol ) +{ + vol *= 0.85 / osc_count / 64; + norm_synth.volume( vol ); + fast_synth.volume( vol ); +} + +void Sms_Apu::treble_eq( blip_eq_t const& eq ) +{ + norm_synth.treble_eq( eq ); + fast_synth.treble_eq( eq ); +} + +inline int Sms_Apu::calc_output( int i ) const +{ + int flags = ggstereo >> i; + return (flags >> 3 & 2) | (flags & 1); +} + +void Sms_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( center ) + { + unsigned const divisor = 16384 * 16 * 2; + min_tone_period = ((unsigned) center->clock_rate() + divisor/2) / divisor; + } + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Osc& o = oscs [i]; + o.outputs [0] = NULL; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; +} + +void Sms_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} + +static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width ) +{ + unsigned galois = 0; + while ( --width >= 0 ) + { + galois = (galois << 1) | (fibonacci & 1); + fibonacci >>= 1; + } + return galois; +} + +void Sms_Apu::reset( unsigned feedback, int noise_width ) +{ + last_time = 0; + latch = 0; + ggstereo = 0; + + // Calculate noise feedback values + if ( !feedback || !noise_width ) + { + feedback = 0x0009; + noise_width = 16; + } + looped_feedback = 1 << (noise_width - 1); + noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width ); + + // Reset oscs + for ( int i = osc_count; --i >= 0; ) + { + Osc& o = oscs [i]; + o.output = NULL; + o.last_amp = 0; + o.delay = 0; + o.phase = 0; + o.period = 0; + o.volume = 15; // silent + } + + oscs [noise_osc].phase = 0x8000; + write_ggstereo( 0, 0xFF ); +} + +Sms_Apu::Sms_Apu() +{ + min_tone_period = 7; + + // Clear outputs to NULL FIRST + ggstereo = 0; + set_output( NULL ); + + volume( 1.0 ); + reset(); +} + +void Sms_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); + if ( end_time <= last_time ) + return; + + // Synthesize each oscillator + for ( int idx = osc_count; --idx >= 0; ) + { + Osc& osc = oscs [idx]; + int vol = 0; + int amp = 0; + + // Determine what will be generated + Blip_Buffer* const out = osc.output; + if ( out ) + { + // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) + static unsigned char const volumes [16] = { + 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0 + }; + + vol = volumes [osc.volume]; + amp = (osc.phase & 1) * vol; + + // Square freq above 16 kHz yields constant amplitude at half volume + if ( idx != noise_osc && osc.period < min_tone_period ) + { + amp = vol >> 1; + vol = 0; + } + + // Update amplitude + int delta = amp - osc.last_amp; + if ( delta ) + { + osc.last_amp = amp; + norm_synth.offset( last_time, delta, out ); + out->set_modified(); + } + } + + // Generate wave + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + // Calculate actual period + int period = osc.period; + if ( idx == noise_osc ) + { + period = 0x20 << (period & 3); + if ( period == 0x100 ) + period = oscs [2].period * 2; + } + period *= 0x10; + if ( !period ) + period = 0x10; + + // Maintain phase when silent + int phase = osc.phase; + if ( !vol ) + { + int count = (end_time - time + period - 1) / period; + time += count * period; + if ( idx != noise_osc ) // TODO: maintain noise LFSR phase? + phase ^= count & 1; + } + else + { + int delta = amp * 2 - vol; + + if ( idx != noise_osc ) + { + // Square + do + { + delta = -delta; + norm_synth.offset( time, delta, out ); + time += period; + } + while ( time < end_time ); + phase = (delta >= 0); + } + else + { + // Noise + unsigned const feedback = (osc.period & 4 ? noise_feedback : looped_feedback); + do + { + unsigned changed = phase + 1; + phase = ((phase & 1) * feedback) ^ (phase >> 1); + if ( changed & 2 ) // true if bits 0 and 1 differ + { + delta = -delta; + fast_synth.offset_inline( time, delta, out ); + } + time += period; + } + while ( time < end_time ); + check( phase ); + } + osc.last_amp = (phase & 1) * vol; + out->set_modified(); + } + osc.phase = phase; + } + osc.delay = time - end_time; + } + last_time = end_time; +} + +void Sms_Apu::write_ggstereo( blip_time_t time, int data ) +{ + require( (unsigned) data <= 0xFF ); + + run_until( time ); + ggstereo = data; + + for ( int i = osc_count; --i >= 0; ) + { + Osc& osc = oscs [i]; + + Blip_Buffer* old = osc.output; + osc.output = osc.outputs [calc_output( i )]; + if ( osc.output != old ) + { + int delta = -osc.last_amp; + if ( delta ) + { + osc.last_amp = 0; + if ( old ) + { + old->set_modified(); + fast_synth.offset( last_time, delta, old ); + } + } + } + } +} + +void Sms_Apu::write_data( blip_time_t time, int data ) +{ + require( (unsigned) data <= 0xFF ); + + run_until( time ); + + if ( data & 0x80 ) + latch = data; + + // We want the raw values written so our save state format can be + // as close to hardware as possible and unspecific to any emulator. + int idx = latch >> 5 & 3; + Osc& osc = oscs [idx]; + if ( latch & 0x10 ) + { + osc.volume = data & 0x0F; + } + else + { + if ( idx == noise_osc ) + osc.phase = 0x8000; // reset noise LFSR + + // Replace high 6 bits/low 4 bits of register with data + int lo = osc.period; + int hi = data << 4; + if ( idx == noise_osc || (data & 0x80) ) + { + hi = lo; + lo = data; + } + osc.period = (hi & 0x3F0) | (lo & 0x00F); + } +} + +void Sms_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + last_time -= end_time; + assert( last_time >= 0 ); +} + +#if SMS_APU_CUSTOM_STATE + #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) ) +#else + #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y ))) + + static unsigned get_val( byte const p [] ) + { + return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0]; + } + + static void set_val( byte p [], unsigned n ) + { + p [0] = (byte) (n ); + p [1] = (byte) (n >> 8); + p [2] = (byte) (n >> 16); + p [3] = (byte) (n >> 24); + } +#endif + +inline const char* Sms_Apu::save_load( sms_apu_state_t* io, bool save ) +{ + #if !SMS_APU_CUSTOM_STATE + assert( sizeof (sms_apu_state_t) == 128 ); + #endif + + // Format of data, where later format is incompatible with earlier + int format = io->format0; + REFLECT( format, format ); + if ( format != io->format0 ) + return "Unsupported sound save state format"; + + // Version of data, where later versions just add fields to the end + int version = 0; + REFLECT( version, version ); + + REFLECT( latch, latch ); + REFLECT( ggstereo, ggstereo ); + + for ( int i = osc_count; --i >= 0; ) + { + Osc& osc = oscs [i]; + REFLECT( osc.period, periods [i] ); + REFLECT( osc.volume, volumes [i] ); + REFLECT( osc.delay, delays [i] ); + REFLECT( osc.phase, phases [i] ); + } + + return 0; +} + +void Sms_Apu::save_state( sms_apu_state_t* out ) +{ + save_load( out, true ); + #if !SMS_APU_CUSTOM_STATE + memset( out->unused, 0, sizeof out->unused ); + #endif +} + +blargg_err_t Sms_Apu::load_state( sms_apu_state_t const& in ) +{ + RETURN_ERR( save_load( CONST_CAST(sms_apu_state_t*,&in), false ) ); + write_ggstereo( 0, ggstereo ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Sms_Apu.h b/Frameworks/GME/gme/Sms_Apu.h index b93e47bc8..f0dbda743 100644 --- a/Frameworks/GME/gme/Sms_Apu.h +++ b/Frameworks/GME/gme/Sms_Apu.h @@ -1,128 +1,128 @@ -// Sega Master System SN76489 PSG sound chip emulator - -// Sms_Snd_Emu $vers -#ifndef SMS_APU_H -#define SMS_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct sms_apu_state_t; - -class Sms_Apu { -public: -// Basics - - // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, - // output is mono. - void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Emulates to time t, then writes data to Game Gear left/right assignment byte - void write_ggstereo( blip_time_t t, int data ); - - // Emulates to time t, then writes data - void write_data( blip_time_t t, int data ); - - // Emulates to time t, then subtracts t from the current time. - // OK if previous write call had time slightly after t. - void end_frame( blip_time_t t ); - -// More features - - // Resets sound chip and sets noise feedback bits and width - void reset( unsigned noise_feedback = 0, int noise_width = 0 ); - - // Same as set_output(), but for a particular channel - // 0: Square 1, 1: Square 2, 2: Square 3, 3: Noise - enum { osc_count = 4 }; // 0 <= chan < osc_count - void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - - // Sets overall volume, where 1.0 is normal - void volume( double ); - - // Sets treble equalization - void treble_eq( blip_eq_t const& ); - - // Saves full emulation state to state_out. Data format is portable and - // includes some extra space to avoid expansion in case more state needs - // to be stored in the future. - void save_state( sms_apu_state_t* state_out ); - - // Loads state. You should call reset() BEFORE this. - blargg_err_t load_state( sms_apu_state_t const& in ); - -private: - // noncopyable - Sms_Apu( const Sms_Apu& ); - Sms_Apu& operator = ( const Sms_Apu& ); - -// Implementation -public: - Sms_Apu(); - ~Sms_Apu() { } - BLARGG_DISABLE_NOTHROW - - // Use set_output() instead - BLARGG_DEPRECATED( void output ( Blip_Buffer* c ) { set_output( c, c, c ); } ) - BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) - BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) - BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) - -private: - struct Osc - { - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int last_amp; - - int volume; - int period; - int delay; - unsigned phase; - }; - - Osc oscs [osc_count]; - int ggstereo; - int latch; - - blip_time_t last_time; - int min_tone_period; - unsigned noise_feedback; - unsigned looped_feedback; - Blip_Synth_Fast fast_synth; - Blip_Synth_Norm norm_synth; - - int calc_output( int i ) const; - void run_until( blip_time_t ); - const char* save_load( sms_apu_state_t*, bool save ); - friend class Sms_Apu_Tester; -}; - -struct sms_apu_state_t -{ - // If SMS_APU_CUSTOM_STATE is 1, values are stored as normal integers, - // so your code can then save and load them however it likes. Otherwise, - // they are 4-byte arrays in little-endian format, making entire - // structure suitable for direct storage on disk. - -#if SMS_APU_CUSTOM_STATE - typedef int val_t; -#else - typedef unsigned char val_t [4]; -#endif - - enum { format0 = 0x50414D53 }; - - val_t format; - val_t version; - val_t latch; - val_t ggstereo; - val_t periods [4]; - val_t volumes [4]; - val_t delays [4]; - val_t phases [4]; - - val_t unused [12]; // for future expansion -}; - -#endif +// Sega Master System SN76489 PSG sound chip emulator + +// Sms_Snd_Emu $vers +#ifndef SMS_APU_H +#define SMS_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct sms_apu_state_t; + +class Sms_Apu { +public: +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to Game Gear left/right assignment byte + void write_ggstereo( blip_time_t t, int data ); + + // Emulates to time t, then writes data + void write_data( blip_time_t t, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip and sets noise feedback bits and width + void reset( unsigned noise_feedback = 0, int noise_width = 0 ); + + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Square 3, 3: Noise + enum { osc_count = 4 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Sets overall volume, where 1.0 is normal + void volume( double ); + + // Sets treble equalization + void treble_eq( blip_eq_t const& ); + + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( sms_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( sms_apu_state_t const& in ); + +private: + // noncopyable + Sms_Apu( const Sms_Apu& ); + Sms_Apu& operator = ( const Sms_Apu& ); + +// Implementation +public: + Sms_Apu(); + ~Sms_Apu() { } + BLARGG_DISABLE_NOTHROW + + // Use set_output() instead + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ) { set_output( c, c, c ); } ) + BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) + +private: + struct Osc + { + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int last_amp; + + int volume; + int period; + int delay; + unsigned phase; + }; + + Osc oscs [osc_count]; + int ggstereo; + int latch; + + blip_time_t last_time; + int min_tone_period; + unsigned noise_feedback; + unsigned looped_feedback; + Blip_Synth_Fast fast_synth; + Blip_Synth_Norm norm_synth; + + int calc_output( int i ) const; + void run_until( blip_time_t ); + const char* save_load( sms_apu_state_t*, bool save ); + friend class Sms_Apu_Tester; +}; + +struct sms_apu_state_t +{ + // If SMS_APU_CUSTOM_STATE is 1, values are stored as normal integers, + // so your code can then save and load them however it likes. Otherwise, + // they are 4-byte arrays in little-endian format, making entire + // structure suitable for direct storage on disk. + +#if SMS_APU_CUSTOM_STATE + typedef int val_t; +#else + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414D53 }; + + val_t format; + val_t version; + val_t latch; + val_t ggstereo; + val_t periods [4]; + val_t volumes [4]; + val_t delays [4]; + val_t phases [4]; + + val_t unused [12]; // for future expansion +}; + +#endif diff --git a/Frameworks/GME/gme/Spc_Emu.h b/Frameworks/GME/gme/Spc_Emu.h index 33b04f27c..ea815ed92 100644 --- a/Frameworks/GME/gme/Spc_Emu.h +++ b/Frameworks/GME/gme/Spc_Emu.h @@ -1,93 +1,93 @@ -// Super Nintendo SPC music file emulator - -// Game_Music_Emu $vers -#ifndef SPC_EMU_H -#define SPC_EMU_H - -#include "Music_Emu.h" -#include "higan/smp/smp.hpp" -#include "Spc_Filter.h" - -#if GME_SPC_FAST_RESAMPLER - #include "Upsampler.h" - typedef Upsampler Spc_Emu_Resampler; -#else - #include "Fir_Resampler.h" - typedef Fir_Resampler<24> Spc_Emu_Resampler; -#endif - -class Spc_Emu : public Music_Emu { -public: - // The Super Nintendo hardware samples at 32kHz. Other sample rates are - // handled by resampling the 32kHz output; emulation accuracy is not affected. - enum { native_sample_rate = 32000 }; - - // Disables annoying pseudo-surround effect some music uses - void disable_surround( bool disable = true ) { smp.dsp.disable_surround( disable ); } - - // Enables gaussian, cubic or sinc interpolation - void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); } - - SuperFamicom::SMP const* get_smp() const; - SuperFamicom::SMP * get_smp(); - - // SPC file header - struct header_t - { - enum { size = 0x100 }; - - char tag [35]; - byte format; - byte version; - byte pc [ 2]; - byte a, x, y, psw, sp; - byte unused [ 2]; - char song [32]; - char game [32]; - char dumper [16]; - char comment [32]; - byte date [11]; - byte len_secs [ 3]; - byte fade_msec [ 4]; - char author [32]; // sometimes first char should be skipped (see official SPC spec) - byte mute_mask; - byte emulator; - byte unused2 [46]; - }; - - // Header for currently loaded file - header_t const& header() const { return *(header_t const*) file_begin(); } - - blargg_err_t hash_( Hash_Function& ) const; - - static gme_type_t static_type() { return gme_spc_type; } - -// Implementation -public: - Spc_Emu(); - ~Spc_Emu(); - -protected: - virtual blargg_err_t load_mem_( byte const [], int ); - virtual blargg_err_t track_info_( track_info_t*, int track ) const; - virtual blargg_err_t set_sample_rate_( int ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t play_( int, sample_t [] ); - virtual blargg_err_t skip_( int ); - virtual void mute_voices_( int ); - virtual void set_tempo_( double ); - -private: - Spc_Emu_Resampler resampler; - Spc_Filter filter; - SuperFamicom::SMP smp; - - byte const* trailer_() const; - int trailer_size_() const; - blargg_err_t play_and_filter( int count, sample_t out [] ); -}; - -inline SuperFamicom::SMP const* Spc_Emu::get_smp() const { return &smp; } -inline SuperFamicom::SMP * Spc_Emu::get_smp() { return &smp; } - -#endif +// Super Nintendo SPC music file emulator + +// Game_Music_Emu $vers +#ifndef SPC_EMU_H +#define SPC_EMU_H + +#include "Music_Emu.h" +#include "higan/smp/smp.hpp" +#include "Spc_Filter.h" + +#if GME_SPC_FAST_RESAMPLER + #include "Upsampler.h" + typedef Upsampler Spc_Emu_Resampler; +#else + #include "Fir_Resampler.h" + typedef Fir_Resampler<24> Spc_Emu_Resampler; +#endif + +class Spc_Emu : public Music_Emu { +public: + // The Super Nintendo hardware samples at 32kHz. Other sample rates are + // handled by resampling the 32kHz output; emulation accuracy is not affected. + enum { native_sample_rate = 32000 }; + + // Disables annoying pseudo-surround effect some music uses + void disable_surround( bool disable = true ) { smp.dsp.disable_surround( disable ); } + + // Enables gaussian, cubic or sinc interpolation + void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); } + + SuperFamicom::SMP const* get_smp() const; + SuperFamicom::SMP * get_smp(); + + // SPC file header + struct header_t + { + enum { size = 0x100 }; + + char tag [35]; + byte format; + byte version; + byte pc [ 2]; + byte a, x, y, psw, sp; + byte unused [ 2]; + char song [32]; + char game [32]; + char dumper [16]; + char comment [32]; + byte date [11]; + byte len_secs [ 3]; + byte fade_msec [ 4]; + char author [32]; // sometimes first char should be skipped (see official SPC spec) + byte mute_mask; + byte emulator; + byte unused2 [46]; + }; + + // Header for currently loaded file + header_t const& header() const { return *(header_t const*) file_begin(); } + + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_spc_type; } + +// Implementation +public: + Spc_Emu(); + ~Spc_Emu(); + +protected: + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t set_sample_rate_( int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int, sample_t [] ); + virtual blargg_err_t skip_( int ); + virtual void mute_voices_( int ); + virtual void set_tempo_( double ); + +private: + Spc_Emu_Resampler resampler; + Spc_Filter filter; + SuperFamicom::SMP smp; + + byte const* trailer_() const; + int trailer_size_() const; + blargg_err_t play_and_filter( int count, sample_t out [] ); +}; + +inline SuperFamicom::SMP const* Spc_Emu::get_smp() const { return &smp; } +inline SuperFamicom::SMP * Spc_Emu::get_smp() { return &smp; } + +#endif diff --git a/Frameworks/GME/gme/Spc_Filter.cpp b/Frameworks/GME/gme/Spc_Filter.cpp index 87b38fae6..5fda0bd16 100644 --- a/Frameworks/GME/gme/Spc_Filter.cpp +++ b/Frameworks/GME/gme/Spc_Filter.cpp @@ -1,107 +1,107 @@ -// snes_spc $vers. http://www.slack.net/~ant/ - -#include "Spc_Filter.h" - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -static const float limiter_max = (float)0.9999; -static const float limiter_max_05 = (float)(limiter_max - 0.5); - -#include - -static short hard_limit_sample( int sample ) -{ - double val = (double)sample * (1.0 / 32768.0); - if (val < -0.5) - val = (tanh((val + 0.5) / limiter_max_05) * limiter_max_05 - 0.5); - else if (val > 0.5) - val = (tanh((val - 0.5) / limiter_max_05) * limiter_max_05 + 0.5); - return val * 32768.0; -} - -void Spc_Filter::build_limit_table() -{ - for (int i = -65536; i < 65536; ++i) - { - hard_limit_table[ i + 65536 ] = hard_limit_sample( i ); - } -} - -inline short Spc_Filter::limit_sample(int sample) -{ - if (!limiting && ((unsigned)sample + 32768) < 65536) return sample; - limiting = true; - if (((unsigned)sample + 65536) < 131072) return hard_limit_table[ sample + 65536 ]; - return hard_limit_sample( sample ); -} - -void Spc_Filter::clear() { limiting = false; memset( ch, 0, sizeof ch ); } - -Spc_Filter::Spc_Filter() -{ - enabled = true; - gain = gain_unit; - bass = bass_norm; - clear(); - build_limit_table(); -} - -void Spc_Filter::run( short io [], int count ) -{ - require( (count & 1) == 0 ); // must be even - - int const gain = this->gain; - if ( enabled ) - { - int const bass = this->bass; - chan_t* c = &ch [2]; - do - { - // cache in registers - int sum = (--c)->sum; - int pp1 = c->pp1; - int p1 = c->p1; - - for ( int i = 0; i < count; i += 2 ) - { - // Low-pass filter (two point FIR with coeffs 0.25, 0.75) - int f = io [i] + p1; - p1 = io [i] * 3; - - // High-pass filter ("leaky integrator") - int delta = f - pp1; - pp1 = f; - int s = sum >> (gain_bits + 2); - sum += (delta * gain) - (sum >> bass); - - io [i] = limit_sample( s ); - } - - c->p1 = p1; - c->pp1 = pp1; - c->sum = sum; - ++io; - } - while ( c != ch ); - } - else if ( gain != gain_unit ) - { - short* const end = io + count; - while ( io < end ) - { - int s = (*io * gain) >> gain_bits; - *io++ = limit_sample( s ); - } - } -} +// snes_spc $vers. http://www.slack.net/~ant/ + +#include "Spc_Filter.h" + +/* Copyright (C) 2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static const float limiter_max = (float)0.9999; +static const float limiter_max_05 = (float)(limiter_max - 0.5); + +#include + +static short hard_limit_sample( int sample ) +{ + double val = (double)sample * (1.0 / 32768.0); + if (val < -0.5) + val = (tanh((val + 0.5) / limiter_max_05) * limiter_max_05 - 0.5); + else if (val > 0.5) + val = (tanh((val - 0.5) / limiter_max_05) * limiter_max_05 + 0.5); + return val * 32768.0; +} + +void Spc_Filter::build_limit_table() +{ + for (int i = -65536; i < 65536; ++i) + { + hard_limit_table[ i + 65536 ] = hard_limit_sample( i ); + } +} + +inline short Spc_Filter::limit_sample(int sample) +{ + if (!limiting && ((unsigned)sample + 32768) < 65536) return sample; + limiting = true; + if (((unsigned)sample + 65536) < 131072) return hard_limit_table[ sample + 65536 ]; + return hard_limit_sample( sample ); +} + +void Spc_Filter::clear() { limiting = false; memset( ch, 0, sizeof ch ); } + +Spc_Filter::Spc_Filter() +{ + enabled = true; + gain = gain_unit; + bass = bass_norm; + clear(); + build_limit_table(); +} + +void Spc_Filter::run( short io [], int count ) +{ + require( (count & 1) == 0 ); // must be even + + int const gain = this->gain; + if ( enabled ) + { + int const bass = this->bass; + chan_t* c = &ch [2]; + do + { + // cache in registers + int sum = (--c)->sum; + int pp1 = c->pp1; + int p1 = c->p1; + + for ( int i = 0; i < count; i += 2 ) + { + // Low-pass filter (two point FIR with coeffs 0.25, 0.75) + int f = io [i] + p1; + p1 = io [i] * 3; + + // High-pass filter ("leaky integrator") + int delta = f - pp1; + pp1 = f; + int s = sum >> (gain_bits + 2); + sum += (delta * gain) - (sum >> bass); + + io [i] = limit_sample( s ); + } + + c->p1 = p1; + c->pp1 = pp1; + c->sum = sum; + ++io; + } + while ( c != ch ); + } + else if ( gain != gain_unit ) + { + short* const end = io + count; + while ( io < end ) + { + int s = (*io * gain) >> gain_bits; + *io++ = limit_sample( s ); + } + } +} diff --git a/Frameworks/GME/gme/Spc_Filter.h b/Frameworks/GME/gme/Spc_Filter.h index 26653de10..032fbea02 100644 --- a/Frameworks/GME/gme/Spc_Filter.h +++ b/Frameworks/GME/gme/Spc_Filter.h @@ -1,57 +1,57 @@ -// Simple low-pass and high-pass filter to better match sound output of a SNES - -// snes_spc $vers -#ifndef SPC_FILTER_H -#define SPC_FILTER_H - -#include "blargg_common.h" - -struct Spc_Filter { -public: - - // Filters count samples of stereo sound in place. Count must be a multiple of 2. - typedef short sample_t; - void run( sample_t io [], int count ); - -// Optional features - - // Clears filter to silence - void clear(); - - // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit - // are fine, since output is clamped to 16-bit sample range. - enum { gain_unit = 0x100 }; - void set_gain( int gain ); - - // Enables/disables filtering (when disabled, gain is still applied) - void enable( bool b ); - - // Sets amount of bass (logarithmic scale) - enum { bass_none = 0 }; - enum { bass_norm = 8 }; // normal amount - enum { bass_max = 31 }; - void set_bass( int bass ); - -public: - Spc_Filter(); - BLARGG_DISABLE_NOTHROW -private: - enum { gain_bits = 8 }; - int gain; - int bass; - bool enabled; - bool limiting; - struct chan_t { int p1, pp1, sum; }; - chan_t ch [2]; - short hard_limit_table[131072]; - void build_limit_table(); - inline short limit_sample(int sample); -}; - -inline void Spc_Filter::enable( bool b ) { enabled = b; } - -inline void Spc_Filter::set_gain( int g ) { gain = g; } - -inline void Spc_Filter::set_bass( int b ) { bass = b; } - -#endif +// Simple low-pass and high-pass filter to better match sound output of a SNES + +// snes_spc $vers +#ifndef SPC_FILTER_H +#define SPC_FILTER_H + +#include "blargg_common.h" + +struct Spc_Filter { +public: + + // Filters count samples of stereo sound in place. Count must be a multiple of 2. + typedef short sample_t; + void run( sample_t io [], int count ); + +// Optional features + + // Clears filter to silence + void clear(); + + // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit + // are fine, since output is clamped to 16-bit sample range. + enum { gain_unit = 0x100 }; + void set_gain( int gain ); + + // Enables/disables filtering (when disabled, gain is still applied) + void enable( bool b ); + + // Sets amount of bass (logarithmic scale) + enum { bass_none = 0 }; + enum { bass_norm = 8 }; // normal amount + enum { bass_max = 31 }; + void set_bass( int bass ); + +public: + Spc_Filter(); + BLARGG_DISABLE_NOTHROW +private: + enum { gain_bits = 8 }; + int gain; + int bass; + bool enabled; + bool limiting; + struct chan_t { int p1, pp1, sum; }; + chan_t ch [2]; + short hard_limit_table[131072]; + void build_limit_table(); + inline short limit_sample(int sample); +}; + +inline void Spc_Filter::enable( bool b ) { enabled = b; } + +inline void Spc_Filter::set_gain( int g ) { gain = g; } + +inline void Spc_Filter::set_bass( int b ) { bass = b; } + +#endif diff --git a/Frameworks/GME/gme/Upsampler.cpp b/Frameworks/GME/gme/Upsampler.cpp index 5331176d0..9823de663 100644 --- a/Frameworks/GME/gme/Upsampler.cpp +++ b/Frameworks/GME/gme/Upsampler.cpp @@ -1,73 +1,73 @@ -// $package. http://www.slack.net/~ant/ - -#include "Upsampler.h" - -/* Copyright (C) 2004-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const shift = 15; -int const unit = 1 << shift; - -void Upsampler::clear_() -{ - pos = 0; - Resampler::clear_(); -} - -Upsampler::Upsampler() -{ - clear(); -} - -blargg_err_t Upsampler::set_rate_( double new_factor ) -{ - step = (int) (new_factor * unit + 0.5); - return Resampler::set_rate_( 1.0 / unit * step ); -} - -Resampler::sample_t const* Upsampler::resample_( sample_t** out_, sample_t const* out_end, - sample_t const in [], int in_size ) -{ - in_size -= write_offset; - if ( in_size > 0 ) - { - sample_t* BLARGG_RESTRICT out = *out_; - sample_t const* const in_end = in + in_size; - - int const step = this->step; - int pos = this->pos; - - do - { - #define INTERP( i, out )\ - {\ - int t = in [0 + i] * (unit - pos) + in [stereo + i] * pos;\ - out = t >> shift;\ - } - - int out_0; - INTERP( 0, out_0 ) - INTERP( 1, out [0] = out_0; out [1] ) - out += stereo; - - pos += step; - in += ((unsigned) pos >> shift) * stereo; - pos &= unit - 1; - } - while ( in < in_end && out < out_end ); - - this->pos = pos; - *out_ = out; - } - return in; -} +// $package. http://www.slack.net/~ant/ + +#include "Upsampler.h" + +/* Copyright (C) 2004-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +int const shift = 15; +int const unit = 1 << shift; + +void Upsampler::clear_() +{ + pos = 0; + Resampler::clear_(); +} + +Upsampler::Upsampler() +{ + clear(); +} + +blargg_err_t Upsampler::set_rate_( double new_factor ) +{ + step = (int) (new_factor * unit + 0.5); + return Resampler::set_rate_( 1.0 / unit * step ); +} + +Resampler::sample_t const* Upsampler::resample_( sample_t** out_, sample_t const* out_end, + sample_t const in [], int in_size ) +{ + in_size -= write_offset; + if ( in_size > 0 ) + { + sample_t* BLARGG_RESTRICT out = *out_; + sample_t const* const in_end = in + in_size; + + int const step = this->step; + int pos = this->pos; + + do + { + #define INTERP( i, out )\ + {\ + int t = in [0 + i] * (unit - pos) + in [stereo + i] * pos;\ + out = t >> shift;\ + } + + int out_0; + INTERP( 0, out_0 ) + INTERP( 1, out [0] = out_0; out [1] ) + out += stereo; + + pos += step; + in += ((unsigned) pos >> shift) * stereo; + pos &= unit - 1; + } + while ( in < in_end && out < out_end ); + + this->pos = pos; + *out_ = out; + } + return in; +} diff --git a/Frameworks/GME/gme/Upsampler.h b/Frameworks/GME/gme/Upsampler.h index 7159d67d1..052e76422 100644 --- a/Frameworks/GME/gme/Upsampler.h +++ b/Frameworks/GME/gme/Upsampler.h @@ -1,25 +1,25 @@ -// Increases sampling rate using linear interpolation - -// $package -#ifndef UPSAMPLER_H -#define UPSAMPLER_H - -#include "Resampler.h" - -class Upsampler : public Resampler { -public: - Upsampler(); - -protected: - virtual blargg_err_t set_rate_( double ); - virtual void clear_(); - virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); - -protected: - enum { stereo = 2 }; - enum { write_offset = 2 * stereo }; - int pos; - int step; -}; - -#endif +// Increases sampling rate using linear interpolation + +// $package +#ifndef UPSAMPLER_H +#define UPSAMPLER_H + +#include "Resampler.h" + +class Upsampler : public Resampler { +public: + Upsampler(); + +protected: + virtual blargg_err_t set_rate_( double ); + virtual void clear_(); + virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); + +protected: + enum { stereo = 2 }; + enum { write_offset = 2 * stereo }; + int pos; + int step; +}; + +#endif diff --git a/Frameworks/GME/gme/Vgm_Core.cpp b/Frameworks/GME/gme/Vgm_Core.cpp index bf6893ffc..4ed8c7d9c 100644 --- a/Frameworks/GME/gme/Vgm_Core.cpp +++ b/Frameworks/GME/gme/Vgm_Core.cpp @@ -1,2304 +1,187 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Vgm_Core.h" - -#include "dac_control.h" - -#include "blargg_endian.h" -#include - -// Needed for OKIM6295 system detection -#include "Vgm_Emu.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const stereo = 2; -int const fm_time_bits = 12; -int const blip_time_bits = 12; - -enum { - cmd_gg_stereo = 0x4F, - cmd_gg_stereo_2 = 0x3F, - cmd_psg = 0x50, - cmd_psg_2 = 0x30, - cmd_ym2413 = 0x51, - cmd_ym2413_2 = 0xA1, - cmd_ym2612_port0 = 0x52, - cmd_ym2612_2_port0 = 0xA2, - cmd_ym2612_port1 = 0x53, - cmd_ym2612_2_port1 = 0xA3, - cmd_ym2151 = 0x54, - cmd_ym2151_2 = 0xA4, - cmd_ym2203 = 0x55, - cmd_ym2203_2 = 0xA5, - cmd_ym2608_port0 = 0x56, - cmd_ym2608_2_port0 = 0xA6, - cmd_ym2608_port1 = 0x57, - cmd_ym2608_2_port1 = 0xA7, - cmd_ym2610_port0 = 0x58, - cmd_ym2610_2_port0 = 0xA8, - cmd_ym2610_port1 = 0x59, - cmd_ym2610_2_port1 = 0xA9, - cmd_ym3812 = 0x5A, - cmd_ym3812_2 = 0xAA, - cmd_ymz280b = 0x5D, - cmd_ymf262_port0 = 0x5E, - cmd_ymf262_2_port0 = 0xAE, - cmd_ymf262_port1 = 0x5F, - cmd_ymf262_2_port1 = 0xAF, - cmd_delay = 0x61, - cmd_delay_735 = 0x62, - cmd_delay_882 = 0x63, - cmd_byte_delay = 0x64, - cmd_end = 0x66, - cmd_data_block = 0x67, - cmd_ram_block = 0x68, - cmd_short_delay = 0x70, - cmd_pcm_delay = 0x80, - cmd_dacctl_setup = 0x90, - cmd_dacctl_data = 0x91, - cmd_dacctl_freq = 0x92, - cmd_dacctl_play = 0x93, - cmd_dacctl_stop = 0x94, - cmd_dacctl_playblock= 0x95, - cmd_ay8910 = 0xA0, - cmd_rf5c68 = 0xB0, - cmd_rf5c164 = 0xB1, - cmd_pwm = 0xB2, - cmd_gbdmg_write = 0xB3, - cmd_okim6258_write = 0xB7, - cmd_okim6295_write = 0xB8, - cmd_huc6280_write = 0xB9, - cmd_k053260_write = 0xBA, - cmd_segapcm_write = 0xC0, - cmd_rf5c68_mem = 0xC1, - cmd_rf5c164_mem = 0xC2, - cmd_qsound_write = 0xC4, - cmd_k051649_write = 0xD2, - cmd_k054539_write = 0xD3, - cmd_c140 = 0xD4, - cmd_pcm_seek = 0xE0, - - rf5c68_ram_block = 0x01, - rf5c164_ram_block = 0x02, - - pcm_block_type = 0x00, - pcm_aux_block_type = 0x40, - rom_block_type = 0x80, - ram_block_type = 0xC0, - - rom_segapcm = 0x80, - rom_ym2608_deltat = 0x81, - rom_ym2610_adpcm = 0x82, - rom_ym2610_deltat = 0x83, - rom_ymz280b = 0x86, - rom_okim6295 = 0x8B, - rom_k054539 = 0x8C, - rom_c140 = 0x8D, - rom_k053260 = 0x8E, - rom_qsound = 0x8F, - - ram_rf5c68 = 0xC0, - ram_rf5c164 = 0xC1, - ram_nesapu = 0xC2, - - ym2612_dac_port = 0x2A, - ym2612_dac_pan_port = 0xB6 -}; - -inline int command_len( int command ) -{ - static byte const lens [0x10] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 1,1,1,2,2,3,1,1,1,1,3,3,4,4,5,5 - }; - int len = lens [command >> 4]; - check( len != 1 ); - return len; -} - -int Vgm_Core::run_ym2151( int chip, int time ) -{ - return ym2151[!!chip].run_until( time ); -} - -int Vgm_Core::run_ym2203( int chip, int time ) -{ - return ym2203[!!chip].run_until( time ); -} - -int Vgm_Core::run_ym2413( int chip, int time ) -{ - return ym2413[!!chip].run_until( time ); -} - -int Vgm_Core::run_ym2612( int chip, int time ) -{ - return ym2612[!!chip].run_until( time ); -} - -int Vgm_Core::run_ym2610( int chip, int time ) -{ - return ym2610[!!chip].run_until( time ); -} - -int Vgm_Core::run_ym2608( int chip, int time ) -{ - return ym2608[!!chip].run_until( time ); -} - -int Vgm_Core::run_ym3812( int chip, int time ) -{ - return ym3812[!!chip].run_until( time ); -} - -int Vgm_Core::run_ymf262( int chip, int time ) -{ - return ymf262[!!chip].run_until( time ); -} - -int Vgm_Core::run_ymz280b( int time ) -{ - return ymz280b.run_until( time ); -} - -int Vgm_Core::run_c140( int time ) -{ - return c140.run_until( time ); -} - -int Vgm_Core::run_segapcm( int time ) -{ - return segapcm.run_until( time ); -} - -int Vgm_Core::run_rf5c68( int time ) -{ - return rf5c68.run_until( time ); -} - -int Vgm_Core::run_rf5c164( int time ) -{ - return rf5c164.run_until( time ); -} - -int Vgm_Core::run_pwm( int time ) -{ - return pwm.run_until( time ); -} - -int Vgm_Core::run_okim6258( int chip, int time ) -{ - chip = !!chip; - if ( okim6258[chip].enabled() ) - { - int current_clock = okim6258[chip].get_clock(); - if ( okim6258_hz[chip] != current_clock ) - { - okim6258_hz[chip] = current_clock; - okim6258[chip].setup( (double)okim6258_hz[chip] / vgm_rate, 0.85, 1.0 ); - } - } - return okim6258[chip].run_until( time ); -} - -int Vgm_Core::run_okim6295( int chip, int time ) -{ - return okim6295[!!chip].run_until( time ); -} - -int Vgm_Core::run_k051649( int time ) -{ - return k051649.run_until( time ); -} - -int Vgm_Core::run_k053260( int time ) -{ - return k053260.run_until( time ); -} - -int Vgm_Core::run_k054539( int time ) -{ - return k054539.run_until( time ); -} - -int Vgm_Core::run_qsound( int chip, int time ) -{ - return qsound[!!chip].run_until( time ); -} - -/* Recursive fun starts here! */ -int Vgm_Core::run_dac_control( int time ) -{ - if (dac_control_recursion) return 1; - - ++dac_control_recursion; - for ( unsigned i = 0; i < DacCtrlUsed; i++ ) - { - int time_start = DacCtrlTime[DacCtrlMap[i]]; - if ( time > time_start ) - { - DacCtrlTime[DacCtrlMap[i]] = time; - daccontrol_update( dac_control [i], time_start, time - time_start ); - } - } - --dac_control_recursion; - - return 1; -} - -Vgm_Core::Vgm_Core() -{ - blip_buf[0] = stereo_buf[0].center(); - blip_buf[1] = blip_buf[0]; - has_looped = false; - DacCtrlUsed = 0; - dac_control = NULL; - memset( PCMBank, 0, sizeof( PCMBank ) ); - memset( &PCMTbl, 0, sizeof( PCMTbl ) ); - memset( DacCtrl, 0, sizeof( DacCtrl ) ); - memset( DacCtrlTime, 0, sizeof( DacCtrlTime ) ); -} - -Vgm_Core::~Vgm_Core() -{ - for (unsigned i = 0; i < DacCtrlUsed; i++) device_stop_daccontrol( dac_control [i] ); - if ( dac_control ) free( dac_control ); - for (unsigned i = 0; i < PCM_BANK_COUNT; i++) - { - if ( PCMBank [i].Bank ) free( PCMBank [i].Bank ); - if ( PCMBank [i].Data ) free( PCMBank [i].Data ); - } - if ( PCMTbl.Entries ) free( PCMTbl.Entries ); -} - -typedef unsigned int FUINT8; -typedef unsigned int FUINT16; - -void Vgm_Core::ReadPCMTable(unsigned DataSize, const byte* Data) -{ - byte ValSize; - unsigned TblSize; - - PCMTbl.ComprType = Data[0x00]; - PCMTbl.CmpSubType = Data[0x01]; - PCMTbl.BitDec = Data[0x02]; - PCMTbl.BitCmp = Data[0x03]; - PCMTbl.EntryCount = get_le16( Data + 0x04 ); - - ValSize = (PCMTbl.BitDec + 7) / 8; - TblSize = PCMTbl.EntryCount * ValSize; - - PCMTbl.Entries = realloc(PCMTbl.Entries, TblSize); - memcpy(PCMTbl.Entries, Data + 0x06, TblSize); -} - -bool Vgm_Core::DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data) -{ - UINT8 ComprType; - UINT8 BitDec; - FUINT8 BitCmp; - UINT8 CmpSubType; - UINT16 AddVal; - const UINT8* InPos; - const UINT8* InDataEnd; - UINT8* OutPos; - const UINT8* OutDataEnd; - FUINT16 InVal; - FUINT16 OutVal = 0; - FUINT8 ValSize; - FUINT8 InShift; - FUINT8 OutShift; - UINT8* Ent1B; - UINT16* Ent2B; - - // ReadBits Variables - FUINT8 BitsToRead; - FUINT8 BitReadVal; - FUINT8 InValB; - FUINT8 BitMask; - FUINT8 OutBit; - - // Variables for DPCM - UINT16 OutMask; - - ComprType = Data[0x00]; - Bank->DataSize = get_le32( Data + 0x01 ); - - switch(ComprType) - { - case 0x00: // n-Bit compression - BitDec = Data[0x05]; - BitCmp = Data[0x06]; - CmpSubType = Data[0x07]; - AddVal = get_le16( Data + 0x08 ); - - if (CmpSubType == 0x02) - { - Ent1B = (UINT8*)PCMTbl.Entries; - Ent2B = (UINT16*)PCMTbl.Entries; - if (! PCMTbl.EntryCount) - { - Bank->DataSize = 0x00; - return false; - } - else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp) - { - Bank->DataSize = 0x00; - return false; - } - } - - ValSize = (BitDec + 7) / 8; - InPos = Data + 0x0A; - InDataEnd = Data + DataSize; - InShift = 0; - OutShift = BitDec - BitCmp; - OutDataEnd = Bank->Data + Bank->DataSize; - - for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize) - { - //InVal = ReadBits(Data, InPos, &InShift, BitCmp); - // inlined - is 30% faster - OutBit = 0x00; - InVal = 0x0000; - BitsToRead = BitCmp; - while(BitsToRead) - { - BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; - BitsToRead -= BitReadVal; - BitMask = (1 << BitReadVal) - 1; - - InShift += BitReadVal; - InValB = (*InPos << InShift >> 8) & BitMask; - if (InShift >= 8) - { - InShift -= 8; - InPos ++; - if (InShift) - InValB |= (*InPos << InShift >> 8) & BitMask; - } - - InVal |= InValB << OutBit; - OutBit += BitReadVal; - } - - switch(CmpSubType) - { - case 0x00: // Copy - OutVal = InVal + AddVal; - break; - case 0x01: // Shift Left - OutVal = (InVal << OutShift) + AddVal; - break; - case 0x02: // Table - switch(ValSize) - { - case 0x01: - OutVal = Ent1B[InVal]; - break; - case 0x02: - OutVal = Ent2B[InVal]; - break; - } - break; - } - - //memcpy(OutPos, &OutVal, ValSize); - if (ValSize == 0x01) - *((UINT8*)OutPos) = (UINT8)OutVal; - else //if (ValSize == 0x02) - *((UINT16*)OutPos) = (UINT16)OutVal; - } - break; - case 0x01: // Delta-PCM - BitDec = Data[0x05]; - BitCmp = Data[0x06]; - OutVal = get_le16( Data + 0x08 ); - - Ent1B = (UINT8*)PCMTbl.Entries; - Ent2B = (UINT16*)PCMTbl.Entries; - if (! PCMTbl.EntryCount) - { - Bank->DataSize = 0x00; - return false; - } - else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp) - { - Bank->DataSize = 0x00; - return false; - } - - ValSize = (BitDec + 7) / 8; - OutMask = (1 << BitDec) - 1; - InPos = Data + 0x0A; - InDataEnd = Data + DataSize; - InShift = 0; - OutShift = BitDec - BitCmp; - OutDataEnd = Bank->Data + Bank->DataSize; - AddVal = 0x0000; - - for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize) - { - //InVal = ReadBits(Data, InPos, &InShift, BitCmp); - // inlined - is 30% faster - OutBit = 0x00; - InVal = 0x0000; - BitsToRead = BitCmp; - while(BitsToRead) - { - BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; - BitsToRead -= BitReadVal; - BitMask = (1 << BitReadVal) - 1; - - InShift += BitReadVal; - InValB = (*InPos << InShift >> 8) & BitMask; - if (InShift >= 8) - { - InShift -= 8; - InPos ++; - if (InShift) - InValB |= (*InPos << InShift >> 8) & BitMask; - } - - InVal |= InValB << OutBit; - OutBit += BitReadVal; - } - - switch(ValSize) - { - case 0x01: - AddVal = Ent1B[InVal]; - OutVal += AddVal; - OutVal &= OutMask; - *((UINT8*)OutPos) = (UINT8)OutVal; - break; - case 0x02: - AddVal = Ent2B[InVal]; - OutVal += AddVal; - OutVal &= OutMask; - *((UINT16*)OutPos) = (UINT16)OutVal; - break; - } - } - break; - default: - return false; - } - - return true; -} - -void Vgm_Core::AddPCMData(byte Type, unsigned DataSize, const byte* Data) -{ - unsigned CurBnk; - VGM_PCM_BANK* TempPCM; - VGM_PCM_DATA* TempBnk; - unsigned BankSize; - bool RetVal; - - - if ((Type & 0x3F) >= PCM_BANK_COUNT || has_looped) - return; - - if (Type == 0x7F) - { - ReadPCMTable( DataSize, Data ); - return; - } - - TempPCM = &PCMBank[Type & 0x3F]; - CurBnk = TempPCM->BankCount; - TempPCM->BankCount ++; - TempPCM->BnkPos ++; - if (TempPCM->BnkPos < TempPCM->BankCount) - return; // Speed hack (for restarting playback) - TempPCM->Bank = (VGM_PCM_DATA*)realloc(TempPCM->Bank, - sizeof(VGM_PCM_DATA) * TempPCM->BankCount); - - if (! (Type & 0x40)) - BankSize = DataSize; - else - BankSize = get_le32( Data + 1 ); - TempPCM->Data = ( byte * ) realloc(TempPCM->Data, TempPCM->DataSize + BankSize); - TempBnk = &TempPCM->Bank[CurBnk]; - TempBnk->DataStart = TempPCM->DataSize; - if (! (Type & 0x40)) - { - TempBnk->DataSize = DataSize; - TempBnk->Data = TempPCM->Data + TempBnk->DataStart; - memcpy(TempBnk->Data, Data, DataSize); - } - else - { - TempBnk->Data = TempPCM->Data + TempBnk->DataStart; - RetVal = DecompressDataBlk(TempBnk, DataSize, Data); - if (! RetVal) - { - TempBnk->Data = NULL; - TempBnk->DataSize = 0x00; - return; - } - } - TempPCM->DataSize += BankSize; -} - -const byte* Vgm_Core::GetPointerFromPCMBank(byte Type, unsigned DataPos) -{ - if (Type >= PCM_BANK_COUNT) - return NULL; - - if (DataPos >= PCMBank[Type].DataSize) - return NULL; - - return &PCMBank[Type].Data[DataPos]; -} - -void Vgm_Core::dac_control_grow(byte chip_id) -{ - for ( unsigned i = 0; i < DacCtrlUsed; i++ ) - { - if ( DacCtrlUsg [i] == chip_id ) - { - device_reset_daccontrol( dac_control [i] ); - return; - } - } - unsigned chip_mapped = DacCtrlUsed; - DacCtrlUsg [DacCtrlUsed++] = chip_id; - DacCtrlMap [chip_id] = chip_mapped; - dac_control = (void**) realloc( dac_control, DacCtrlUsed * sizeof(void*) ); - dac_control [chip_mapped] = device_start_daccontrol( vgm_rate, this ); - device_reset_daccontrol( dac_control [chip_mapped] ); -} - -extern "C" void chip_reg_write(void * context, UINT32 Sample, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data) -{ - Vgm_Core * core = (Vgm_Core *) context; - core->chip_reg_write(Sample, ChipType, ChipID, Port, Offset, Data); -} - -void Vgm_Core::chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data) -{ - run_dac_control( Sample ); /* Let's get recursive! */ - ChipID = !!ChipID; - switch (ChipType) - { - case 0x02: - switch (Port) - { - case 0: - if ( Offset == ym2612_dac_port ) - { - write_pcm( Sample, ChipID, Data ); - } - else if ( run_ym2612( ChipID, to_fm_time( Sample ) ) ) - { - if ( Offset == 0x2B ) - { - dac_disabled[ChipID] = (Data >> 7 & 1) - 1; - dac_amp[ChipID] |= dac_disabled[ChipID]; - } - ym2612[ChipID].write0( Offset, Data ); - } - break; - - case 1: - if ( run_ym2612( ChipID, to_fm_time( Sample ) ) ) - { - if ( Offset == ym2612_dac_pan_port ) - { - Blip_Buffer * blip_buf = NULL; - switch ( Data >> 6 ) - { - case 0: blip_buf = NULL; break; - case 1: blip_buf = stereo_buf[0].right(); break; - case 2: blip_buf = stereo_buf[0].left(); break; - case 3: blip_buf = stereo_buf[0].center(); break; - } - /*if ( this->blip_buf != blip_buf ) - { - blip_time_t blip_time = to_psg_time( vgm_time ); - if ( this->blip_buf ) pcm.offset_inline( blip_time, -dac_amp, this->blip_buf ); - if ( blip_buf ) pcm.offset_inline( blip_time, dac_amp, blip_buf ); - }*/ - this->blip_buf[ChipID] = blip_buf; - } - ym2612[ChipID].write1( Offset, Data ); - } - break; - } - break; - - case 0x11: - if ( run_pwm( to_fm_time( Sample ) ) ) - pwm.write( Port, ( ( Offset ) << 8 ) + Data ); - break; - - case 0x00: - psg[ChipID].write_data( to_psg_time( Sample ), Data ); - break; - - case 0x01: - if ( run_ym2413( ChipID, to_fm_time( Sample ) ) ) - ym2413[ChipID].write( Offset, Data ); - break; - - case 0x03: - if ( run_ym2151( ChipID, to_fm_time( Sample ) ) ) - ym2151[ChipID].write( Offset, Data ); - break; - - case 0x06: - if ( run_ym2203( ChipID, to_fm_time( Sample ) ) ) - ym2203[ChipID].write( Offset, Data ); - break; - - case 0x07: - if ( run_ym2608( ChipID, to_fm_time( Sample ) ) ) - { - switch (Port) - { - case 0: ym2608[ChipID].write0( Offset, Data ); break; - case 1: ym2608[ChipID].write1( Offset, Data ); break; - } - } - break; - - case 0x08: - if ( run_ym2610( ChipID, to_fm_time( Sample ) ) ) - { - switch (Port) - { - case 0: ym2610[ChipID].write0( Offset, Data ); break; - case 1: ym2610[ChipID].write1( Offset, Data ); break; - } - } - break; - - case 0x09: - if ( run_ym3812( ChipID, to_fm_time( Sample ) ) ) - ym3812[ChipID].write( Offset, Data ); - break; - - case 0x0C: - if ( run_ymf262( ChipID, to_fm_time( Sample ) ) ) - { - switch (Port) - { - case 0: ymf262[ChipID].write0( Offset, Data ); break; - case 1: ymf262[ChipID].write1( Offset, Data ); break; - } - } - break; - - case 0x0F: - if ( run_ymz280b( to_fm_time( Sample ) ) ) - ymz280b.write( Offset, Data ); - break; - - case 0x12: - ay[ChipID].write_addr( Offset ); - ay[ChipID].write_data( to_ay_time( Sample ), Data ); - break; - - case 0x13: - gbdmg[ChipID].write_register( to_gbdmg_time( Sample ), 0xFF10 + Offset, Data ); - break; - - case 0x17: - if ( run_okim6258( ChipID, to_fm_time( Sample ) ) ) - okim6258[ChipID].write( Offset, Data ); - break; - - case 0x18: - if ( run_okim6295( ChipID, to_fm_time( Sample ) ) ) - okim6295[ChipID].write( Offset, Data ); - break; - - case 0x19: - if ( run_k051649( to_fm_time( Sample ) ) ) - k051649.write( Port, Offset, Data ); - break; - - case 0x1A: - if ( run_k054539( to_fm_time( Sample ) ) ) - k054539.write( ( Port << 8 ) | Offset, Data ); - break; - - case 0x1B: - huc6280[ChipID].write_data( to_huc6280_time( Sample ), 0x800 + Offset, Data ); - break; - - case 0x1D: - if ( run_k053260( to_fm_time( Sample ) ) ) - k053260.write( Offset, Data ); - break; - - case 0x1F: - if ( run_qsound( ChipID, Sample ) ) - qsound[ ChipID ].write( Data, ( Port << 8 ) + Offset ); - break; - } -} - -void Vgm_Core::set_tempo( double t ) -{ - if ( file_begin() ) - { - vgm_rate = (int) (44100 * t + 0.5); - blip_time_factor = (int) ((double) - (1 << blip_time_bits) / vgm_rate * stereo_buf[0].center()->clock_rate() + 0.5); - blip_ay_time_factor = (int) ((double) - (1 << blip_time_bits) / vgm_rate * stereo_buf[1].center()->clock_rate() + 0.5); - blip_huc6280_time_factor = (int) ((double) - (1 << blip_time_bits) / vgm_rate * stereo_buf[2].center()->clock_rate() + 0.5); - blip_gbdmg_time_factor = (int)((double) - (1 << blip_time_bits) / vgm_rate * stereo_buf[3].center()->clock_rate() + 0.5); - //dprintf( "blip_time_factor: %ld\n", blip_time_factor ); - //dprintf( "vgm_rate: %ld\n", vgm_rate ); - // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only) - //blip_time_factor = (int) floor( double (1 << blip_time_bits) * psg_rate_ / 44100 / t + 0.5 ); - //vgm_rate = (int) floor( double (1 << blip_time_bits) * psg_rate_ / blip_time_factor + 0.5 ); - - fm_time_factor = 2 + (int) (fm_rate * (1 << fm_time_bits) / vgm_rate + 0.5); - } -} - -bool Vgm_Core::header_t::valid_tag() const -{ - return !memcmp( tag, "Vgm ", 4 ); -} - -int Vgm_Core::header_t::size() const -{ - unsigned int version = get_le32( this->version ); - unsigned int data_offset; - if ( version >= 0x150 ) - { - data_offset = get_le32( this->data_offset ); - if ( data_offset ) data_offset += offsetof( header_t, data_offset ); - } - else data_offset = 0x40; - unsigned expected_size = ( version > 0x150 ) ? ( ( version > 0x160 ) ? unsigned(size_max) : unsigned(size_151) ) : unsigned(size_min); - if ( expected_size > data_offset ) expected_size = data_offset ? (data_offset > unsigned(size_max) ? unsigned(size_max) : data_offset) : unsigned(size_min); - return expected_size; -} - -void Vgm_Core::header_t::cleanup() -{ - unsigned int version = get_le32( this->version ); - - if ( size() < size_max ) memset( ((byte*)this) + size(), 0, size_max - size() ); - - if ( version < 0x161 ) - { - memset( this->gbdmg_rate, 0, size_max - offsetof(header_t, gbdmg_rate) ); - } - - if ( version < 0x160 ) - { - volume_modifier = 0; - reserved = 0; - loop_base = 0; - } - - if ( version < 0x151 ) memset( this->rf5c68_rate, 0, size_max - size_min ); - - if ( version < 0x150 ) - { - set_le32( data_offset, size_min - offsetof(header_t, data_offset) ); - sn76489_flags = 0; - set_le32( segapcm_rate, 0 ); - set_le32( segapcm_reg, 0 ); - } - - if ( version < 0x110 ) - { - set_le16( noise_feedback, 0 ); - noise_width = 0; - unsigned int rate = get_le32( ym2413_rate ); - set_le32( ym2612_rate, rate ); - set_le32( ym2151_rate, rate ); - } - - if ( version < 0x101 ) - { - set_le32( frame_rate, 0 ); - } -} - -blargg_err_t Vgm_Core::load_mem_( byte const data [], int size ) -{ - assert( offsetof (header_t, rf5c68_rate) == header_t::size_min ); - assert( offsetof (header_t, extra_offset[4]) == header_t::size_max ); - - if ( size <= header_t::size_min ) - return blargg_err_file_type; - - memcpy( &_header, data, header_t::size_min ); - - header_t const& h = header(); - - if ( !h.valid_tag() ) - return blargg_err_file_type; - - int version = get_le32( h.version ); - - check( version < 0x100 ); - - if ( version > 0x150 ) - { - if ( size < header().size() ) - return "Invalid header"; - - memcpy( &_header.rf5c68_rate, data + offsetof (header_t, rf5c68_rate), header().size() - header_t::size_min ); - } - - _header.cleanup(); - - // Get loop - loop_begin = file_end(); - if ( get_le32( h.loop_offset ) ) - loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; - - // PSG rate - int psg_rate = get_le32( h.psg_rate ) & 0x3FFFFFFF; - if ( !psg_rate ) - psg_rate = 3579545; - stereo_buf[0].clock_rate( psg_rate ); - - int ay_rate = get_le32( h.ay8910_rate ) & 0xBFFFFFFF; - if ( !ay_rate ) - ay_rate = 2000000; - stereo_buf[1].clock_rate( ay_rate * 2 ); - ay[0].set_type( (Ay_Apu::Ay_Apu_Type) header().ay8910_type ); - ay[1].set_type( (Ay_Apu::Ay_Apu_Type) header().ay8910_type ); - - int huc6280_rate = get_le32( h.huc6280_rate ) & 0xBFFFFFFF; - if ( !huc6280_rate ) - huc6280_rate = 3579545; - stereo_buf[2].clock_rate( huc6280_rate * 2 ); - - int gbdmg_rate = get_le32( h.gbdmg_rate ) & 0xBFFFFFFF; - if ( !gbdmg_rate ) - gbdmg_rate = Gb_Apu::clock_rate; - stereo_buf[3].clock_rate( gbdmg_rate ); - - // Disable FM - fm_rate = 0; - ymz280b.enable( false ); - ymf262[0].enable( false ); - ymf262[1].enable( false ); - ym3812[0].enable( false ); - ym3812[1].enable( false ); - ym2612[0].enable( false ); - ym2612[1].enable( false ); - ym2610[0].enable( false ); - ym2610[1].enable( false ); - ym2608[0].enable( false ); - ym2608[1].enable( false ); - ym2413[0].enable( false ); - ym2413[1].enable( false ); - ym2203[0].enable( false ); - ym2203[1].enable( false ); - ym2151[0].enable( false ); - ym2151[1].enable( false ); - c140.enable( false ); - segapcm.enable( false ); - rf5c68.enable( false ); - rf5c164.enable( false ); - pwm.enable( false ); - okim6258[0].enable( false ); - okim6258[1].enable( false ); - okim6295[0].enable( false ); - okim6295[1].enable( false ); - k051649.enable( false ); - k053260.enable( false ); - k054539.enable( false ); - qsound[0].enable( false ); - qsound[1].enable( false ); - - set_tempo( 1 ); - - return blargg_ok; -} - -// Update pre-1.10 header FM rates by scanning commands -void Vgm_Core::update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const -{ - byte const* p = file_begin() + header().size(); - int data_offset = get_le32( header().data_offset ); - check( data_offset ); - if ( data_offset ) - p += data_offset + offsetof( header_t, data_offset ) - header().size(); - while ( p < file_end() ) - { - switch ( *p ) - { - case cmd_end: - return; - - case cmd_psg: - case cmd_byte_delay: - p += 2; - break; - - case cmd_delay: - p += 3; - break; - - case cmd_data_block: - p += 7 + get_le32( p + 3 ); - break; - - case cmd_ram_block: - p += 12; - break; - - case cmd_ym2413: - *ym2151_rate = 0; - *ym2612_rate = 0; - return; - - case cmd_ym2612_port0: - case cmd_ym2612_port1: - *ym2612_rate = *ym2413_rate; - *ym2413_rate = 0; - *ym2151_rate = 0; - return; - - case cmd_ym2151: - *ym2151_rate = *ym2413_rate; - *ym2413_rate = 0; - *ym2612_rate = 0; - return; - - default: - p += command_len( *p ); - } - } -} - -blargg_err_t Vgm_Core::init_chips( double* rate, bool reinit ) -{ - int ymz280b_rate = get_le32( header().ymz280b_rate ) & 0xBFFFFFFF; - int ymf262_rate = get_le32( header().ymf262_rate ) & 0xBFFFFFFF; - int ym3812_rate = get_le32( header().ym3812_rate ) & 0xBFFFFFFF; - int ym2612_rate = get_le32( header().ym2612_rate ) & 0xBFFFFFFF; - int ym2610_rate = get_le32( header().ym2610_rate ) & 0x3FFFFFFF; - int ym2608_rate = get_le32( header().ym2608_rate ) & 0x3FFFFFFF; - int ym2413_rate = get_le32( header().ym2413_rate ) & 0xBFFFFFFF; - int ym2203_rate = get_le32( header().ym2203_rate ) & 0xBFFFFFFF; - int ym2151_rate = get_le32( header().ym2151_rate ) & 0xBFFFFFFF; - int c140_rate = get_le32( header().c140_rate ) & 0xBFFFFFFF; - int segapcm_rate = get_le32( header().segapcm_rate ) & 0xBFFFFFFF; - int rf5c68_rate = get_le32( header().rf5c68_rate ) & 0xBFFFFFFF; - int rf5c164_rate = get_le32( header().rf5c164_rate ) & 0xBFFFFFFF; - int pwm_rate = get_le32( header().pwm_rate ) & 0xBFFFFFFF; - int okim6258_rate = get_le32( header().okim6258_rate ) & 0xBFFFFFFF; - int okim6295_rate = get_le32( header().okim6295_rate ) & 0xBFFFFFFF; - int k051649_rate = get_le32( header().k051649_rate ) & 0xBFFFFFFF; - int k053260_rate = get_le32( header().k053260_rate ) & 0xBFFFFFFF; - int k054539_rate = get_le32( header().k054539_rate ) & 0xBFFFFFFF; - int qsound_rate = get_le32( header().qsound_rate ) & 0xBFFFFFFF; - if ( ym2413_rate && get_le32( header().version ) < 0x110 ) - update_fm_rates( &ym2151_rate, &ym2413_rate, &ym2612_rate ); - - *rate = vgm_rate; - - if ( ymf262_rate ) - { - bool dual_chip = !!(header().ymf262_rate[3] & 0x40); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ymf262_rate / 288.0; - int result; - if ( !reinit ) - { - result = ymf262[0].set_rate( fm_rate, ymf262_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ymf262[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ymf262[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ymf262[1].set_rate( fm_rate, ymf262_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ymf262[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ymf262[1].enable(); - } - } - if ( ym3812_rate ) - { - bool dual_chip = !!(header().ym3812_rate[3] & 0x40); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ym3812_rate / 72.0; - int result; - if ( !reinit ) - { - result = ym3812[0].set_rate( fm_rate, ym3812_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym3812[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym3812[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ym3812[1].set_rate( fm_rate, ym3812_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym3812[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym3812[1].enable(); - } - } - if ( ym2612_rate ) - { - bool dual_chip = !!(header().ym2612_rate[3] & 0x40); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ym2612_rate / 144.0; - if ( !reinit ) - { - RETURN_ERR( ym2612[0].set_rate( fm_rate, ym2612_rate ) ); - } - RETURN_ERR( ym2612[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2612[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - RETURN_ERR( ym2612[1].set_rate( fm_rate, ym2612_rate ) ); - } - RETURN_ERR( ym2612[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2612[1].enable(); - } - } - if ( ym2610_rate ) - { - bool dual_chip = !!(header().ym2610_rate[3] & 0x40); - bool is_2610b = !!(header().ym2610_rate[3] & 0x80); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ym2610_rate / 72.0; - int result; - if ( !reinit ) - { - result = ym2610[0].set_rate( fm_rate, ym2610_rate, is_2610b ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2610[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2610[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ym2610[1].set_rate( fm_rate, ym2610_rate, is_2610b ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2610[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2610[1].enable(); - } - } - if ( ym2608_rate ) - { - bool dual_chip = !!(header().ym2610_rate[3] & 0x40); - double gain = dual_chip ? 1.0 : 2.0; - double fm_rate = ym2608_rate / 72.0; - int result; - if ( !reinit ) - { - result = ym2608[0].set_rate( fm_rate, ym2608_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2608[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2608[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ym2608[1].set_rate( fm_rate, ym2608_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2608[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2608[1].enable(); - } - } - if ( ym2413_rate ) - { - bool dual_chip = !!(header().ym2413_rate[3] & 0x40); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ym2413_rate / 72.0; - int result; - if ( !reinit ) - { - result = ym2413[0].set_rate( fm_rate, ym2413_rate ); - if ( result == 2 ) - return "YM2413 FM sound not supported"; - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2413[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2413[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ym2413[1].set_rate( fm_rate, ym2413_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2413[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2413[1].enable(); - } - } - if ( ym2151_rate ) - { - bool dual_chip = !!(header().ym2151_rate[3] & 0x40); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ym2151_rate / 64.0; - int result; - if ( !reinit ) - { - result = ym2151[0].set_rate( fm_rate, ym2151_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2151[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2151[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ym2151[1].set_rate( fm_rate, ym2151_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( ym2151[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2151[1].enable(); - } - } - if ( ym2203_rate ) - { - bool dual_chip = !!(header().ym2203_rate[3] & 0x40); - double gain = dual_chip ? 0.5 : 1.0; - double fm_rate = ym2203_rate / 72.0; - int result; - if ( !reinit ) - { - result = ym2203[0].set_rate( fm_rate, ym2203_rate ); - CHECK_ALLOC ( !result ); - } - RETURN_ERR( ym2203[0].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2203[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - result = ym2203[1].set_rate( fm_rate, ym2203_rate ); - CHECK_ALLOC ( !result ); - } - RETURN_ERR( ym2203[1].setup( fm_rate / vgm_rate, 0.85, gain ) ); - ym2203[1].enable(); - } - } - - if ( segapcm_rate ) - { - double pcm_rate = segapcm_rate / 128.0; - if ( !reinit ) - { - int result = segapcm.set_rate( get_le32( header().segapcm_reg ) ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( segapcm.setup( pcm_rate / vgm_rate, 0.85, 1.5 ) ); - segapcm.enable(); - } - if ( rf5c68_rate ) - { - double pcm_rate = rf5c68_rate / 384.0; - if ( !reinit ) - { - int result = rf5c68.set_rate(); - CHECK_ALLOC( !result ); - } - RETURN_ERR( rf5c68.setup( pcm_rate / vgm_rate, 0.85, 0.6875 ) ); - rf5c68.enable(); - } - if ( rf5c164_rate ) - { - double pcm_rate = rf5c164_rate / 384.0; - if ( !reinit ) - { - int result = rf5c164.set_rate( rf5c164_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( rf5c164.setup( pcm_rate / vgm_rate, 0.85, 0.5 ) ); - rf5c164.enable(); - } - if ( pwm_rate ) - { - double pcm_rate = 22020.0; - if ( !reinit ) - { - int result = pwm.set_rate( pwm_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( pwm.setup( pcm_rate / vgm_rate, 0.85, 0.875 ) ); - pwm.enable(); - } - if ( okim6258_rate ) - { - bool dual_chip = !!( header().okim6258_rate[3] & 0x40 ); - if ( !reinit ) - { - okim6258_hz[0] = okim6258[0].set_rate( okim6258_rate, header().okim6258_flags & 0x03, ( header().okim6258_flags & 0x04 ) >> 2, ( header().okim6258_flags & 0x08 ) >> 3 ); - CHECK_ALLOC( okim6258_hz[0] ); - } - RETURN_ERR( okim6258[0].setup( (double)okim6258_hz[0] / vgm_rate, 0.85, 1.0 ) ); - okim6258[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - okim6258_hz[1] = okim6258[1].set_rate( okim6258_rate, header().okim6258_flags & 0x03, ( header().okim6258_flags & 0x04 ) >> 2, ( header().okim6258_flags & 0x08 ) >> 3 ); - CHECK_ALLOC( okim6258_hz[1] ); - } - RETURN_ERR( okim6258[1].setup( (double)okim6258_hz[1] / vgm_rate, 0.85, 1.0 ) ); - okim6258[1].enable(); - } - } - if ( okim6295_rate ) - { - // moo - Mem_File_Reader rdr( file_begin(), file_size() ); - Music_Emu * vgm = gme_vgm_type->new_info(); - track_info_t info; - vgm->load( rdr ); - vgm->track_info( &info, 0 ); - delete vgm; - - bool is_cp_system = strncmp( info.system, "CP", 2 ) == 0; - bool dual_chip = !!( header().okim6295_rate[3] & 0x40 ); - double gain = is_cp_system ? 0.4296875 : 1.0; - if ( dual_chip ) gain *= 0.5; - if ( !reinit ) - { - okim6295_hz = okim6295[0].set_rate( okim6295_rate ); - CHECK_ALLOC( okim6295_hz ); - } - RETURN_ERR( okim6295[0].setup( (double)okim6295_hz / vgm_rate, 0.85, gain ) ); - okim6295[0].enable(); - if ( dual_chip ) - { - if ( !reinit ) - { - int result = okim6295[1].set_rate( okim6295_rate ); - CHECK_ALLOC( result ); - } - RETURN_ERR( okim6295[1].setup( (double)okim6295_hz / vgm_rate, 0.85, gain ) ); - okim6295[1].enable(); - } - } - if ( c140_rate ) - { - double pcm_rate = c140_rate; - if ( !reinit ) - { - int result = c140.set_rate( header().c140_type, c140_rate, c140_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( c140.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) ); - c140.enable(); - } - if ( k051649_rate ) - { - double pcm_rate = k051649_rate / 16.0; - if ( !reinit ) - { - int result = k051649.set_rate( k051649_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( k051649.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) ); - k051649.enable(); - } - if ( k053260_rate ) - { - double pcm_rate = k053260_rate / 32.0; - if ( !reinit ) - { - int result = k053260.set_rate( k053260_rate ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( k053260.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) ); - k053260.enable(); - } - if ( k054539_rate ) - { - double pcm_rate = k054539_rate; - if ( !reinit ) - { - int result = k054539.set_rate( k054539_rate, header().k054539_flags ); - CHECK_ALLOC( !result ); - } - RETURN_ERR( k054539.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) ); - k054539.enable(); - } - if ( ymz280b_rate ) - { - if ( !reinit ) - { - ymz280b_hz = ymz280b.set_rate( ymz280b_rate ); - CHECK_ALLOC( ymz280b_hz ); - } - RETURN_ERR( ymz280b.setup( (double)ymz280b_hz / vgm_rate, 0.85, 0.59375 ) ); - ymz280b.enable(); - } - if ( qsound_rate ) - { - /*double pcm_rate = (double)qsound_rate / 166.0;*/ - if ( !reinit ) - { - int result = qsound[0].set_rate( qsound_rate ); - CHECK_ALLOC( result ); - } - qsound[0].set_sample_rate( vgm_rate ); - RETURN_ERR( qsound[0].setup( 1.0, 0.85, 1.0 ) ); - qsound[0].enable(); - } - - fm_rate = *rate; - - return blargg_ok; -} - -void Vgm_Core::start_track() -{ - psg[0].reset( get_le16( header().noise_feedback ), header().noise_width ); - psg[1].reset( get_le16( header().noise_feedback ), header().noise_width ); - ay[0].reset(); - ay[1].reset(); - huc6280[0].reset(); - huc6280[1].reset(); - gbdmg[0].reset(); - gbdmg[1].reset(); - - blip_buf[0] = stereo_buf[0].center(); - blip_buf[1] = blip_buf[0]; - - dac_disabled[0] = -1; - dac_disabled[1] = -1; - pos = file_begin() + header().size(); - dac_amp[0] = -1; - dac_amp[1] = -1; - vgm_time = 0; - int data_offset = get_le32( header().data_offset ); - check( data_offset ); - if ( data_offset ) - pos += data_offset + offsetof (header_t,data_offset) - header().size(); - pcm_pos = pos; - - if ( uses_fm() ) - { - if ( rf5c68.enabled() ) - rf5c68.reset(); - - if ( rf5c164.enabled() ) - rf5c164.reset(); - - if ( segapcm.enabled() ) - segapcm.reset(); - - if ( pwm.enabled() ) - pwm.reset(); - - if ( okim6258[0].enabled() ) - okim6258[0].reset(); - - if ( okim6258[1].enabled() ) - okim6258[1].reset(); - - if ( okim6295[0].enabled() ) - okim6295[0].reset(); - - if ( okim6295[1].enabled() ) - okim6295[1].reset(); - - if ( k051649.enabled() ) - k051649.reset(); - - if ( k053260.enabled() ) - k053260.reset(); - - if ( k054539.enabled() ) - k054539.reset(); - - if ( c140.enabled() ) - c140.reset(); - - if ( ym2151[0].enabled() ) - ym2151[0].reset(); - - if ( ym2151[1].enabled() ) - ym2151[1].reset(); - - if ( ym2203[0].enabled() ) - ym2203[0].reset(); - - if ( ym2203[1].enabled() ) - ym2203[1].reset(); - - if ( ym2413[0].enabled() ) - ym2413[0].reset(); - - if ( ym2413[1].enabled() ) - ym2413[1].reset(); - - if ( ym2612[0].enabled() ) - ym2612[0].reset(); - - if ( ym2612[1].enabled() ) - ym2612[1].reset(); - - if ( ym2610[0].enabled() ) - ym2610[0].reset(); - - if ( ym2610[1].enabled() ) - ym2610[1].reset(); - - if ( ym2608[0].enabled() ) - ym2608[0].reset(); - - if ( ym2608[1].enabled() ) - ym2608[0].reset(); - - if ( ym3812[0].enabled() ) - ym3812[0].reset(); - - if ( ym3812[1].enabled() ) - ym3812[1].reset(); - - if ( ymf262[0].enabled() ) - ymf262[0].reset(); - - if ( ymf262[1].enabled() ) - ymf262[1].reset(); - - if ( ymz280b.enabled() ) - ymz280b.reset(); - - if ( qsound[0].enabled() ) - qsound[0].reset(); - - if ( qsound[1].enabled() ) - qsound[1].reset(); - - stereo_buf[0].clear(); - stereo_buf[1].clear(); - stereo_buf[2].clear(); - stereo_buf[3].clear(); - } - - for ( unsigned i = 0; i < DacCtrlUsed; i++ ) - { - device_reset_daccontrol( dac_control [i] ); - DacCtrlTime[DacCtrlMap[i]] = 0; - } - - for ( unsigned i = 0; i < PCM_BANK_COUNT; i++) - { - // reset PCM Bank, but not the data - // (this way I don't need to decompress the data again when restarting) - PCMBank [i].DataPos = 0; - PCMBank [i].BnkPos = 0; - } - PCMTbl.EntryCount = 0; - - fm_time_offset = 0; - ay_time_offset = 0; - huc6280_time_offset = 0; - gbdmg_time_offset = 0; - - dac_control_recursion = 0; -} - -inline Vgm_Core::fm_time_t Vgm_Core::to_fm_time( vgm_time_t t ) const -{ - return (t * fm_time_factor + fm_time_offset) >> fm_time_bits; -} - -inline blip_time_t Vgm_Core::to_psg_time( vgm_time_t t ) const -{ - return (t * blip_time_factor) >> blip_time_bits; -} - -inline blip_time_t Vgm_Core::to_ay_time( vgm_time_t t ) const -{ - return (t * blip_ay_time_factor) >> blip_time_bits; -} - -inline blip_time_t Vgm_Core::to_huc6280_time( vgm_time_t t ) const -{ - return (t * blip_huc6280_time_factor) >> blip_time_bits; -} - -inline blip_time_t Vgm_Core::to_gbdmg_time( vgm_time_t t ) const -{ - return (t * blip_gbdmg_time_factor) >> blip_time_bits; -} - -void Vgm_Core::write_pcm( vgm_time_t vgm_time, int chip, int amp ) -{ - chip = !!chip; - if ( blip_buf[chip] ) - { - check( amp >= 0 ); - blip_time_t blip_time = to_psg_time( vgm_time ); - int old = dac_amp[chip]; - int delta = amp - old; - dac_amp[chip] = amp; - blip_buf[chip]->set_modified(); - if ( old >= 0 ) // first write is ignored, to avoid click - pcm.offset_inline( blip_time, delta, blip_buf[chip] ); - else - dac_amp[chip] |= dac_disabled[chip]; - } -} - -blip_time_t Vgm_Core::run( vgm_time_t end_time ) -{ - vgm_time_t vgm_time = this->vgm_time; - vgm_time_t vgm_loop_time = ~0; - int ChipID; - byte const* pos = this->pos; - if ( pos > file_end() ) - set_warning( "Stream lacked end event" ); - - while ( vgm_time < end_time && pos < file_end() ) - { - // TODO: be sure there are enough bytes left in stream for particular command - // so we don't read past end - switch ( *pos++ ) - { - case cmd_end: - if ( vgm_loop_time == ~0 ) vgm_loop_time = vgm_time; - else if ( vgm_loop_time == vgm_time ) loop_begin = file_end(); // XXX some files may loop forever on a region without any delay commands - pos = loop_begin; // if not looped, loop_begin == file_end() - if ( pos != file_end() ) has_looped = true; - break; - - case cmd_delay_735: - vgm_time += 735; - break; - - case cmd_delay_882: - vgm_time += 882; - break; - - case cmd_gg_stereo: - psg[0].write_ggstereo( to_psg_time( vgm_time ), *pos++ ); - break; - - case cmd_gg_stereo_2: - psg[1].write_ggstereo( to_psg_time( vgm_time ), *pos++ ); - break; - - case cmd_psg: - psg[0].write_data( to_psg_time( vgm_time ), *pos++ ); - break; - - case cmd_psg_2: - psg[1].write_data( to_psg_time( vgm_time ), *pos++ ); - break; - - case cmd_ay8910: - ChipID = !!(pos [0] & 0x80); - chip_reg_write( vgm_time, 0x12, ChipID, 0x00, pos [0] & 0x7F, pos [1] ); - pos += 2; - break; - - case cmd_delay: - vgm_time += pos [1] * 0x100 + pos [0]; - pos += 2; - break; - - case cmd_byte_delay: - vgm_time += *pos++; - break; - - case cmd_segapcm_write: - if ( get_le32( header().segapcm_rate ) > 0 ) - if ( run_segapcm( to_fm_time( vgm_time ) ) ) - segapcm.write( get_le16( pos ), pos [2] ); - pos += 3; - break; - - case cmd_rf5c68: - if ( run_rf5c68( to_fm_time( vgm_time ) ) ) - rf5c68.write( pos [0], pos [1] ); - pos += 2; - break; - - case cmd_rf5c68_mem: - if ( run_rf5c68( to_fm_time( vgm_time ) ) ) - rf5c68.write_mem( get_le16( pos ), pos [2] ); - pos += 3; - break; - - case cmd_rf5c164: - if ( run_rf5c164( to_fm_time( vgm_time ) ) ) - rf5c164.write( pos [0], pos [1] ); - pos += 2; - break; - - case cmd_rf5c164_mem: - if ( run_rf5c164( to_fm_time( vgm_time ) ) ) - rf5c164.write_mem( get_le16( pos ), pos [2] ); - pos += 3; - break; - - case cmd_pwm: - chip_reg_write( vgm_time, 0x11, 0x00, pos [0] >> 4, pos [0] & 0x0F, pos [1] ); - pos += 2; - break; - - case cmd_c140: - if ( get_le32( header().c140_rate ) > 0 ) - if ( run_c140( to_fm_time( vgm_time ) ) ) - c140.write( get_be16( pos ), pos [2] ); - pos += 3; - break; - - case cmd_ym2151: - chip_reg_write( vgm_time, 0x03, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2151_2: - chip_reg_write( vgm_time, 0x03, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2203: - chip_reg_write( vgm_time, 0x06, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2203_2: - chip_reg_write( vgm_time, 0x06, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2413: - chip_reg_write( vgm_time, 0x01, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2413_2: - chip_reg_write( vgm_time, 0x01, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym3812: - chip_reg_write( vgm_time, 0x09, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym3812_2: - chip_reg_write( vgm_time, 0x09, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ymf262_port0: - chip_reg_write( vgm_time, 0x0C, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ymf262_2_port0: - chip_reg_write( vgm_time, 0x0C, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ymf262_port1: - chip_reg_write( vgm_time, 0x0C, 0x00, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ymf262_2_port1: - chip_reg_write( vgm_time, 0x0C, 0x01, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ymz280b: - chip_reg_write( vgm_time, 0x0F, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2612_port0: - chip_reg_write( vgm_time, 0x02, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2612_2_port0: - chip_reg_write( vgm_time, 0x02, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2612_port1: - chip_reg_write( vgm_time, 0x02, 0x00, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2612_2_port1: - chip_reg_write( vgm_time, 0x02, 0x01, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2610_port0: - chip_reg_write( vgm_time, 0x08, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2610_2_port0: - chip_reg_write( vgm_time, 0x08, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2610_port1: - chip_reg_write( vgm_time, 0x08, 0x00, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2610_2_port1: - chip_reg_write( vgm_time, 0x08, 0x01, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2608_port0: - chip_reg_write( vgm_time, 0x07, 0x00, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2608_2_port0: - chip_reg_write( vgm_time, 0x07, 0x01, 0x00, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2608_port1: - chip_reg_write( vgm_time, 0x07, 0x00, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2608_2_port1: - chip_reg_write( vgm_time, 0x07, 0x01, 0x01, pos [0], pos [1] ); - pos += 2; - break; - - case cmd_okim6258_write: - chip_reg_write( vgm_time, 0x17, ChipID, 0x00, pos [0] & 0x7F, pos [1] ); - pos += 2; - break; - - case cmd_okim6295_write: - ChipID = (pos [0] & 0x80) ? 1 : 0; - chip_reg_write( vgm_time, 0x18, ChipID, 0x00, pos [0] & 0x7F, pos [1] ); - pos += 2; - break; - - case cmd_huc6280_write: - ChipID = (pos [0] & 0x80) ? 1 : 0; - chip_reg_write( vgm_time, 0x1B, ChipID, 0x00, pos [0] & 0x7F, pos [1] ); - pos += 2; - break; - - case cmd_gbdmg_write: - ChipID = (pos [0] & 0x80) ? 1 : 0; - chip_reg_write( vgm_time, 0x13, ChipID, 0x00, pos [0] & 0x7F, pos [1] ); - pos += 2; - break; - - case cmd_k051649_write: - chip_reg_write( vgm_time, 0x19, 0x00, pos [0] & 0x7F, pos [1], pos [2] ); - pos += 3; - break; - - case cmd_k053260_write: - chip_reg_write( vgm_time, 0x1D, 0x00, 0x00, pos [0] & 0x7F, pos [1] ); - pos += 2; - break; - - case cmd_k054539_write: - chip_reg_write( vgm_time, 0x1A, 0x00, pos [0] & 0x7F, pos [1], pos [2] ); - pos += 3; - break; - - case cmd_qsound_write: - chip_reg_write( vgm_time, 0x1F, 0x00, pos [0], pos [1], pos [2] ); - pos += 3; - break; - - case cmd_dacctl_setup: - if ( run_dac_control( vgm_time ) ) - { - unsigned chip = pos [0]; - if ( chip < 0xFF ) - { - if ( ! DacCtrl [chip].Enable ) - { - dac_control_grow( chip ); - DacCtrl [chip].Enable = true; - } - daccontrol_setup_chip( dac_control [DacCtrlMap [chip]], pos [1] & 0x7F, ( pos [1] & 0x80 ) >> 7, get_be16( pos + 2 ) ); - } - } - pos += 4; - break; - - case cmd_dacctl_data: - if ( run_dac_control( vgm_time ) ) - { - unsigned chip = pos [0]; - if ( chip < 0xFF && DacCtrl [chip].Enable ) - { - DacCtrl [chip].Bank = pos [1]; - if ( DacCtrl [chip].Bank >= 0x40 ) - DacCtrl [chip].Bank = 0x00; - - VGM_PCM_BANK * TempPCM = &PCMBank [DacCtrl [chip].Bank]; - daccontrol_set_data( dac_control [DacCtrlMap [chip]], TempPCM->Data, TempPCM->DataSize, pos [2], pos [3] ); - } - } - pos += 4; - break; - case cmd_dacctl_freq: - if ( run_dac_control( vgm_time ) ) - { - unsigned chip = pos [0]; - if ( chip < 0xFF && DacCtrl [chip].Enable ) - { - daccontrol_set_frequency( dac_control [DacCtrlMap [chip]], get_le32( pos + 1 ) ); - } - } - pos += 5; - break; - case cmd_dacctl_play: - if ( run_dac_control( vgm_time ) ) - { - unsigned chip = pos [0]; - if ( chip < 0xFF && DacCtrl [chip].Enable && PCMBank [DacCtrl [chip].Bank].BankCount ) - { - daccontrol_start( dac_control [DacCtrlMap [chip]], get_le32( pos + 1 ), pos [5], get_le32( pos + 6 ) ); - } - } - pos += 10; - break; - case cmd_dacctl_stop: - if ( run_dac_control( vgm_time ) ) - { - unsigned chip = pos [0]; - if ( chip < 0xFF && DacCtrl [chip].Enable ) - { - daccontrol_stop( dac_control [DacCtrlMap [chip]] ); - } - else if ( chip == 0xFF ) - { - for ( unsigned i = 0; i < DacCtrlUsed; i++ ) - { - daccontrol_stop( dac_control [i] ); - } - } - } - pos++; - break; - case cmd_dacctl_playblock: - if ( run_dac_control( vgm_time ) ) - { - unsigned chip = pos [0]; - if ( chip < 0xFF && DacCtrl [chip].Enable && PCMBank [DacCtrl [chip].Bank].BankCount ) - { - VGM_PCM_BANK * TempPCM = &PCMBank [DacCtrl [chip].Bank]; - unsigned block_number = get_le16( pos + 1 ); - if ( block_number >= TempPCM->BankCount ) - block_number = 0; - VGM_PCM_DATA * TempBnk = &TempPCM->Bank [block_number]; - unsigned flags = DCTRL_LMODE_BYTES | ((pos [4] & 1) << 7); - daccontrol_start( dac_control [DacCtrlMap [chip]], TempBnk->DataStart, flags, TempBnk->DataSize ); - } - } - pos += 4; - break; - - case cmd_data_block: { - check( *pos == cmd_end ); - int type = pos [1]; - int size = get_le32( pos + 2 ); - int chipid = 0; - if ( size & 0x80000000 ) - { - size &= 0x7FFFFFFF; - chipid = 1; - } - pos += 6; - switch ( type & 0xC0 ) - { - case pcm_block_type: - case pcm_aux_block_type: - AddPCMData( type, size, pos ); - break; - - case rom_block_type: - if ( size >= 8 ) - { - int rom_size = get_le32( pos ); - int data_start = get_le32( pos + 4 ); - int data_size = size - 8; - void * rom_data = ( void * ) ( pos + 8 ); - - switch ( type ) - { - case rom_segapcm: - if ( segapcm.enabled() ) - segapcm.write_rom( rom_size, data_start, data_size, rom_data ); - break; - - case rom_ym2608_deltat: - if ( ym2608[chipid].enabled() ) - { - ym2608[chipid].write_rom( 0x02, rom_size, data_start, data_size, rom_data ); - } - break; - - case rom_ym2610_adpcm: - case rom_ym2610_deltat: - if ( ym2610[chipid].enabled() ) - { - int rom_id = 0x01 + ( type - rom_ym2610_adpcm ); - ym2610[chipid].write_rom( rom_id, rom_size, data_start, data_size, rom_data ); - } - break; - - case rom_ymz280b: - if ( ymz280b.enabled() ) - ymz280b.write_rom( rom_size, data_start, data_size, rom_data ); - break; - - case rom_okim6295: - if ( okim6295[chipid].enabled() ) - okim6295[chipid].write_rom( rom_size, data_start, data_size, rom_data ); - break; - - case rom_k054539: - if ( k054539.enabled() ) - k054539.write_rom( rom_size, data_start, data_size, rom_data ); - break; - - case rom_c140: - if ( c140.enabled() ) - c140.write_rom( rom_size, data_start, data_size, rom_data ); - break; - - case rom_k053260: - if ( k053260.enabled() ) - k053260.write_rom( rom_size, data_start, data_size, rom_data ); - break; - - case rom_qsound: - if ( qsound[chipid].enabled() ) - qsound[chipid].write_rom( rom_size, data_start, data_size, rom_data ); - break; - } - } - break; - - case ram_block_type: - if ( size >= 2 ) - { - int data_start = get_le16( pos ); - int data_size = size - 2; - void * ram_data = ( void * ) ( pos + 2 ); - - switch ( type ) - { - case ram_rf5c68: - if ( rf5c68.enabled() ) - rf5c68.write_ram( data_start, data_size, ram_data ); - break; - - case ram_rf5c164: - if ( rf5c164.enabled() ) - rf5c164.write_ram( data_start, data_size, ram_data ); - break; - } - } - break; - } - pos += size; - break; - } - - case cmd_ram_block: { - check( *pos == cmd_end ); - int type = pos[ 1 ]; - int data_start = get_le24( pos + 2 ); - int data_addr = get_le24( pos + 5 ); - int data_size = get_le24( pos + 8 ); - if ( !data_size ) data_size += 0x01000000; - void * data_ptr = (void *) GetPointerFromPCMBank( type, data_start ); - switch ( type ) - { - case rf5c68_ram_block: - if ( rf5c68.enabled() ) - rf5c68.write_ram( data_addr, data_size, data_ptr ); - break; - - case rf5c164_ram_block: - if ( rf5c164.enabled() ) - rf5c164.write_ram( data_addr, data_size, data_ptr ); - break; - } - pos += 11; - break; - } - - case cmd_pcm_seek: - pcm_pos = GetPointerFromPCMBank( 0, get_le32( pos ) ); - pos += 4; - break; - - default: - int cmd = pos [-1]; - switch ( cmd & 0xF0 ) - { - case cmd_pcm_delay: - chip_reg_write( vgm_time, 0x02, 0x00, 0x00, ym2612_dac_port, *pcm_pos++ ); - vgm_time += cmd & 0x0F; - break; - - case cmd_short_delay: - vgm_time += (cmd & 0x0F) + 1; - break; - - case 0x50: - pos += 2; - break; - - default: - pos += command_len( cmd ) - 1; - set_warning( "Unknown stream event" ); - } - } - } - vgm_time -= end_time; - this->pos = pos; - this->vgm_time = vgm_time; - - return to_psg_time( end_time ); -} - -blip_time_t Vgm_Core::run_psg( int msec ) -{ - blip_time_t t = run( msec * vgm_rate / 1000 ); - psg[0].end_frame( t ); - psg[1].end_frame( t ); - return t; -} - -int Vgm_Core::play_frame( blip_time_t blip_time, int sample_count, blip_sample_t out [] ) -{ - // to do: timing is working mostly by luck - int min_pairs = (unsigned) sample_count / 2; - int vgm_time = (min_pairs << fm_time_bits) / fm_time_factor - 1; - assert( to_fm_time( vgm_time ) <= min_pairs ); - int pairs; - while ( (pairs = to_fm_time( vgm_time )) < min_pairs ) - vgm_time++; - //dprintf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs ); - - memset( out, 0, pairs * stereo * sizeof *out ); - - if ( ymf262[0].enabled() ) - { - ymf262[0].begin_frame( out ); - if ( ymf262[1].enabled() ) - { - ymf262[1].begin_frame( out ); - } - } - if ( ym3812[0].enabled() ) - { - ym3812[0].begin_frame( out ); - if ( ym3812[1].enabled() ) - { - ym3812[1].begin_frame( out ); - } - } - if ( ym2612[0].enabled() ) - { - ym2612[0].begin_frame( out ); - if ( ym2612[1].enabled() ) - { - ym2612[1].begin_frame( out ); - } - } - if ( ym2610[0].enabled() ) - { - ym2610[0].begin_frame( out ); - if ( ym2610[1].enabled() ) - { - ym2610[1].begin_frame( out ); - } - } - if ( ym2608[0].enabled() ) - { - ym2608[0].begin_frame( out ); - if ( ym2608[1].enabled() ) - { - ym2608[1].begin_frame( out ); - } - } - if ( ym2413[0].enabled() ) - { - ym2413[0].begin_frame( out ); - if ( ym2413[1].enabled() ) - { - ym2413[1].begin_frame( out ); - } - } - if ( ym2203[0].enabled() ) - { - ym2203[0].begin_frame( out ); - if ( ym2203[1].enabled() ) - { - ym2203[1].begin_frame( out ); - } - } - if ( ym2151[0].enabled() ) - { - ym2151[0].begin_frame( out ); - if ( ym2151[1].enabled() ) - { - ym2151[1].begin_frame( out ); - } - } - - if ( c140.enabled() ) - { - c140.begin_frame( out ); - } - if ( segapcm.enabled() ) - { - segapcm.begin_frame( out ); - } - if ( rf5c68.enabled() ) - { - rf5c68.begin_frame( out ); - } - if ( rf5c164.enabled() ) - { - rf5c164.begin_frame( out ); - } - if ( pwm.enabled() ) - { - pwm.begin_frame( out ); - } - if ( okim6258[0].enabled() ) - { - okim6258[0].begin_frame( out ); - if ( okim6258[1].enabled() ) - { - okim6258[1].begin_frame( out ); - } - } - if ( okim6295[0].enabled() ) - { - okim6295[0].begin_frame( out ); - if ( okim6295[1].enabled() ) - { - okim6295[1].begin_frame( out ); - } - } - if ( k051649.enabled() ) - { - k051649.begin_frame( out ); - } - if ( k053260.enabled() ) - { - k053260.begin_frame( out ); - } - if ( k054539.enabled() ) - { - k054539.begin_frame( out ); - } - if ( ymz280b.enabled() ) - { - ymz280b.begin_frame( out ); - } - if ( qsound[0].enabled() ) - { - qsound[0].begin_frame( out ); - if ( qsound[1].enabled() ) - { - qsound[1].begin_frame( out ); - } - } - - run( vgm_time ); - - run_dac_control( vgm_time ); - - run_ymf262( 0, pairs ); run_ymf262( 1, pairs ); - run_ym3812( 0, pairs ); run_ym3812( 1, pairs ); - run_ym2612( 0, pairs ); run_ym2612( 1, pairs ); - run_ym2610( 0, pairs ); run_ym2610( 1, pairs ); - run_ym2608( 0, pairs ); run_ym2608( 1, pairs ); - run_ym2413( 0, pairs ); run_ym2413( 1, pairs ); - run_ym2203( 0, pairs ); run_ym2203( 1, pairs ); - run_ym2151( 0, pairs ); run_ym2151( 1, pairs ); - run_c140( pairs ); - run_segapcm( pairs ); - run_rf5c68( pairs ); - run_rf5c164( pairs ); - run_pwm( pairs ); - run_okim6258( 0, pairs ); run_okim6258( 1, pairs ); - run_okim6295( 0, pairs ); run_okim6295( 1, pairs ); - run_k051649( pairs ); - run_k053260( pairs ); - run_k054539( pairs ); - run_ymz280b( pairs ); - run_qsound( 0, pairs ); run_qsound( 1, pairs ); - - fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - (pairs << fm_time_bits); - - psg[0].end_frame( blip_time ); - psg[1].end_frame( blip_time ); - - ay_time_offset = (vgm_time * blip_ay_time_factor + ay_time_offset) - (pairs << blip_time_bits); - - blip_time_t ay_end_time = to_ay_time( vgm_time ); - ay[0].end_frame( ay_end_time ); - ay[1].end_frame( ay_end_time ); - - huc6280_time_offset = (vgm_time * blip_huc6280_time_factor + huc6280_time_offset) - (pairs << blip_time_bits); - - blip_time_t huc6280_end_time = to_huc6280_time( vgm_time ); - huc6280[0].end_frame( huc6280_end_time ); - huc6280[1].end_frame( huc6280_end_time ); - - gbdmg_time_offset = (vgm_time * blip_gbdmg_time_factor + gbdmg_time_offset) - (pairs << blip_time_bits); - - blip_time_t gbdmg_end_time = to_gbdmg_time( vgm_time ); - gbdmg[0].end_frame( gbdmg_end_time ); - gbdmg[1].end_frame( gbdmg_end_time ); - - memset( DacCtrlTime, 0, sizeof(DacCtrlTime) ); - - return pairs * stereo; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Vgm_Core.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Vgm_Core::Vgm_Core() +{ + vgmp = (VGM_PLAYER *) VGMPlay_Init(); + vgmp->VGMMaxLoop = 0; + VGMPlay_Init2(vgmp); +} + +Vgm_Core::~Vgm_Core() +{ + StopVGM(vgmp); + VGMPlay_Deinit(vgmp); +} + +static UINT32 gcd(UINT32 x, UINT32 y) +{ + UINT32 shift; + UINT32 diff; + + // Thanks to Wikipedia for this algorithm + // http://en.wikipedia.org/wiki/Binary_GCD_algorithm + if (! x || ! y) + return x | y; + + for (shift = 0; ((x | y) & 1) == 0; shift ++) + { + x >>= 1; + y >>= 1; + } + + while((x & 1) == 0) + x >>= 1; + + do + { + while((y & 1) == 0) + y >>= 1; + + if (x < y) + { + y -= x; + } + else + { + diff = x - y; + x = y; + y = diff; + } + y >>= 1; + } while(y); + + return x << shift; +} + +void Vgm_Core::set_tempo( double t ) +{ + if ( file_begin() ) + { + int vgm_rate_unit = header().lngRate; + if (!vgm_rate_unit) + vgm_rate_unit = 44100; + int vgm_rate = (int) (vgm_rate_unit * t + 0.5); + int old_rate = vgmp->VGMPbRate; + vgmp->VGMPbRate = vgm_rate; + vgmp->SampleRate = sample_rate; + + if (vgmp->PlayingMode != 0xFF) + { + if (!old_rate) + old_rate = vgm_rate_unit; + + INT32 TempSLng = gcd(vgm_rate_unit, vgmp->VGMPbRate); + vgmp->VGMPbRateMul = vgm_rate_unit / TempSLng; + vgmp->VGMPbRateDiv = vgmp->VGMPbRate / TempSLng; + + vgmp->VGMSmplRateMul = vgmp->SampleRate * vgmp->VGMPbRateMul; + vgmp->VGMSmplRateDiv = vgmp->VGMSampleRate * vgmp->VGMPbRateDiv; + // same as above - to speed up the VGM <-> Playback calculation + TempSLng = gcd(vgmp->VGMSmplRateMul, vgmp->VGMSmplRateDiv); + vgmp->VGMSmplRateMul /= TempSLng; + vgmp->VGMSmplRateDiv /= TempSLng; + + vgmp->VGMSmplPlayed = (INT32)((INT64)vgmp->VGMSmplPlayed * old_rate / vgm_rate); + } + } +} + +struct VGM_FILE_mem +{ + VGM_FILE vf; + const BOOST::uint8_t* buffer; + UINT32 ptr; + UINT32 size; +}; + +static int VGMF_mem_Read(VGM_FILE* f, void* out, UINT32 count) +{ + VGM_FILE_mem* mf = (VGM_FILE_mem *) f; + if (count + mf->ptr > mf->size) + count = mf->size - mf->ptr; + memcpy(out, mf->buffer + mf->ptr, count); + mf->ptr += count; + return count; +} + +static int VGMF_mem_Seek(VGM_FILE* f, UINT32 offset) +{ + VGM_FILE_mem* mf = (VGM_FILE_mem *) f; + if (offset > mf->size) + offset = mf->size; + mf->ptr = offset; + return 0; +} + +static UINT32 VGMF_mem_GetSize(VGM_FILE* f) +{ + VGM_FILE_mem* mf = (VGM_FILE_mem *) f; + return mf->size; +} + +blargg_err_t Vgm_Core::load_mem_( byte const data [], int size ) +{ + VGM_FILE_mem memFile; + + memFile.vf.Read = &VGMF_mem_Read; + memFile.vf.Seek = &VGMF_mem_Seek; + memFile.vf.GetSize = &VGMF_mem_GetSize; + memFile.buffer = data; + memFile.ptr = 0; + memFile.size = size; + + if ( !GetVGMFileInfo_Handle( (VGM_FILE *) &memFile, &_header, 0 ) ) + return blargg_err_file_type; + + memFile.ptr = 0; + + if ( !OpenVGMFile_Handle( vgmp, (VGM_FILE *) &memFile ) ) + return blargg_err_file_type; + + if ( !header().lngLoopOffset ) + vgmp->VGMMaxLoop = 1; + + set_tempo( 1 ); + + return blargg_ok; +} + +void Vgm_Core::start_track() +{ + PlayVGM(vgmp); + RestartVGM(vgmp); +} + +int Vgm_Core::play_( int sample_count, short out [] ) +{ + // to do: timing is working mostly by luck + int pairs = (unsigned) sample_count / 2; + + memset(out, 0, sizeof(short) * pairs * 2); + FillBuffer(vgmp, (WAVE_16BS*) out, pairs); + + return pairs * 2; +} + +void Vgm_Core::skip_( int count ) +{ + SeekVGM( vgmp, true, count / 2 ); +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Vgm_Core.h b/Frameworks/GME/gme/Vgm_Core.h index f2bb6c568..de8624e78 100644 --- a/Frameworks/GME/gme/Vgm_Core.h +++ b/Frameworks/GME/gme/Vgm_Core.h @@ -1,353 +1,58 @@ -// Sega VGM music file emulator core - -// Game_Music_Emu $vers -#ifndef VGM_CORE_H -#define VGM_CORE_H - -#include "Gme_Loader.h" -#include "Ymz280b_Emu.h" -#include "Ymf262_Emu.h" -#include "Ym2612_Emu.h" -#include "Ym2610b_Emu.h" -#include "Ym2608_Emu.h" -#include "Ym3812_Emu.h" -#include "Ym2413_Emu.h" -#include "Ym2151_Emu.h" -#include "C140_Emu.h" -#include "SegaPcm_Emu.h" -#include "Rf5C68_Emu.h" -#include "Rf5C164_Emu.h" -#include "Pwm_Emu.h" -#include "Okim6258_Emu.h" -#include "Okim6295_Emu.h" -#include "K051649_Emu.h" -#include "K053260_Emu.h" -#include "K054539_Emu.h" -#include "Qsound_Apu.h" -#include "Ym2203_Emu.h" -#include "Ay_Apu.h" -#include "Gb_Apu.h" -#include "Hes_Apu.h" -#include "Sms_Apu.h" -#include "Multi_Buffer.h" -#include "Chip_Resampler.h" - - template - class Chip_Emu : public Emu { - int last_time; - short* out; - enum { disabled_time = -1 }; - public: - Chip_Emu() { last_time = disabled_time; out = NULL; } - void enable( bool b = true ) { last_time = b ? 0 : disabled_time; } - bool enabled() const { return last_time != disabled_time; } - void begin_frame( short* buf ) { out = buf; last_time = 0; } - - int run_until( int time ) - { - int count = time - last_time; - if ( count > 0 ) - { - if ( last_time < 0 ) - return false; - last_time = time; - short* p = out; - out += count * Emu::out_chan_count; - Emu::run( count, p ); - } - return true; - } - }; - -class Vgm_Core : public Gme_Loader { -public: - - // VGM file header - struct header_t - { - enum { size_min = 0x40 }; - enum { size_151 = 0x80 }; - enum { size_max = 0xC0 }; - - char tag [4]; // 0x00 - byte data_size [4]; // 0x04 - byte version [4]; // 0x08 - byte psg_rate [4]; // 0x0C - byte ym2413_rate [4]; // 0x10 - byte gd3_offset [4]; // 0x14 - byte track_duration [4]; // 0x18 - byte loop_offset [4]; // 0x1C - byte loop_duration [4]; // 0x20 - byte frame_rate [4]; // 0x24 v1.01 V - byte noise_feedback [2]; // 0x28 v1.10 V - byte noise_width; // 0x2A - byte sn76489_flags; // 0x2B v1.51 < - byte ym2612_rate [4]; // 0x2C v1.10 V - byte ym2151_rate [4]; // 0x30 - byte data_offset [4]; // 0x34 v1.50 V - byte segapcm_rate [4]; // 0x38 v1.51 V - byte segapcm_reg [4]; // 0x3C - byte rf5c68_rate [4]; // 0x40 - byte ym2203_rate [4]; // 0x44 - byte ym2608_rate [4]; // 0x48 - byte ym2610_rate [4]; // 0x4C - byte ym3812_rate [4]; // 0x50 - byte ym3526_rate [4]; // 0x54 - byte y8950_rate [4]; // 0x58 - byte ymf262_rate [4]; // 0x5C - byte ymf278b_rate [4]; // 0x60 - byte ymf271_rate [4]; // 0x64 - byte ymz280b_rate [4]; // 0x68 - byte rf5c164_rate [4]; // 0x6C - byte pwm_rate [4]; // 0x70 - byte ay8910_rate [4]; // 0x74 - byte ay8910_type; // 0x78 - byte ay8910_flags; // 0x79 - byte ym2203_ay8910_flags;// 0x7A - byte ym2608_ay8910_flags;// 0x7B - byte volume_modifier; // 0x7C v1.60 V - byte reserved; // 0x7D - byte loop_base; // 0x7E - byte loop_modifier; // 0x7F v1.51 < - byte gbdmg_rate [4]; // 0x80 v1.61 V - byte nesapu_rate [4]; // 0x84 - byte multipcm_rate [4]; // 0x88 - byte upd7759_rate [4]; // 0x8C - byte okim6258_rate [4]; // 0x90 - byte okim6258_flags; // 0x94 - byte k054539_flags; // 0x95 - byte c140_type; // 0x96 - byte reserved_flags; // 0x97 - byte okim6295_rate [4]; // 0x98 - byte k051649_rate [4]; // 0x9C - byte k054539_rate [4]; // 0xA0 - byte huc6280_rate [4]; // 0xA4 - byte c140_rate [4]; // 0xA8 - byte k053260_rate [4]; // 0xAC - byte pokey_rate [4]; // 0xB0 - byte qsound_rate [4]; // 0xB4 - byte reserved2 [4]; // 0xB8 - byte extra_offset [4]; // 0xBC - - // True if header has valid file signature - bool valid_tag() const; - int size() const; - void cleanup(); - }; - - // Header for currently loaded file - header_t const& header() const { return _header; } - - // Raw file data, for parsing GD3 tags - byte const* file_begin() const { return Gme_Loader::file_begin(); } - byte const* file_end () const { return Gme_Loader::file_end(); } - - // If file uses FM, initializes FM sound emulator using *sample_rate. If - // *sample_rate is zero, sets *sample_rate to the proper accurate rate and - // uses that. The output of the FM sound emulator is resampled to the - // final sampling rate. - blargg_err_t init_chips( double* fm_rate, bool reinit = false ); - - // True if any FM chips are used by file. Always false until init_fm() - // is called. - bool uses_fm() const { return ym2612[0].enabled() || ym2413[0].enabled() || ym2151[0].enabled() || c140.enabled() || - segapcm.enabled() || rf5c68.enabled() || rf5c164.enabled() || pwm.enabled() || okim6258[0].enabled() || okim6295[0].enabled() || - k051649.enabled() || k053260.enabled() || k054539.enabled() || ym2203[0].enabled() || ym3812[0].enabled() || ymf262[0].enabled() || - ymz280b.enabled() || ym2610[0].enabled() || ym2608[0].enabled() || qsound[0].enabled() || - (header().ay8910_rate[0] | header().ay8910_rate[1] | header().ay8910_rate[2] | header().ay8910_rate[3]) || - (header().huc6280_rate[0] | header().huc6280_rate[1] | header().huc6280_rate[2] | header().huc6280_rate[3]) || - (header().gbdmg_rate[0] | header().gbdmg_rate[1] | header().gbdmg_rate[2] | header().gbdmg_rate[3]); } - - // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. - // Loading a file resets tempo to 1.0. - void set_tempo( double ); - - void set_sample_rate( int r ) { sample_rate = r; } - - // Starts track - void start_track(); - - // Runs PSG-only VGM for msec and returns number of clocks it ran for - blip_time_t run_psg( int msec ); - - // Plays FM for at most count samples into *out, and returns number of - // samples actually generated (always even). Also runs PSG for blip_time. - int play_frame( blip_time_t blip_time, int count, blip_sample_t out [] ); - - // True if all of file data has been played - bool track_ended() const { return pos >= file_end(); } - - // 0 for PSG and YM2612 DAC, 1 for AY, 2 for HuC6280, 3 for GB DMG - Stereo_Buffer stereo_buf[4]; - - // PCM sound is always generated here - Blip_Buffer * blip_buf[2]; - - // PSG sound chips, for assigning to Blip_Buffer, and setting volume and EQ - Sms_Apu psg[2]; - Ay_Apu ay[2]; - Hes_Apu huc6280[2]; - Gb_Apu gbdmg[2]; - - // PCM synth, for setting volume and EQ - Blip_Synth_Fast pcm; - - // FM sound chips - Chip_Resampler_Emu ymf262[2]; - Chip_Resampler_Emu ym3812[2]; - Chip_Resampler_Emu ym2612[2]; - Chip_Resampler_Emu ym2610[2]; - Chip_Resampler_Emu ym2608[2]; - Chip_Resampler_Emu ym2413[2]; - Chip_Resampler_Emu ym2151[2]; - Chip_Resampler_Emu ym2203[2]; - - // PCM sound chips - Chip_Resampler_Emu c140; - Chip_Resampler_Emu segapcm; - Chip_Resampler_Emu rf5c68; - Chip_Resampler_Emu rf5c164; - Chip_Resampler_Emu pwm; - Chip_Resampler_Emu okim6258[2]; int okim6258_hz[2]; - Chip_Resampler_Emu okim6295[2]; int okim6295_hz; - Chip_Resampler_Emu k051649; - Chip_Resampler_Emu k053260; - Chip_Resampler_Emu k054539; - Chip_Resampler_Emu ymz280b; int ymz280b_hz; - Chip_Resampler_Emu qsound[2]; - - // DAC control - typedef struct daccontrol_data - { - bool Enable; - byte Bank; - } DACCTRL_DATA; - - byte DacCtrlUsed; - byte DacCtrlUsg[0xFF]; - DACCTRL_DATA DacCtrl[0xFF]; - byte DacCtrlMap[0xFF]; - int DacCtrlTime[0xFF]; - void ** dac_control; - - void dac_control_grow(byte chip_id); - - int dac_control_recursion; - - int run_dac_control( int time ); - -public: - void chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data); - -// Implementation -public: - Vgm_Core(); - ~Vgm_Core(); - -protected: - virtual blargg_err_t load_mem_( byte const [], int ); - -private: - // blip_time_t // PSG clocks - typedef int vgm_time_t; // 44100 per second, REGARDLESS of sample rate - typedef int fm_time_t; // FM sample count - - int sample_rate; - int vgm_rate; // rate of log, 44100 normally, adjusted by tempo - double fm_rate; // FM samples per second - - header_t _header; - - // VGM to FM time - int fm_time_factor; - int fm_time_offset; - fm_time_t to_fm_time( vgm_time_t ) const; - - // VGM to PSG time - int blip_time_factor; - blip_time_t to_psg_time( vgm_time_t ) const; - - int blip_ay_time_factor; - int ay_time_offset; - blip_time_t to_ay_time( vgm_time_t ) const; - - int blip_huc6280_time_factor; - int huc6280_time_offset; - blip_time_t to_huc6280_time( vgm_time_t ) const; - - int blip_gbdmg_time_factor; - int gbdmg_time_offset; - blip_time_t to_gbdmg_time( vgm_time_t ) const; - - // Current time and position in log - vgm_time_t vgm_time; - byte const* pos; - byte const* loop_begin; - bool has_looped; - - // PCM - enum { PCM_BANK_COUNT = 0x40 }; - typedef struct _vgm_pcm_bank_data - { - unsigned DataSize; - byte* Data; - unsigned DataStart; - } VGM_PCM_DATA; - typedef struct _vgm_pcm_bank - { - unsigned BankCount; - VGM_PCM_DATA* Bank; - unsigned DataSize; - byte* Data; - unsigned DataPos; - unsigned BnkPos; - } VGM_PCM_BANK; - - typedef struct pcmbank_table - { - byte ComprType; - byte CmpSubType; - byte BitDec; - byte BitCmp; - unsigned EntryCount; - void* Entries; - } PCMBANK_TBL; - - VGM_PCM_BANK PCMBank[PCM_BANK_COUNT]; - PCMBANK_TBL PCMTbl; - - void ReadPCMTable(unsigned DataSize, const byte* Data); - void AddPCMData(byte Type, unsigned DataSize, const byte* Data); - bool DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data); - const byte* GetPointerFromPCMBank(byte Type, unsigned DataPos); - - byte const* pcm_pos; // current position in PCM data - int dac_amp[2]; - int dac_disabled[2]; // -1 if disabled - void write_pcm( vgm_time_t, int chip, int amp ); - - blip_time_t run( vgm_time_t ); - int run_ym2151( int chip, int time ); - int run_ym2203( int chip, int time ); - int run_ym2413( int chip, int time ); - int run_ym2612( int chip, int time ); - int run_ym3812( int chip, int time ); - int run_ymf262( int chip, int time ); - int run_ym2610( int chip, int time ); - int run_ym2608( int chip, int time ); - int run_ymz280b( int time ); - int run_c140( int time ); - int run_segapcm( int time ); - int run_rf5c68( int time ); - int run_rf5c164( int time ); - int run_pwm( int time ); - int run_okim6258( int chip, int time ); - int run_okim6295( int chip, int time ); - int run_k051649( int time ); - int run_k053260( int time ); - int run_k054539( int time ); - int run_qsound( int chip, int time ); - void update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const; -}; - -#endif +// Sega VGM music file emulator core + +// Game_Music_Emu $vers +#ifndef VGM_CORE_H +#define VGM_CORE_H + +#include "Gme_Loader.h" + +#include "../vgmplay/VGMPlay.h" + +class Vgm_Core : public Gme_Loader { +public: + typedef VGM_HEADER header_t; + + // VGM file header + // Header for currently loaded file + header_t const& header() const { return _header; } + + // Raw file data, for parsing GD3 tags + byte const* file_begin() const { return Gme_Loader::file_begin(); } + byte const* file_end () const { return Gme_Loader::file_end(); } + + // Adjusts music tempo, where 1.0 is normal. Can be changed while playing. + // Loading a file resets tempo to 1.0. + void set_tempo( double ); + + void set_sample_rate( int r ) { sample_rate = r; } + + // Starts track + void start_track(); + + // Plays FM for at most count samples into *out, and returns number of + // samples actually generated (always even). + int play_( int count, short out [] ); + + // True if all of file data has been played + bool track_ended() const { return !!vgmp->VGMEnd; } + + // Skips the specified number of samples + void skip_( int count ); + +// Implementation +public: + Vgm_Core(); + ~Vgm_Core(); + +protected: + virtual blargg_err_t load_mem_( byte const [], int ); + +private: + int sample_rate; + + header_t _header; + + VGM_PLAYER* vgmp; +}; + +#endif diff --git a/Frameworks/GME/gme/Vgm_Emu.cpp b/Frameworks/GME/gme/Vgm_Emu.cpp index 14c7364c4..9d9869d23 100644 --- a/Frameworks/GME/gme/Vgm_Emu.cpp +++ b/Frameworks/GME/gme/Vgm_Emu.cpp @@ -1,551 +1,533 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Vgm_Emu.h" - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// FM emulators are internally quieter to avoid 16-bit overflow -double const fm_gain = 3.0; -double const rolloff = 0.990; -double const oversample_factor = 1.5; - -Vgm_Emu::Vgm_Emu() -{ - resampler.set_callback( play_frame_, this ); - disable_oversampling_ = false; - muted_voices = 0; - set_type( gme_vgm_type ); - set_max_initial_silence( 1 ); - set_silence_lookahead( 1 ); // tracks should already be trimmed - - static equalizer_t const eq = { -14.0, 80 , 0,0,0,0,0,0,0,0 }; - set_equalizer( eq ); -} - -Vgm_Emu::~Vgm_Emu() { } - -void Vgm_Emu::unload() -{ - core.unload(); - Classic_Emu::unload(); -} - -// Track info - -static byte const* skip_gd3_str( byte const in [], byte const* end ) -{ - while ( end - in >= 2 ) - { - in += 2; - if ( !(in [-2] | in [-1]) ) - break; - } - return in; -} - -static byte const* get_gd3_str( byte const* in, byte const* end, char field [] ) -{ - byte const* mid = skip_gd3_str( in, end ); - int len = (mid - in) / 2 - 1; - if ( len > 0 ) - { - len = min( len, (int) Gme_File::max_field_ ); - field [len] = 0; - for ( int i = 0; i < len; i++ ) - field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8 - } - return mid; -} - -static byte const* get_gd3_pair( byte const* in, byte const* end, char field [] ) -{ - return skip_gd3_str( get_gd3_str( in, end, field ), end ); -} - -static void parse_gd3( byte const in [], byte const* end, track_info_t* out ) -{ - in = get_gd3_pair( in, end, out->song ); - in = get_gd3_pair( in, end, out->game ); - in = get_gd3_pair( in, end, out->system ); - in = get_gd3_pair( in, end, out->author ); - in = get_gd3_str ( in, end, out->copyright ); - in = get_gd3_pair( in, end, out->dumper ); - in = get_gd3_str ( in, end, out->comment ); -} - -int const gd3_header_size = 12; - -static int check_gd3_header( byte const h [], int remain ) -{ - if ( remain < gd3_header_size ) return 0; - if ( memcmp( h, "Gd3 ", 4 ) ) return 0; - if ( get_le32( h + 4 ) >= 0x200 ) return 0; - - int gd3_size = get_le32( h + 8 ); - if ( gd3_size > remain - gd3_header_size ) return 0; - - return gd3_size; -} - -static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) -{ - int length = get_le32( h.track_duration ) * 10 / 441; // 1000 / 44100 - if ( length > 0 ) - { - int loop = get_le32( h.loop_duration ); - if ( loop > 0 && get_le32( h.loop_offset ) ) - { - out->loop_length = loop * 10 / 441; - out->intro_length = length - out->loop_length; - check( out->loop_length <= length ); - // TODO: Also set out->length? We now have play_length for suggested play time. - } - else - { - out->length = length; - out->intro_length = length; - out->loop_length = 0; - } - } -} - -blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const -{ - get_vgm_length( header(), out ); - - int gd3_offset = get_le32( header().gd3_offset ); - if ( gd3_offset <= 0 ) - return blargg_ok; - - byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); - int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); - if ( gd3_size ) - { - byte const* gd3_data = gd3 + gd3_header_size; - parse_gd3( gd3_data, gd3_data + gd3_size, out ); - } - - return blargg_ok; -} - -blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size ) -{ - *data = 0; - *size = 0; - - int gd3_offset = get_le32( header().gd3_offset ); - if ( gd3_offset <= 0 ) - return blargg_ok; - - byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); - int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); - if ( gd3_size ) - { - *data = gd3; - *size = gd3_size + gd3_header_size; - } - - return blargg_ok; -} - -static void hash_vgm_file( Vgm_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) -{ - out.hash_( &h.data_size[0], sizeof(h.data_size) ); - out.hash_( &h.version[0], sizeof(h.version) ); - out.hash_( &h.psg_rate[0], sizeof(h.psg_rate) ); - out.hash_( &h.ym2413_rate[0], sizeof(h.ym2413_rate) ); - out.hash_( &h.track_duration[0], sizeof(h.track_duration) ); - out.hash_( &h.loop_offset[0], sizeof(h.loop_offset) ); - out.hash_( &h.loop_duration[0], sizeof(h.loop_duration) ); - out.hash_( &h.frame_rate[0], sizeof(h.frame_rate) ); - out.hash_( &h.noise_feedback[0], sizeof(h.noise_feedback) ); - out.hash_( &h.noise_width, sizeof(h.noise_width) ); - out.hash_( &h.sn76489_flags, sizeof(h.sn76489_flags) ); - out.hash_( &h.ym2612_rate[0], sizeof(h.ym2612_rate) ); - out.hash_( &h.ym2151_rate[0], sizeof(h.ym2151_rate) ); - out.hash_( &h.data_offset[0], sizeof(h.data_offset) ); - out.hash_( &h.segapcm_rate[0], sizeof(h.segapcm_rate) ); - out.hash_( &h.segapcm_reg[0], sizeof(h.segapcm_reg) ); - out.hash_( &h.rf5c68_rate[0], sizeof(h.rf5c68_rate) ); - out.hash_( &h.ym2203_rate[0], sizeof(h.ym2203_rate) ); - out.hash_( &h.ym2608_rate[0], sizeof(h.ym2608_rate) ); - out.hash_( &h.ym2610_rate[0], sizeof(h.ym2610_rate) ); - out.hash_( &h.ym3812_rate[0], sizeof(h.ym3812_rate) ); - out.hash_( &h.ym3526_rate[0], sizeof(h.ym3526_rate) ); - out.hash_( &h.y8950_rate[0], sizeof(h.y8950_rate) ); - out.hash_( &h.ymf262_rate[0], sizeof(h.ymf262_rate) ); - out.hash_( &h.ymf278b_rate[0], sizeof(h.ymf278b_rate) ); - out.hash_( &h.ymf271_rate[0], sizeof(h.ymf271_rate) ); - out.hash_( &h.ymz280b_rate[0], sizeof(h.ymz280b_rate) ); - out.hash_( &h.rf5c164_rate[0], sizeof(h.rf5c164_rate) ); - out.hash_( &h.pwm_rate[0], sizeof(h.pwm_rate) ); - out.hash_( &h.ay8910_rate[0], sizeof(h.ay8910_rate) ); - out.hash_( &h.ay8910_type, sizeof(h.ay8910_type) ); - out.hash_( &h.ay8910_flags, sizeof(h.ay8910_flags) ); - out.hash_( &h.ym2203_ay8910_flags, sizeof(h.ym2203_ay8910_flags) ); - out.hash_( &h.ym2608_ay8910_flags, sizeof(h.ym2608_ay8910_flags) ); - out.hash_( &h.reserved, sizeof(h.reserved) ); - out.hash_( &h.gbdmg_rate[0], sizeof(h.gbdmg_rate) ); - out.hash_( &h.nesapu_rate[0], sizeof(h.nesapu_rate) ); - out.hash_( &h.multipcm_rate[0], sizeof(h.multipcm_rate) ); - out.hash_( &h.upd7759_rate[0], sizeof(h.upd7759_rate) ); - out.hash_( &h.okim6258_rate[0], sizeof(h.okim6258_rate) ); - out.hash_( &h.okim6258_flags, sizeof(h.okim6258_flags) ); - out.hash_( &h.k054539_flags, sizeof(h.k054539_flags) ); - out.hash_( &h.c140_type, sizeof(h.c140_type) ); - out.hash_( &h.reserved_flags, sizeof(h.reserved_flags) ); - out.hash_( &h.okim6295_rate[0], sizeof(h.okim6295_rate) ); - out.hash_( &h.k051649_rate[0], sizeof(h.k051649_rate) ); - out.hash_( &h.k054539_rate[0], sizeof(h.k054539_rate) ); - out.hash_( &h.huc6280_rate[0], sizeof(h.huc6280_rate) ); - out.hash_( &h.c140_rate[0], sizeof(h.c140_rate) ); - out.hash_( &h.k053260_rate[0], sizeof(h.k053260_rate) ); - out.hash_( &h.pokey_rate[0], sizeof(h.pokey_rate) ); - out.hash_( &h.qsound_rate[0], sizeof(h.qsound_rate) ); - out.hash_( &h.reserved2[0], sizeof(h.reserved2) ); - out.hash_( &h.extra_offset[0], sizeof(h.extra_offset) ); - out.hash_( data, data_size ); -} - -struct Vgm_File : Gme_Info_ -{ - Vgm_Emu::header_t h; - blargg_vector data; - blargg_vector gd3; - - Vgm_File() { set_type( gme_vgm_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - int file_size = in.remain(); - if ( file_size <= h.size_min ) - return blargg_err_file_type; - - RETURN_ERR( in.read( &h, h.size_min ) ); - if ( !h.valid_tag() ) - return blargg_err_file_type; - - if ( h.size() > h.size_min ) - RETURN_ERR( in.read( &h.rf5c68_rate, h.size() - h.size_min ) ); - - h.cleanup(); - - int data_offset = get_le32( h.data_offset ) + offsetof( Vgm_Core::header_t, data_offset ); - int data_size = file_size - offsetof( Vgm_Core::header_t, data_offset ) - data_offset; - int gd3_offset = get_le32( h.gd3_offset ); - if ( gd3_offset > 0 ) - gd3_offset += offsetof( Vgm_Core::header_t, gd3_offset ); - - int amount_to_skip = gd3_offset - h.size(); - - if ( gd3_offset > 0 && gd3_offset > data_offset ) - { - data_size = gd3_offset - data_offset; - amount_to_skip = 0; - - RETURN_ERR( data.resize( data_size ) ); - RETURN_ERR( in.skip( data_offset - h.size() ) ); - RETURN_ERR( in.read( data.begin(), data_size ) ); - } - - int remain = file_size - gd3_offset; - byte gd3_h [gd3_header_size]; - if ( gd3_offset > 0 && remain >= gd3_header_size ) - { - RETURN_ERR( in.skip( amount_to_skip ) ); - RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); - int gd3_size = check_gd3_header( gd3_h, remain ); - if ( gd3_size ) - { - RETURN_ERR( gd3.resize( gd3_size ) ); - RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); - } - - if ( data_offset > gd3_offset ) - { - RETURN_ERR( data.resize( data_size ) ); - RETURN_ERR( in.skip( data_offset - gd3_offset - sizeof gd3_h - gd3.size() ) ); - RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); - } - } - - return blargg_ok; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - get_vgm_length( h, out ); - if ( gd3.size() ) - parse_gd3( gd3.begin(), gd3.end(), out ); - return blargg_ok; - } - - blargg_err_t hash_( Hash_Function& out ) const - { - hash_vgm_file( h, data.begin(), data.end() - data.begin(), out ); - return blargg_ok; - } -}; - -static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } -static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } - -gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }}; - -gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }}; - -// Setup - -void Vgm_Emu::set_tempo_( double t ) -{ - core.set_tempo( t ); -} - -blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate ) -{ - RETURN_ERR( core.stereo_buf[0].set_sample_rate( sample_rate, 1000 / 30 ) ); - RETURN_ERR( core.stereo_buf[1].set_sample_rate( sample_rate, 1000 / 30 ) ); - RETURN_ERR( core.stereo_buf[2].set_sample_rate( sample_rate, 1000 / 30 ) ); - RETURN_ERR( core.stereo_buf[3].set_sample_rate( sample_rate, 1000 / 30 ) ); - core.set_sample_rate(sample_rate); - return Classic_Emu::set_sample_rate_( sample_rate ); -} - -void Vgm_Emu::update_eq( blip_eq_t const& eq ) -{ - core.psg[0].treble_eq( eq ); - core.psg[1].treble_eq( eq ); - core.ay[0].treble_eq( eq ); - core.ay[1].treble_eq( eq ); - core.huc6280[0].treble_eq( eq ); - core.huc6280[1].treble_eq( eq ); - core.gbdmg[0].treble_eq( eq ); - core.gbdmg[1].treble_eq( eq ); - core.pcm.treble_eq( eq ); -} - -void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - if ( i < core.psg[0].osc_count ) - { - core.psg[0].set_output( i, c, l, r ); - core.psg[1].set_output( i, c, l, r ); - } -} - -void Vgm_Emu::mute_voices_( int mask ) -{ - muted_voices = mask; - - Classic_Emu::mute_voices_( mask ); - - // TODO: what was this for? - //core.pcm.output( &core.blip_buf ); - - // TODO: silence PCM if FM isn't used? - if ( core.uses_fm() ) - { - core.psg[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); - core.psg[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); - core.ay[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); - core.ay[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); - for ( unsigned i = 0, j = 1; i < core.huc6280[0].osc_count; i++, j <<= 1) - { - Blip_Buffer * center = ( mask & j ) ? 0 : core.stereo_buf[2].center(); - Blip_Buffer * left = ( mask & j ) ? 0 : core.stereo_buf[2].left(); - Blip_Buffer * right = ( mask & j ) ? 0 : core.stereo_buf[2].right(); - core.huc6280[0].set_output( i, center, left, right ); - core.huc6280[1].set_output( i, center, left, right ); - } - for (unsigned i = 0, j = 1; i < core.gbdmg[0].osc_count; i++, j <<= 1) - { - Blip_Buffer * center = (mask & j) ? 0 : core.stereo_buf[3].center(); - Blip_Buffer * left = (mask & j) ? 0 : core.stereo_buf[3].left(); - Blip_Buffer * right = (mask & j) ? 0 : core.stereo_buf[3].right(); - core.gbdmg[0].set_output(i, center, left, right); - core.gbdmg[1].set_output(i, center, left, right); - } - if (core.ym2612[0].enabled()) - { - core.pcm.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); - core.ym2612[0].mute_voices( mask ); - if ( core.ym2612[1].enabled() ) - core.ym2612[1].mute_voices( mask ); - } - - if ( core.ym2413[0].enabled() ) - { - int m = mask & 0x3F; - if ( mask & 0x20 ) - m |= 0x01E0; // channels 5-8 - if ( mask & 0x40 ) - m |= 0x3E00; - core.ym2413[0].mute_voices( m ); - if ( core.ym2413[1].enabled() ) - core.ym2413[1].mute_voices( m ); - } - - if ( core.ym2151[0].enabled() ) - { - core.ym2151[0].mute_voices( mask ); - if ( core.ym2151[1].enabled() ) - core.ym2151[1].mute_voices( mask ); - } - - if ( core.c140.enabled() ) - { - int m = 0; - int m_add = 7; - for ( unsigned i = 0; i < 8; i++, m_add <<= 3 ) - { - if ( mask & ( 1 << i ) ) m += m_add; - } - core.c140.mute_voices( m ); - } - - if ( core.rf5c68.enabled() ) - { - core.rf5c68.mute_voices( mask ); - } - - if ( core.rf5c164.enabled() ) - { - core.rf5c164.mute_voices( mask ); - } - } -} - -blargg_err_t Vgm_Emu::load_mem_( byte const data [], int size ) -{ - RETURN_ERR( core.load_mem( data, size ) ); - - set_voice_count( core.psg[0].osc_count ); - - double fm_rate = 0.0; - if ( !disable_oversampling_ ) - fm_rate = sample_rate() * oversample_factor; - RETURN_ERR( core.init_chips( &fm_rate ) ); - - double psg_gain = ( ( core.header().psg_rate[3] & 0xC0 ) == 0x40 ) ? 0.5 : 1.0; - - if ( core.uses_fm() ) - { - set_voice_count( 8 ); - RETURN_ERR( resampler.setup( fm_rate / sample_rate(), rolloff, gain() ) ); - RETURN_ERR( resampler.reset( core.stereo_buf[0].length() * sample_rate() / 1000 ) ); - core.psg[0].volume( 0.135 * fm_gain * psg_gain * gain() ); - core.psg[1].volume( 0.135 * fm_gain * psg_gain * gain() ); - core.ay[0].volume( 0.135 * fm_gain * gain() ); - core.ay[1].volume( 0.135 * fm_gain * gain() ); - core.huc6280[0].volume( 0.135 * fm_gain * gain() ); - core.huc6280[1].volume( 0.135 * fm_gain * gain() ); - core.gbdmg[0].volume( 0.135 * fm_gain * gain() ); - core.gbdmg[1].volume( 0.135 * fm_gain * gain() ); - } - else - { - core.psg[0].volume( psg_gain * gain() ); - core.psg[1].volume( psg_gain * gain() ); - } - - static const char* const fm_names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; - set_voice_names( core.uses_fm() ? fm_names : psg_names ); - - static int const types [8] = { - wave_type+1, wave_type+2, wave_type+3, noise_type+1, - 0, 0, 0, 0 - }; - set_voice_types( types ); - - return Classic_Emu::setup_buffer( core.stereo_buf[0].center()->clock_rate() ); -} - -// Emulation - -blargg_err_t Vgm_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - core.start_track(); - - mute_voices_(muted_voices); - - if ( core.uses_fm() ) - resampler.clear(); - - return blargg_ok; -} - -inline void Vgm_Emu::check_end() -{ - if ( core.track_ended() ) - set_track_ended(); -} - -inline void Vgm_Emu::check_warning() -{ - const char* w = core.warning(); - if ( w ) - set_warning( w ); -} - -blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) -{ - check_end(); - time_io = core.run_psg( msec ); - check_warning(); - return blargg_ok; -} - -inline int Vgm_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ) -{ - check_end(); - int result = core.play_frame( blip_time, sample_count, buf ); - check_warning(); - return result; -} - -int Vgm_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) -{ - return STATIC_CAST(Vgm_Emu*,p)->play_frame( a, b, c ); -} - -blargg_err_t Vgm_Emu::play_( int count, sample_t out [] ) -{ - if ( !core.uses_fm() ) - return Classic_Emu::play_( count, out ); - - Stereo_Buffer * secondaries[] = { &core.stereo_buf[1], &core.stereo_buf[2], &core.stereo_buf[3] }; - resampler.dual_play( count, out, core.stereo_buf[0], secondaries, 3 ); - return blargg_ok; -} - -blargg_err_t Vgm_Emu::hash_( Hash_Function& out ) const -{ - byte const* p = file_begin() + header().size(); - byte const* e = file_end(); - int data_offset = get_le32( header().data_offset ); - if ( data_offset ) - p += data_offset + offsetof( header_t, data_offset ) - header().size(); - int gd3_offset = get_le32( header().gd3_offset ); - if ( gd3_offset > 0 && gd3_offset + offsetof( header_t, gd3_offset ) > data_offset + offsetof( header_t, data_offset ) ) - e = file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); - hash_vgm_file( header(), p, e - p, out ); - return blargg_ok; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Vgm_Emu.h" + +#include "blargg_endian.h" +#include "blargg_common.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// FM emulators are internally quieter to avoid 16-bit overflow +double const fm_gain = 3.0; +double const rolloff = 0.990; +double const oversample_factor = 1.5; + +Vgm_Emu::Vgm_Emu() +{ + muted_voices = 0; + set_type( gme_vgm_type ); + set_max_initial_silence( 1 ); + set_silence_lookahead( 1 ); // tracks should already be trimmed +} + +Vgm_Emu::~Vgm_Emu() { } + +void Vgm_Emu::unload() +{ + core.unload(); +} + +// Track info + +static byte const* skip_gd3_str( byte const in [], byte const* end ) +{ + while ( end - in >= 2 ) + { + in += 2; + if ( !(in [-2] | in [-1]) ) + break; + } + return in; +} + +static byte const* get_gd3_str( byte const* in, byte const* end, char field [] ) +{ + byte const* mid = skip_gd3_str( in, end ); + int len = (int)((mid - in) / 2) - 1; + if ( len > 0 ) + { + char * in_utf8 = blargg_to_utf8( (blargg_wchar_t *) in ); + len = min( len, (int) Gme_File::max_field_ ); + field [len] = 0; + for ( int i = 0; i < len; i++ ) + field [i] = in_utf8 [i]; + free(in_utf8); + } + return mid; +} + +static byte const* get_gd3_pair( byte const* in, byte const* end, char field [], char field_j [] ) +{ + return get_gd3_str( get_gd3_str( in, end, field ), end, field_j ); +} + +static void parse_gd3( byte const in [], byte const* end, track_info_t* out, track_info_t* out_j ) +{ + in = get_gd3_pair( in, end, out->song , out_j->song ); + in = get_gd3_pair( in, end, out->game , out_j->game ); + in = get_gd3_pair( in, end, out->system , out_j->system ); + in = get_gd3_pair( in, end, out->author , out_j->author ); + in = get_gd3_str ( in, end, out->copyright ); + in = get_gd3_pair( in, end, out->dumper , out_j->dumper ); + in = get_gd3_str ( in, end, out->comment ); +} + +static blargg_err_t write_gd3_str( gme_writer_t writer, void* your_data, const char field [] ) +{ + blargg_wchar_t * wstring = blargg_to_wide( field ); + if (!wstring) + return "Out of memory"; + blargg_err_t err = writer( your_data, wstring, blargg_wcslen( wstring ) * 2 + 2 ); + free( wstring ); + return err; +} + +static blargg_err_t write_gd3_pair( gme_writer_t writer, void* your_data, const char field [], const char field_j [] ) +{ + RETURN_ERR(write_gd3_str( writer, your_data, field )); + RETURN_ERR(write_gd3_str( writer, your_data, field )); + return blargg_ok; +} + +static blargg_err_t write_gd3_strings( gme_writer_t writer, void* your_data, const track_info_t* in, const track_info_t* in_j ) +{ + RETURN_ERR(write_gd3_pair( writer, your_data, in->song , in_j->song )); + RETURN_ERR(write_gd3_pair( writer, your_data, in->game , in_j->game )); + RETURN_ERR(write_gd3_pair( writer, your_data, in->system , in_j->system )); + RETURN_ERR(write_gd3_pair( writer, your_data, in->author , in_j->author )); + RETURN_ERR(write_gd3_str ( writer, your_data, in->copyright )); + RETURN_ERR(write_gd3_pair( writer, your_data, in->dumper , in_j->dumper )); + RETURN_ERR(write_gd3_str ( writer, your_data, in->comment )); + return blargg_ok; +} + +static gme_err_t writer_calc_size(void* param, const void* ptr, long count) +{ + *(long *)param += count; + return blargg_ok; +} + +static blargg_err_t write_gd3( gme_writer_t writer, void* your_data, const track_info_t* in, const track_info_t* in_j ) +{ + long string_size = 0; + byte version[4]; + RETURN_ERR(writer( your_data, "Gd3 ", 4 )); + set_le32(version, 0x100); + RETURN_ERR(writer( your_data, version, 4 )); + write_gd3_strings( &writer_calc_size, &string_size, in, in_j ); + if ( string_size > 1000000000 ) + return "GD3 tag too large"; + set_le32(version, (int)string_size); + RETURN_ERR(writer( your_data, version, 4)); + return write_gd3_strings( writer, your_data, in, in_j ); +} + +int const gd3_header_size = 12; + +static int check_gd3_header( byte const h [], int remain ) +{ + if ( remain < gd3_header_size ) return 0; + if ( memcmp( h, "Gd3 ", 4 ) ) return 0; + if ( get_le32( h + 4 ) >= 0x200 ) return 0; + + int gd3_size = get_le32( h + 8 ); + if ( gd3_size > remain - gd3_header_size ) return 0; + + return gd3_size; +} + +static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) +{ + int length = get_le32( &h.lngTotalSamples ) * 10 / 441; // 1000 / 44100 + if ( length > 0 ) + { + int loop = get_le32( &h.lngLoopSamples ); + if ( loop > 0 && get_le32( &h.lngLoopOffset ) ) + { + out->loop_length = loop * 10 / 441; + out->intro_length = length - out->loop_length; + check( out->loop_length <= length ); + // TODO: Also set out->length? We now have play_length for suggested play time. + } + else + { + out->length = length; + out->intro_length = length; + out->loop_length = 0; + } + } +} + +blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const +{ + + return blargg_ok; +} + +blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size ) +{ + *data = 0; + *size = 0; + + int gd3_offset = header().lngGD3Offset; + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset; + int gd3_size = check_gd3_header( gd3, (int)(core.file_end() - gd3) ); + if ( gd3_size ) + { + *data = gd3; + *size = gd3_size + gd3_header_size; + } + + return blargg_ok; +} + +static void hash_vgm_file( Vgm_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( (const byte *) &h.lngEOFOffset, sizeof(h.lngEOFOffset) ); + out.hash_( (const byte *) &h.lngVersion, sizeof(h.lngVersion) ); + out.hash_( (const byte *) &h.lngHzPSG, sizeof(h.lngHzPSG) ); + out.hash_( (const byte *) &h.lngHzYM2413, sizeof(h.lngHzYM2413) ); + out.hash_( (const byte *) &h.lngTotalSamples, sizeof(h.lngTotalSamples) ); + out.hash_( (const byte *) &h.lngLoopOffset, sizeof(h.lngLoopOffset) ); + out.hash_( (const byte *) &h.lngLoopSamples, sizeof(h.lngLoopSamples) ); + out.hash_( (const byte *) &h.lngRate, sizeof(h.lngRate) ); + out.hash_( (const byte *) &h.shtPSG_Feedback, sizeof(h.shtPSG_Feedback) ); + out.hash_( (const byte *) &h.bytPSG_SRWidth, sizeof(h.bytPSG_SRWidth) ); + out.hash_( (const byte *) &h.bytPSG_Flags, sizeof(h.bytPSG_Flags) ); + out.hash_( (const byte *) &h.lngHzYM2612, sizeof(h.lngHzYM2612) ); + out.hash_( (const byte *) &h.lngHzYM2151, sizeof(h.lngHzYM2151) ); + out.hash_( (const byte *) &h.lngDataOffset, sizeof(h.lngDataOffset) ); + out.hash_( (const byte *) &h.lngHzSPCM, sizeof(h.lngHzSPCM) ); + out.hash_( (const byte *) &h.lngSPCMIntf, sizeof(h.lngSPCMIntf) ); + out.hash_( (const byte *) &h.lngHzRF5C68, sizeof(h.lngHzRF5C68) ); + out.hash_( (const byte *) &h.lngHzYM2203, sizeof(h.lngHzYM2203) ); + out.hash_( (const byte *) &h.lngHzYM2608, sizeof(h.lngHzYM2608) ); + out.hash_( (const byte *) &h.lngHzYM2610, sizeof(h.lngHzYM2610) ); + out.hash_( (const byte *) &h.lngHzYM3812, sizeof(h.lngHzYM3812) ); + out.hash_( (const byte *) &h.lngHzYM3526, sizeof(h.lngHzYM3526) ); + out.hash_( (const byte *) &h.lngHzY8950, sizeof(h.lngHzY8950) ); + out.hash_( (const byte *) &h.lngHzYMF262, sizeof(h.lngHzYMF262) ); + out.hash_( (const byte *) &h.lngHzYMF278B, sizeof(h.lngHzYMF278B) ); + out.hash_( (const byte *) &h.lngHzYMF271, sizeof(h.lngHzYMF271) ); + out.hash_( (const byte *) &h.lngHzYMZ280B, sizeof(h.lngHzYMZ280B) ); + out.hash_( (const byte *) &h.lngHzRF5C164, sizeof(h.lngHzRF5C164) ); + out.hash_( (const byte *) &h.lngHzPWM, sizeof(h.lngHzPWM) ); + out.hash_( (const byte *) &h.lngHzAY8910, sizeof(h.lngHzAY8910) ); + out.hash_( (const byte *) &h.bytAYType, sizeof(h.bytAYType) ); + out.hash_( (const byte *) &h.bytAYFlag, sizeof(h.bytAYFlag) ); + out.hash_( (const byte *) &h.bytAYFlagYM2203, sizeof(h.bytAYFlagYM2203) ); + out.hash_( (const byte *) &h.bytAYFlagYM2608, sizeof(h.bytAYFlagYM2608) ); + out.hash_( (const byte *) &h.bytReserved2, sizeof(h.bytReserved2) ); + out.hash_( (const byte *) &h.lngHzGBDMG, sizeof(h.lngHzGBDMG) ); + out.hash_( (const byte *) &h.lngHzNESAPU, sizeof(h.lngHzNESAPU) ); + out.hash_( (const byte *) &h.lngHzMultiPCM, sizeof(h.lngHzMultiPCM) ); + out.hash_( (const byte *) &h.lngHzUPD7759, sizeof(h.lngHzUPD7759) ); + out.hash_( (const byte *) &h.lngHzOKIM6258, sizeof(h.lngHzOKIM6258) ); + out.hash_( (const byte *) &h.bytOKI6258Flags, sizeof(h.bytOKI6258Flags) ); + out.hash_( (const byte *) &h.bytK054539Flags, sizeof(h.bytK054539Flags) ); + out.hash_( (const byte *) &h.bytC140Type, sizeof(h.bytC140Type) ); + out.hash_( (const byte *) &h.bytReservedFlags, sizeof(h.bytReservedFlags) ); + out.hash_( (const byte *) &h.lngHzOKIM6295, sizeof(h.lngHzOKIM6295) ); + out.hash_( (const byte *) &h.lngHzK051649, sizeof(h.lngHzK051649) ); + out.hash_( (const byte *) &h.lngHzK054539, sizeof(h.lngHzK054539) ); + out.hash_( (const byte *) &h.lngHzHuC6280, sizeof(h.lngHzHuC6280) ); + out.hash_( (const byte *) &h.lngHzC140, sizeof(h.lngHzC140) ); + out.hash_( (const byte *) &h.lngHzK053260, sizeof(h.lngHzK053260) ); + out.hash_( (const byte *) &h.lngHzPokey, sizeof(h.lngHzPokey) ); + out.hash_( (const byte *) &h.lngHzQSound, sizeof(h.lngHzQSound) ); + out.hash_( (const byte *) &h.lngHzSCSP, sizeof(h.lngHzSCSP) ); + // out.hash_( (const byte *) &h.lngExtraOffset, sizeof(h.lngExtraOffset) ); + out.hash_( (const byte *) &h.lngHzWSwan, sizeof(h.lngHzWSwan) ); + out.hash_( (const byte *) &h.lngHzVSU, sizeof(h.lngHzVSU) ); + out.hash_( (const byte *) &h.lngHzSAA1099, sizeof(h.lngHzSAA1099) ); + out.hash_( (const byte *) &h.lngHzES5503, sizeof(h.lngHzES5503) ); + out.hash_( (const byte *) &h.lngHzES5506, sizeof(h.lngHzES5506) ); + out.hash_( (const byte *) &h.bytES5503Chns, sizeof(h.bytES5503Chns) ); + out.hash_( (const byte *) &h.bytES5506Chns, sizeof(h.bytES5506Chns) ); + out.hash_( (const byte *) &h.bytC352ClkDiv, sizeof(h.bytC352ClkDiv) ); + out.hash_( (const byte *) &h.bytESReserved, sizeof(h.bytESReserved) ); + out.hash_( (const byte *) &h.lngHzX1_010, sizeof(h.lngHzX1_010) ); + out.hash_( (const byte *) &h.lngHzC352, sizeof(h.lngHzC352) ); + out.hash_( (const byte *) &h.lngHzGA20, sizeof(h.lngHzGA20) ); + out.hash_( data, data_size ); +} + +struct VGM_FILE_mem +{ + VGM_FILE vf; + const BOOST::uint8_t* buffer; + UINT32 ptr; + UINT32 size; +}; + +static int VGMF_mem_Read(VGM_FILE* f, void* out, UINT32 count) +{ + VGM_FILE_mem* mf = (VGM_FILE_mem *) f; + if (count + mf->ptr > mf->size) + count = mf->size - mf->ptr; + memcpy(out, mf->buffer + mf->ptr, count); + mf->ptr += count; + return count; +} + +static int VGMF_mem_Seek(VGM_FILE* f, UINT32 offset) +{ + VGM_FILE_mem* mf = (VGM_FILE_mem *) f; + if (offset > mf->size) + offset = mf->size; + mf->ptr = offset; + return 0; +} + +static UINT32 VGMF_mem_GetSize(VGM_FILE* f) +{ + VGM_FILE_mem* mf = (VGM_FILE_mem *) f; + return mf->size; +} + +struct Vgm_File : Gme_Info_ +{ + Vgm_Emu::header_t h; + blargg_vector original_header; + blargg_vector data; + blargg_vector gd3; + + track_info_t metadata; + track_info_t metadata_j; + + Vgm_File() { set_type( gme_vgm_type ); } + + blargg_err_t load_mem_( const byte* in, int file_size ) + { + VGM_FILE_mem memFile; + + memFile.vf.Read = &VGMF_mem_Read; + memFile.vf.Seek = &VGMF_mem_Seek; + memFile.vf.GetSize = &VGMF_mem_GetSize; + memFile.buffer = in; + memFile.ptr = 0; + memFile.size = file_size; + + if (!GetVGMFileInfo_Handle((VGM_FILE *) &memFile, &h, 0)) + return blargg_err_file_type; + + int data_offset = get_le32( &h.lngDataOffset ); + int data_size = file_size - data_offset; + int gd3_offset = get_le32( &h.lngGD3Offset ); + + if ( gd3_offset > 0 && gd3_offset > data_offset ) + { + data_size = gd3_offset - data_offset; + + RETURN_ERR( data.resize( data_size ) ); + memcpy( data.begin(), in + data_offset, data_size ); + } + + int remain = file_size - gd3_offset; + byte gd3_h [gd3_header_size]; + if ( gd3_offset > 0 && remain >= gd3_header_size ) + { + memcpy( gd3_h, in + gd3_offset, sizeof gd3_h ); + int gd3_size = check_gd3_header( gd3_h, remain ); + if ( gd3_size ) + { + RETURN_ERR( gd3.resize( gd3_size ) ); + memcpy( gd3.begin(), in + sizeof gd3_h + gd3_offset, gd3.size() ); + } + + if ( data_offset > gd3_offset ) + { + RETURN_ERR( data.resize( data_size ) ); + memcpy( data.begin(), in + data_offset, data_size ); + } + } + + int header_size = data_offset; + if ( gd3_offset && data_offset > gd3_offset ) + header_size = gd3_offset; + RETURN_ERR( original_header.resize( header_size ) ); + memcpy( original_header.begin(), in, header_size ); + + memset( &metadata, 0, sizeof(metadata) ); + memset( &metadata_j, 0, sizeof(metadata_j) ); + get_vgm_length( h, &metadata ); + if ( gd3.size() ) + parse_gd3( gd3.begin(), gd3.end(), &metadata, &metadata_j ); + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + *out = metadata; + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_vgm_file( h, data.begin(), (int)(data.end() - data.begin()), out ); + return blargg_ok; + } + + blargg_err_t set_track_info_( const track_info_t* in, int ) + { + metadata = *in; + + return blargg_ok; + } + + blargg_err_t save_( gme_writer_t writer, void* your_data ) const + { + byte buffer[4]; + int data_size = (int)(data.end() - data.begin()); + int gd3_offset = (int)(original_header.end() - original_header.begin()) + data_size; + + RETURN_ERR( writer( your_data, original_header.begin(), 0x14 ) ); + set_le32(buffer, gd3_offset - 0x14); + RETURN_ERR( writer( your_data, buffer, 4 ) ); + RETURN_ERR( writer( your_data, original_header.begin() + 0x18, original_header.end() - original_header.begin() - 0x18 ) ); + RETURN_ERR( writer( your_data, data.begin(), data_size ) ); + + return write_gd3( writer, your_data, &metadata, &metadata_j ); + } +}; + +static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } +static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } + +gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 0 }}; + +gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 0 }}; + +// Setup + +void Vgm_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate ) +{ + core.set_sample_rate(sample_rate); + return blargg_ok; +} + +void Vgm_Emu::mute_voices_( int mask ) +{ + muted_voices = mask; +} + +// Emulation + +blargg_err_t Vgm_Emu::start_track_( int track ) +{ + core.start_track(); + + mute_voices_(muted_voices); + + return blargg_ok; +} + +inline void Vgm_Emu::check_end() +{ + if ( core.track_ended() ) + set_track_ended(); +} + +blargg_err_t Vgm_Emu::play_( int count, sample_t out [] ) +{ + core.play_(count, out); + check_end(); + return blargg_ok; +} + +blargg_err_t Vgm_Emu::hash_( Hash_Function& out ) const +{ + byte const* p = file_begin(); + byte const* e = file_end(); + int data_offset = header().lngDataOffset; + if ( data_offset ) + p += data_offset; + int gd3_offset = header().lngGD3Offset; + if ( gd3_offset > 0 && gd3_offset > data_offset ) + e = file_begin() + gd3_offset; + hash_vgm_file( header(), p, (int)(e - p), out ); + return blargg_ok; +} + +blargg_err_t Vgm_Emu::load_mem_( const byte* in, int file_size ) +{ + RETURN_ERR( core.load_mem(in, file_size) ); + + get_vgm_length( header(), &metadata ); + + int data_offset = header().lngDataOffset; + int gd3_offset = header().lngGD3Offset; + int data_size = file_size - data_offset; + + if (gd3_offset > 0) + { + if (gd3_offset > data_offset) + data_size = gd3_offset - data_offset; + byte const* gd3 = core.file_begin() + gd3_offset; + int gd3_size = check_gd3_header( gd3, (int)(core.file_end() - gd3) ); + if ( gd3_size ) + { + byte const* gd3_data = gd3 + gd3_header_size; + parse_gd3( gd3_data, gd3_data + gd3_size, &metadata, &metadata_j ); + } + } + + int header_size = data_offset; + if ( gd3_offset && data_offset > gd3_offset ) + header_size = gd3_offset; + RETURN_ERR( original_header.resize( header_size ) ); + memcpy( original_header.begin(), in, header_size ); + + RETURN_ERR( data.resize(data_size) ); + memcpy( data.begin(), in + data_offset, data_size ); + + return blargg_ok; +} + +blargg_err_t Vgm_Emu::skip_( int count ) +{ + core.skip_(count); + return blargg_ok; +} + +blargg_err_t Vgm_Emu::set_track_info_( const track_info_t* in, int ) +{ + metadata = *in; + + return blargg_ok; +} + +blargg_err_t Vgm_Emu::save_(gme_writer_t writer, void* your_data) +{ + byte buffer[4]; + int data_size = (int)(data.end() - data.begin()); + int gd3_offset = (int)(original_header.end() - original_header.begin()) + data_size; + + RETURN_ERR( writer( your_data, original_header.begin(), 0x14 ) ); + set_le32(buffer, gd3_offset - 0x14); + RETURN_ERR( writer( your_data, buffer, 4 ) ); + RETURN_ERR( writer( your_data, original_header.begin() + 0x18, original_header.end() - original_header.begin() - 0x18 ) ); + RETURN_ERR( writer( your_data, data.begin(), data_size ) ); + + return write_gd3( writer, your_data, &metadata, &metadata_j ); +} diff --git a/Frameworks/GME/gme/Vgm_Emu.h b/Frameworks/GME/gme/Vgm_Emu.h index 3aaddf851..777fce533 100644 --- a/Frameworks/GME/gme/Vgm_Emu.h +++ b/Frameworks/GME/gme/Vgm_Emu.h @@ -1,69 +1,61 @@ -// Sega VGM music file emulator - -// Game_Music_Emu $vers -#ifndef VGM_EMU_H -#define VGM_EMU_H - -#include "Classic_Emu.h" -#include "Dual_Resampler.h" -#include "Vgm_Core.h" - -/* Emulates VGM music using SN76489/SN76496 PSG, and YM2612 and YM2413 FM sound chips. -Supports custom sound buffer and frequency equalization when VGM uses just the PSG. FM -sound chips can be run at their proper rates, or slightly higher to reduce aliasing on -high notes. A YM2413 is supported but not provided separately from the library. */ -class Vgm_Emu : public Classic_Emu { -public: - - // True if custom buffer and custom equalization are supported - // TODO: move into Music_Emu and rename to something like supports_custom_buffer() - bool is_classic_emu() const { return !core.uses_fm(); } - - // Disables running FM chips at higher than normal rate. Will result in slightly - // more aliasing of high notes. - void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } - - // VGM file header (see Vgm_Core.h) - typedef Vgm_Core::header_t header_t; - - // Header for currently loaded file - header_t const& header() const { return core.header(); } - - blargg_err_t hash_( Hash_Function& ) const; - - // Gd3 tag for currently loaded file - blargg_err_t gd3_data( const unsigned char ** data, int * size ); - - static gme_type_t static_type() { return gme_vgm_type; } - -// Implementation -public: - Vgm_Emu(); - ~Vgm_Emu(); - -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const [], int ); - blargg_err_t set_sample_rate_( int sample_rate ); - blargg_err_t start_track_( int ); - blargg_err_t play_( int count, sample_t []); - blargg_err_t run_clocks( blip_time_t&, int ); - virtual void set_tempo_( double ); - virtual void mute_voices_( int mask ); - virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - virtual void update_eq( blip_eq_t const& ); - virtual void unload(); - -private: - bool disable_oversampling_; - unsigned muted_voices; - Dual_Resampler resampler; - Vgm_Core core; - - void check_end(); - void check_warning(); - int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ); - static int play_frame_( void*, blip_time_t, int, sample_t [] ); -}; - -#endif +// Sega VGM music file emulator + +// Game_Music_Emu $vers +#ifndef VGM_EMU_H +#define VGM_EMU_H + +#include "Classic_Emu.h" +#include "Dual_Resampler.h" +#include "Vgm_Core.h" + +/* Emulates VGM music using SN76489/SN76496 PSG, and YM2612 and YM2413 FM sound chips. +Supports custom sound buffer and frequency equalization when VGM uses just the PSG. FM +sound chips can be run at their proper rates, or slightly higher to reduce aliasing on +high notes. A YM2413 is supported but not provided separately from the library. */ +class Vgm_Emu : public Music_Emu { +public: + + // VGM file header (see Vgm_Core.h) + typedef Vgm_Core::header_t header_t; + + // Header for currently loaded file + header_t const& header() const { return core.header(); } + + blargg_err_t hash_( Hash_Function& ) const; + + // Gd3 tag for currently loaded file + blargg_err_t gd3_data( const unsigned char ** data, int * size ); + + static gme_type_t static_type() { return gme_vgm_type; } + +// Implementation +public: + Vgm_Emu(); + ~Vgm_Emu(); + +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t set_track_info_( const track_info_t*, int track ); + blargg_err_t load_mem_( byte const [], int ); + blargg_err_t set_sample_rate_( int sample_rate ); + blargg_err_t start_track_( int ); + blargg_err_t play_( int count, sample_t []); + blargg_err_t skip_( int count ); + blargg_err_t save_( gme_writer_t, void* ); + virtual void set_tempo_( double ); + virtual void mute_voices_( int mask ); + virtual void unload(); + +private: + unsigned muted_voices; + Vgm_Core core; + + blargg_vector original_header; + blargg_vector data; + track_info_t metadata; + track_info_t metadata_j; + + void check_end(); +}; + +#endif diff --git a/Frameworks/GME/gme/Ym2151_Emu.cpp b/Frameworks/GME/gme/Ym2151_Emu.cpp deleted file mode 100644 index b7703e7f4..000000000 --- a/Frameworks/GME/gme/Ym2151_Emu.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ym2151_Emu.h" -#include "ym2151.h" - -Ym2151_Emu::Ym2151_Emu() { PSG = 0; } - -Ym2151_Emu::~Ym2151_Emu() -{ - if ( PSG ) ym2151_shutdown( PSG ); -} - -int Ym2151_Emu::set_rate( double sample_rate, double clock_rate ) -{ - if ( PSG ) - { - ym2151_shutdown( PSG ); - PSG = 0; - } - - PSG = ym2151_init( clock_rate, sample_rate ); - if ( !PSG ) - return 1; - - reset(); - return 0; -} - -void Ym2151_Emu::reset() -{ - ym2151_reset_chip( PSG ); - ym2151_set_mask( PSG, 0 ); -} - -static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; - -void Ym2151_Emu::write( int addr, int data ) -{ - ym2151_update_one( PSG, DUMMYBUF, 0 ); - ym2151_write_reg( PSG, addr, data ); -} - -void Ym2151_Emu::mute_voices( int mask ) -{ - ym2151_set_mask( PSG, mask ); -} - -void Ym2151_Emu::run( int pair_count, sample_t* out ) -{ - SAMP bufL[ 1024 ]; - SAMP bufR[ 1024 ]; - SAMP * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - ym2151_update_one( PSG, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Ym2151_Emu.h b/Frameworks/GME/gme/Ym2151_Emu.h deleted file mode 100644 index a55c6f894..000000000 --- a/Frameworks/GME/gme/Ym2151_Emu.h +++ /dev/null @@ -1,33 +0,0 @@ -// YM2151 FM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YM2151_EMU_H -#define YM2151_EMU_H - -class Ym2151_Emu { - void* PSG; -public: - Ym2151_Emu(); - ~Ym2151_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( double sample_rate, double clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 8 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Ym2203_Emu.cpp b/Frameworks/GME/gme/Ym2203_Emu.cpp deleted file mode 100644 index a8aad8291..000000000 --- a/Frameworks/GME/gme/Ym2203_Emu.cpp +++ /dev/null @@ -1,155 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ym2203_Emu.h" -#include "fm.h" -#include - -static void psg_set_clock(void *param, int clock) -{ - Ym2203_Emu *info = (Ym2203_Emu *)param; - info->psg_set_clock( clock ); -} - -static void psg_write(void *param, int address, int data) -{ - Ym2203_Emu *info = (Ym2203_Emu *)param; - info->psg_write( address, data ); -} - -static int psg_read(void *param) -{ - Ym2203_Emu *info = (Ym2203_Emu *)param; - return info->psg_read(); -} - -static void psg_reset(void *param) -{ - Ym2203_Emu *info = (Ym2203_Emu *)param; - info->psg_reset(); -} - -static const ssg_callbacks psgintf = -{ - psg_set_clock, - psg_write, - psg_read, - psg_reset -}; - -Ym2203_Emu::Ym2203_Emu() { opn = 0; psg.set_type( Ay_Apu::Ym2203 ); } - -Ym2203_Emu::~Ym2203_Emu() -{ - if ( opn ) ym2203_shutdown( opn ); -} - -int Ym2203_Emu::set_rate( int sample_rate, int clock_rate ) -{ - if ( opn ) - { - ym2203_shutdown( opn ); - opn = 0; - } - - opn = ym2203_init( this, clock_rate, sample_rate, &psgintf ); - if ( !opn ) - return 1; - - this->sample_rate = sample_rate; - psg_clock = clock_rate * 2; - - buffer.set_sample_rate( sample_rate ); - buffer.clock_rate( psg_clock ); - - psg.volume( 1.0 ); - - reset(); - return 0; -} - -void Ym2203_Emu::reset() -{ - psg.reset(); - ym2203_reset_chip( opn ); - mute_voices( 0 ); -} - -static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; - -void Ym2203_Emu::write( int addr, int data ) -{ - ym2203_update_one( opn, DUMMYBUF, 0 ); - ym2203_write( opn, 0, addr ); - ym2203_write( opn, 1, data ); -} - -void Ym2203_Emu::mute_voices( int mask ) -{ - ym2203_set_mutemask( opn, mask ); - for ( unsigned i = 0, j = 1 << 3; i < 3; i++, j <<= 1) - { - Blip_Buffer * buf = ( mask & j ) ? NULL : &buffer; - psg.set_output( i, buf ); - } -} - -void Ym2203_Emu::run( int pair_count, sample_t* out ) -{ - blip_sample_t buf[ 1024 ]; - FMSAMPLE bufL[ 1024 ]; - FMSAMPLE bufR[ 1024 ]; - FMSAMPLE * buffers[2] = { bufL, bufR }; - - blip_time_t psg_end_time = pair_count * psg_clock / sample_rate; - psg.end_frame( psg_end_time ); - buffer.end_frame( psg_end_time ); - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - ym2203_update_one( opn, buffers, todo ); - - int sample_count = buffer.read_samples( buf, todo ); - memset( buf + sample_count, 0, ( todo - sample_count ) * sizeof( blip_sample_t ) ); - - for (int i = 0; i < todo; i++) - { - int output_l, output_r; - int output = buf [i]; - output_l = output + bufL [i]; - output_r = output + bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} - -void Ym2203_Emu::psg_set_clock( int clock ) -{ - psg_clock = clock * 2; - buffer.clock_rate( psg_clock ); -} - -void Ym2203_Emu::psg_write( int addr, int data ) -{ - if ( !(addr & 1) ) psg.write_addr( data ); - else psg.write_data( 0, data ); -} - -int Ym2203_Emu::psg_read() -{ - return psg.read(); -} - -void Ym2203_Emu::psg_reset() -{ - psg.reset(); -} diff --git a/Frameworks/GME/gme/Ym2203_Emu.h b/Frameworks/GME/gme/Ym2203_Emu.h deleted file mode 100644 index 248a70e58..000000000 --- a/Frameworks/GME/gme/Ym2203_Emu.h +++ /dev/null @@ -1,45 +0,0 @@ -// YM2203 FM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YM2203_EMU_H -#define YM2203_EMU_H - -#include "Ay_Apu.h" - -class Ym2203_Emu { - void* opn; - Ay_Apu psg; - Blip_Buffer buffer; - unsigned sample_rate; - unsigned psg_clock; -public: - Ym2203_Emu(); - ~Ym2203_Emu(); - - // Sets output chip clock rate, in Hz. Returns non-zero - // if error. - int set_rate( int sample_rate, int clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 6 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); - - // SSG interface - inline void psg_set_clock( int clock ); - inline void psg_write( int addr, int data ); - inline int psg_read(); - inline void psg_reset(); -}; - -#endif diff --git a/Frameworks/GME/gme/Ym2413_Emu.cpp b/Frameworks/GME/gme/Ym2413_Emu.cpp index bd4349e01..5ce059e5f 100644 --- a/Frameworks/GME/gme/Ym2413_Emu.cpp +++ b/Frameworks/GME/gme/Ym2413_Emu.cpp @@ -1,26 +1,31 @@ // Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Ym2413_Emu.h" -#include "ym2413.h" + +extern "C" { +#include "../vgmplay/chips/emu2413.h" +} Ym2413_Emu::Ym2413_Emu() { opll = 0; } Ym2413_Emu::~Ym2413_Emu() { - if ( opll ) ym2413_shutdown( opll ); + if ( opll ) OPLL_delete( (OPLL *) opll ); } int Ym2413_Emu::set_rate( int sample_rate, int clock_rate ) { if ( opll ) { - ym2413_shutdown( opll ); + OPLL_delete( (OPLL *) opll ); opll = 0; } - opll = ym2413_init( clock_rate, sample_rate, 0 ); + opll = OPLL_new( clock_rate, sample_rate ); if ( !opll ) return 1; + + OPLL_SetChipMode( (OPLL *) opll, 0 ); reset(); return 0; @@ -28,35 +33,32 @@ int Ym2413_Emu::set_rate( int sample_rate, int clock_rate ) void Ym2413_Emu::reset() { - ym2413_reset_chip( opll ); - ym2413_set_mask( opll, 0 ); + OPLL_reset( (OPLL *) opll ); + OPLL_SetMuteMask( (OPLL *) opll, 0 ); } -static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; - void Ym2413_Emu::write( int addr, int data ) { - ym2413_update_one( opll, DUMMYBUF, 0 ); - ym2413_write( opll, 0, addr ); - ym2413_write( opll, 1, data ); + OPLL_writeIO( (OPLL *) opll, 0, addr ); + OPLL_writeIO( (OPLL *) opll, 1, data ); } void Ym2413_Emu::mute_voices( int mask ) { - ym2413_set_mask( opll, mask ); + OPLL_SetMuteMask( (OPLL *) opll, mask ); } void Ym2413_Emu::run( int pair_count, sample_t* out ) { - SAMP bufMO[ 1024 ]; - SAMP bufRO[ 1024 ]; - SAMP * buffers[2] = { bufMO, bufRO }; + e_int32 bufMO[ 1024 ]; + e_int32 bufRO[ 1024 ]; + e_int32 * buffers[2] = { bufMO, bufRO }; while (pair_count > 0) { int todo = pair_count; if (todo > 1024) todo = 1024; - ym2413_update_one( opll, buffers, todo ); + OPLL_calc_stereo( (OPLL *) opll, buffers, todo, -1 ); for (int i = 0; i < todo; i++) { diff --git a/Frameworks/GME/gme/Ym2413_Emu.h b/Frameworks/GME/gme/Ym2413_Emu.h index 59cb61bf8..9df24eea1 100644 --- a/Frameworks/GME/gme/Ym2413_Emu.h +++ b/Frameworks/GME/gme/Ym2413_Emu.h @@ -4,8 +4,6 @@ #ifndef YM2413_EMU_H #define YM2413_EMU_H -struct OPLL; - class Ym2413_Emu { void* opll; public: diff --git a/Frameworks/GME/gme/Ym2608_Emu.cpp b/Frameworks/GME/gme/Ym2608_Emu.cpp deleted file mode 100644 index fd0680dab..000000000 --- a/Frameworks/GME/gme/Ym2608_Emu.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ym2608_Emu.h" -#include "fm.h" -#include - -static void psg_set_clock(void *param, int clock) -{ - Ym2608_Emu *info = (Ym2608_Emu *)param; - info->psg_set_clock( clock ); -} - -static void psg_write(void *param, int address, int data) -{ - Ym2608_Emu *info = (Ym2608_Emu *)param; - info->psg_write( address, data ); -} - -static int psg_read(void *param) -{ - Ym2608_Emu *info = (Ym2608_Emu *)param; - return info->psg_read(); -} - -static void psg_reset(void *param) -{ - Ym2608_Emu *info = (Ym2608_Emu *)param; - info->psg_reset(); -} - -static const ssg_callbacks psgintf = -{ - psg_set_clock, - psg_write, - psg_read, - psg_reset -}; - -Ym2608_Emu::Ym2608_Emu() { opn = 0; psg.set_type( Ay_Apu::Ym2608 ); } - -Ym2608_Emu::~Ym2608_Emu() -{ - if ( opn ) ym2608_shutdown( opn ); -} - -int Ym2608_Emu::set_rate( int sample_rate, int clock_rate ) -{ - if ( opn ) - { - ym2608_shutdown( opn ); - opn = 0; - } - - opn = ym2608_init( this, clock_rate, sample_rate, &psgintf ); - if ( !opn ) - return 1; - - this->sample_rate = sample_rate; - psg_clock = clock_rate * 2; - - buffer.set_sample_rate( sample_rate ); - buffer.clock_rate( psg_clock ); - - psg.volume( 1.0 ); - - reset(); - return 0; -} - -void Ym2608_Emu::reset() -{ - psg.reset(); - ym2608_reset_chip( opn ); - mute_voices( 0 ); -} - -static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; - -void Ym2608_Emu::write0( int addr, int data ) -{ - ym2608_update_one( opn, DUMMYBUF, 0 ); - ym2608_write( opn, 0, addr ); - ym2608_write( opn, 1, data ); -} - -void Ym2608_Emu::write1( int addr, int data ) -{ - ym2608_update_one( opn, DUMMYBUF, 0 ); - ym2608_write( opn, 2, addr ); - ym2608_write( opn, 3, data ); -} - -void Ym2608_Emu::write_rom( int rom_id, int size, int start, int length, void * data ) -{ - ym2608_write_pcmrom( opn, rom_id, size, start, length, (const UINT8 *) data ); -} - -void Ym2608_Emu::mute_voices( int mask ) -{ - ym2608_set_mutemask( opn, mask ); - for ( unsigned i = 0, j = 1 << 6; i < 3; i++, j <<= 1) - { - Blip_Buffer * buf = ( mask & j ) ? NULL : &buffer; - psg.set_output( i, buf ); - } -} - -void Ym2608_Emu::run( int pair_count, sample_t* out ) -{ - blip_sample_t buf[ 1024 ]; - FMSAMPLE bufL[ 1024 ]; - FMSAMPLE bufR[ 1024 ]; - FMSAMPLE * buffers[2] = { bufL, bufR }; - - blip_time_t psg_end_time = pair_count * psg_clock / sample_rate; - psg.end_frame( psg_end_time ); - buffer.end_frame( psg_end_time ); - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - ym2608_update_one( opn, buffers, todo ); - - int sample_count = buffer.read_samples( buf, todo ); - memset( buf + sample_count, 0, ( todo - sample_count ) * sizeof( blip_sample_t ) ); - - for (int i = 0; i < todo; i++) - { - int output_l, output_r; - int output = buf [i]; - output_l = output + bufL [i]; - output_r = output + bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} - -void Ym2608_Emu::psg_set_clock( int clock ) -{ - psg_clock = clock * 2; - buffer.clock_rate( psg_clock ); -} - -void Ym2608_Emu::psg_write( int addr, int data ) -{ - if ( !(addr & 1) ) psg.write_addr( data ); - else psg.write_data( 0, data ); -} - -int Ym2608_Emu::psg_read() -{ - return psg.read(); -} - -void Ym2608_Emu::psg_reset() -{ - psg.reset(); -} diff --git a/Frameworks/GME/gme/Ym2608_Emu.h b/Frameworks/GME/gme/Ym2608_Emu.h deleted file mode 100644 index 49a44ba4a..000000000 --- a/Frameworks/GME/gme/Ym2608_Emu.h +++ /dev/null @@ -1,49 +0,0 @@ -// YM2608 FM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YM2608_EMU_H -#define YM2608_EMU_H - -#include "Ay_Apu.h" - -class Ym2608_Emu { - void* opn; - Ay_Apu psg; - Blip_Buffer buffer; - unsigned sample_rate; - unsigned psg_clock; -public: - Ym2608_Emu(); - ~Ym2608_Emu(); - - // Sets output chip clock rate, in Hz. Returns non-zero - // if error. - int set_rate( int sample_rate, int clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 9 }; - void mute_voices( int mask ); - - // Writes data to addr - void write0( int addr, int data ); - void write1( int addr, int data ); - - // Sets ROM type, scales ROM size, then writes length bytes from data at start offset - void write_rom( int rom_id, int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); - - // SSG interface - inline void psg_set_clock( int clock ); - inline void psg_write( int addr, int data ); - inline int psg_read(); - inline void psg_reset(); -}; - -#endif diff --git a/Frameworks/GME/gme/Ym2610b_Emu.cpp b/Frameworks/GME/gme/Ym2610b_Emu.cpp deleted file mode 100644 index ff4b8c7ee..000000000 --- a/Frameworks/GME/gme/Ym2610b_Emu.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ym2610b_Emu.h" -#include "fm.h" -#include - -static void psg_set_clock(void *param, int clock) -{ - Ym2610b_Emu *info = (Ym2610b_Emu *)param; - info->psg_set_clock( clock ); -} - -static void psg_write(void *param, int address, int data) -{ - Ym2610b_Emu *info = (Ym2610b_Emu *)param; - info->psg_write( address, data ); -} - -static int psg_read(void *param) -{ - Ym2610b_Emu *info = (Ym2610b_Emu *)param; - return info->psg_read(); -} - -static void psg_reset(void *param) -{ - Ym2610b_Emu *info = (Ym2610b_Emu *)param; - info->psg_reset(); -} - -static const ssg_callbacks psgintf = -{ - psg_set_clock, - psg_write, - psg_read, - psg_reset -}; - -Ym2610b_Emu::Ym2610b_Emu() { opn = 0; } - -Ym2610b_Emu::~Ym2610b_Emu() -{ - if ( opn ) ym2610_shutdown( opn ); -} - -int Ym2610b_Emu::set_rate( int sample_rate, int clock_rate, bool is_2610b ) -{ - if ( opn ) - { - ym2610_shutdown( opn ); - opn = 0; - } - - psg.set_type( is_2610b ? Ay_Apu::Ym2610b : Ay_Apu::Ym2610 ); - - opn = ym2610_init( this, clock_rate, sample_rate, &psgintf ); - if ( !opn ) - return 1; - - this->sample_rate = sample_rate; - psg_clock = clock_rate * 2; - this->is_2610b = is_2610b; - - buffer.set_sample_rate( sample_rate ); - buffer.clock_rate( psg_clock ); - - psg.volume( 1.0 ); - - reset(); - return 0; -} - -void Ym2610b_Emu::reset() -{ - psg.reset(); - ym2610_reset_chip( opn ); - mute_voices( 0 ); -} - -static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; - -void Ym2610b_Emu::write0( int addr, int data ) -{ - if ( is_2610b ) ym2610b_update_one( opn, DUMMYBUF, 0 ); - else ym2610_update_one( opn, DUMMYBUF, 0 ); - ym2610_write( opn, 0, addr ); - ym2610_write( opn, 1, data ); -} - -void Ym2610b_Emu::write1( int addr, int data ) -{ - if ( is_2610b ) ym2610b_update_one( opn, DUMMYBUF, 0 ); - else ym2610_update_one( opn, DUMMYBUF, 0 ); - ym2610_write( opn, 2, addr ); - ym2610_write( opn, 3, data ); -} - -void Ym2610b_Emu::write_rom( int rom_id, int size, int start, int length, void * data ) -{ - ym2610_write_pcmrom( opn, rom_id, size, start, length, (const UINT8 *) data ); -} - -void Ym2610b_Emu::mute_voices( int mask ) -{ - ym2610_set_mutemask( opn, mask ); - for ( unsigned i = 0, j = 1 << 6; i < 3; i++, j <<= 1) - { - Blip_Buffer * buf = ( mask & j ) ? NULL : &buffer; - psg.set_output( i, buf ); - } -} - -void Ym2610b_Emu::run( int pair_count, sample_t* out ) -{ - blip_sample_t buf[ 1024 ]; - FMSAMPLE bufL[ 1024 ]; - FMSAMPLE bufR[ 1024 ]; - FMSAMPLE * buffers[2] = { bufL, bufR }; - - blip_time_t psg_end_time = pair_count * psg_clock / sample_rate; - psg.end_frame( psg_end_time ); - buffer.end_frame( psg_end_time ); - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - if ( is_2610b ) ym2610b_update_one( opn, buffers, todo ); - else ym2610_update_one( opn, buffers, todo ); - - int sample_count = buffer.read_samples( buf, todo ); - memset( buf + sample_count, 0, ( todo - sample_count ) * sizeof( blip_sample_t ) ); - - for (int i = 0; i < todo; i++) - { - int output_l, output_r; - int output = buf [i]; - output_l = output + bufL [i]; - output_r = output + bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} - -void Ym2610b_Emu::psg_set_clock( int clock ) -{ - psg_clock = clock * 2; - buffer.clock_rate( psg_clock ); -} - -void Ym2610b_Emu::psg_write( int addr, int data ) -{ - if ( !(addr & 1) ) psg.write_addr( data ); - else psg.write_data( 0, data ); -} - -int Ym2610b_Emu::psg_read() -{ - return psg.read(); -} - -void Ym2610b_Emu::psg_reset() -{ - psg.reset(); -} diff --git a/Frameworks/GME/gme/Ym2610b_Emu.h b/Frameworks/GME/gme/Ym2610b_Emu.h deleted file mode 100644 index 634ffb1ff..000000000 --- a/Frameworks/GME/gme/Ym2610b_Emu.h +++ /dev/null @@ -1,50 +0,0 @@ -// YM2610B FM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YM2610B_EMU_H -#define YM2610B_EMU_H - -#include "Ay_Apu.h" - -class Ym2610b_Emu { - void* opn; - Ay_Apu psg; - Blip_Buffer buffer; - unsigned sample_rate; - unsigned psg_clock; - bool is_2610b; -public: - Ym2610b_Emu(); - ~Ym2610b_Emu(); - - // Sets output chip clock rate, in Hz. Returns non-zero - // if error. - int set_rate( int sample_rate, int clock_rate, bool is_2610b ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 9 }; - void mute_voices( int mask ); - - // Writes data to addr - void write0( int addr, int data ); - void write1( int addr, int data ); - - // Sets ROM type, scales ROM size, then writes length bytes from data at start offset - void write_rom( int rom_id, int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); - - // SSG interface - inline void psg_set_clock( int clock ); - inline void psg_write( int addr, int data ); - inline int psg_read(); - inline void psg_reset(); -}; - -#endif diff --git a/Frameworks/GME/gme/Ym2612_Emu.cpp b/Frameworks/GME/gme/Ym2612_Emu.cpp index dd850e740..a3f8c47d1 100644 --- a/Frameworks/GME/gme/Ym2612_Emu.cpp +++ b/Frameworks/GME/gme/Ym2612_Emu.cpp @@ -1,7 +1,8 @@ -#include "Ym2612_Emu.h" - -#ifdef USE_GENS -#include "Ym2612_Emu_Gens.cpp" -#else -#include "Ym2612_Emu_MAME.cpp" -#endif +#include "Ym2612_Emu.h" + +#define YM2612_EMU_CPP +#ifdef USE_GENS +#include "Ym2612_Emu_Gens.cpp" +#else +#include "Ym2612_Emu_MAME.cpp" +#endif diff --git a/Frameworks/GME/gme/Ym2612_Emu.h b/Frameworks/GME/gme/Ym2612_Emu.h index d99359187..f1a8d5ad7 100644 --- a/Frameworks/GME/gme/Ym2612_Emu.h +++ b/Frameworks/GME/gme/Ym2612_Emu.h @@ -1,44 +1,44 @@ -// YM2612 FM sound chip emulator - -// Game_Music_Emu $vers -#ifndef YM2612_EMU_H -#define YM2612_EMU_H - -// Gens is buggy and inaccurate, but faster -// #define USE_GENS -#ifdef USE_GENS -struct Ym2612_Impl; -#else -typedef void Ym2612_Impl; -#endif - -class Ym2612_Emu { - Ym2612_Impl* impl; -public: - Ym2612_Emu() { impl = 0; } - ~Ym2612_Emu(); - - // Sets sample rate and chip clock rate, in Hz. Returns non-zero - // if error. If clock_rate=0, uses sample_rate*144 - const char* set_rate( double sample_rate, double clock_rate = 0 ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 6 }; - void mute_voices( int mask ); - - // Writes addr to register 0 then data to register 1 - void write0( int addr, int data ); - - // Writes addr to register 2 then data to register 3 - void write1( int addr, int data ); - - // Runs and adds pair_count*2 samples into current output buffer contents - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif +// YM2612 FM sound chip emulator + +// Game_Music_Emu $vers +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +// Gens is buggy and inaccurate, but faster +// #define USE_GENS +#ifdef USE_GENS +struct Ym2612_Impl; +#else +typedef void Ym2612_Impl; +#endif + +class Ym2612_Emu { + Ym2612_Impl* impl; +public: + Ym2612_Emu() { impl = 0; } + ~Ym2612_Emu(); + + // Sets sample rate and chip clock rate, in Hz. Returns non-zero + // if error. If clock_rate=0, uses sample_rate*144 + const char* set_rate( double sample_rate, double clock_rate = 0 ); + + // Resets to power-up state + void reset(); + + // Mutes voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Writes addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Writes addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Runs and adds pair_count*2 samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff --git a/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp b/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp index 514e7a3d3..9d7cb2bc0 100644 --- a/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp +++ b/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp @@ -1,1299 +1,1303 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -// Based on Gens 2.10 ym2612.c - -#include "Ym2612_Emu.h" - -#include -#include -#include -#include -#include -#include - -/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */ -/* Copyright (C) 2004-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -// This is mostly the original source in its C style and all. -// -// Somewhat optimized and simplified. Uses a template to generate the many -// variants of Update_Chan. Rewrote header file. In need of full rewrite by -// someone more familiar with FM sound and the YM2612. Has some inaccuracies -// compared to the Sega Genesis sound, particularly being mixed at such a -// high sample accuracy (the Genesis sounds like it has onn(ly 8 bit samples). -// - Shay - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -const int output_bits = 14; - -struct slot_t -{ - const int *DT; // parametre detune - int MUL; // parametre "multiple de frequence" - int TL; // Total Level = volume lorsque l'enveloppe est au plus haut - int TLL; // Total Level ajusted - int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression - int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe - int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer - // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite ! - int SEG; // Type enveloppe SSG - const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR [KSR]) - const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR [KSR]) - const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR [KSR]) - const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR [KSR]) - int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN [Finc >> 16]) - int Finc; // frequency step = pas d'incrementation du compteur-frequence - // plus le pas est grand, plus la frequence est aïgu (ou haute) - int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase - // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... - // en fonction de la valeur de cette variable, on va appeler une fonction permettant - // de mettre à jour l'enveloppe courante. - int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe - int Einc; // Envelope step courant - int Ecmp; // Envelope counter limite pour la prochaine phase - int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque - // cette valeur est egal à AR [KSR] - int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression - // cette valeur est egal à DR [KSR] - int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue - // cette valeur est egal à SR [KSR] - int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement - // cette valeur est egal à RR [KSR] - int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree - // d'un autre ou carrement à la sortie de la voie - int INd; // input data of the slot = donnees en entree du slot - int ChgEnM; // Change envelop mask. - int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO - int AMSon; // AMS enable flag = drapeau d'activation de l'AMS -}; - -struct channel_t -{ - int S0_OUT [4]; // anciennes sorties slot 0 (pour le feed back) - int LEFT; // LEFT enable flag - int RIGHT; // RIGHT enable flag - int ALGO; // Algorythm = determine les connections entre les operateurs - int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree) - int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO - int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO - int FNUM [4]; // hauteur frequence de la voie (+ 3 pour le mode special) - int FOCT [4]; // octave de la voie (+ 3 pour le mode special) - int KC [4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) - slot_t SLOT [4]; // four slot.operators = les 4 slots de la voie - int FFlag; // Frequency step recalculation flag -}; - -struct state_t -{ - int TimerBase; // TimerBase calculation - int Status; // YM2612 Status (timer overflow) - int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter - int TimerAL; - int TimerAcnt; // timerA counter = valeur courante du Timer A - int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter - int TimerBL; - int TimerBcnt; // timerB counter = valeur courante du Timer B - int Mode; // Mode actuel des voie 3 et 6 (normal / special) - int DAC; // DAC enabled flag - channel_t CHANNEL [Ym2612_Emu::channel_count]; // Les 6 voies du YM2612 - int REG [2] [0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif - // cela nous rend le debuggage plus facile -}; - -#undef PI -#define PI 3.14159265358979323846 - -#define ATTACK 0 -#define DECAY 1 -#define SUBSTAIN 2 -#define RELEASE 3 - -// SIN_LBITS <= 16 -// LFO_HBITS <= 16 -// (SIN_LBITS + SIN_HBITS) <= 26 -// (ENV_LBITS + ENV_HBITS) <= 28 -// (LFO_LBITS + LFO_HBITS) <= 28 - -#define SIN_HBITS 12 // Sinus phase counter int part -#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) - -#if (SIN_LBITS > 16) -#define SIN_LBITS 16 // Can't be greater than 16 bits -#endif - -#define ENV_HBITS 12 // Env phase counter int part -#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) - -#define LFO_HBITS 10 // LFO phase counter int part -#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) - -#define SIN_LENGHT (1 << SIN_HBITS) -#define ENV_LENGHT (1 << ENV_HBITS) -#define LFO_LENGHT (1 << LFO_HBITS) - -#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO - -#define SIN_MASK (SIN_LENGHT - 1) -#define ENV_MASK (ENV_LENGHT - 1) -#define LFO_MASK (LFO_LENGHT - 1) - -#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB - -#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) -#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) -#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) - -#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 -#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) - -#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) -//#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) - -#define AR_RATE 399128 -#define DR_RATE 5514396 - -//#define AR_RATE 426136 -//#define DR_RATE (AR_RATE * 12) - -#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) -#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) - -#define S0 0 // Stupid typo of the YM2612 -#define S1 2 -#define S2 1 -#define S3 3 - -struct tables_t -{ - short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE) - int LFOcnt; // LFO counter = compteur-frequence pour le LFO - int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO - // plus le pas est grand, plus la frequence est grande - unsigned int AR_TAB [128]; // Attack rate table - unsigned int DR_TAB [96]; // Decay rate table - unsigned int DT_TAB [8] [32]; // Detune table - unsigned int SL_TAB [16]; // Substain level table - unsigned int NULL_RATE [32]; // Table for NULL rate - int LFO_INC_TAB [8]; // LFO step table - - short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) - - short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) - short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE - int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) - unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase - unsigned int FINC_TAB [2048]; // Frequency step table -}; - -static const unsigned char DT_DEF_TAB [4 * 32] = -{ -// FD = 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -// FD = 1 - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, - -// FD = 2 - 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, - 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, - -// FD = 3 - 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, - 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 -}; - -static const unsigned char FKEY_TAB [16] = -{ - 0, 0, 0, 0, - 0, 0, 0, 1, - 2, 3, 3, 3, - 3, 3, 3, 3 -}; - -static const unsigned char LFO_AMS_TAB [4] = -{ - 31, 4, 1, 0 -}; - -static const unsigned char LFO_FMS_TAB [8] = -{ - LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, - LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, - LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, - LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 -}; - -inline void YM2612_Special_Update() { } - -struct Ym2612_Impl -{ - enum { channel_count = Ym2612_Emu::channel_count }; - - state_t YM2612; - int mute_mask; - tables_t g; - - void KEY_ON( channel_t&, int ); - void KEY_OFF( channel_t&, int ); - int SLOT_SET( int, int ); - int CHANNEL_SET( int, int ); - int YM_SET( int, int ); - - void set_rate( double sample_rate, double clock_factor ); - void reset(); - void write0( int addr, int data ); - void write1( int addr, int data ); - void run_timer( int ); - void run( int pair_count, Ym2612_Emu::sample_t [] ); -}; - -void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) -{ - slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot - - if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ? - { - SL->Fcnt = 0; - - // Fix Ecco 2 splash sound - - SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; - SL->ChgEnM = ~0; - -// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; -// SL->Ecnt = 0; - - SL->Einc = SL->EincA; - SL->Ecmp = ENV_DECAY; - SL->Ecurp = ATTACK; - } -} - - -void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) -{ - slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot - - if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ? - { - if (SL->Ecnt < ENV_DECAY) // attack phase ? - { - SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; - } - - SL->Einc = SL->EincR; - SL->Ecmp = ENV_END; - SL->Ecurp = RELEASE; - } -} - - -int Ym2612_Impl::SLOT_SET( int Adr, int data ) -{ - int nch = Adr & 3; - if ( nch == 3 ) - return 1; - - channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)]; - slot_t& sl = ch.SLOT [(Adr >> 2) & 3]; - - switch ( Adr & 0xF0 ) - { - case 0x30: - if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1; - else sl.MUL = 1; - - sl.DT = (int*) g.DT_TAB [(data >> 4) & 7]; - - ch.SLOT [0].Finc = -1; - - break; - - case 0x40: - sl.TL = data & 0x7F; - - // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... - YM2612_Special_Update(); - -#if ((ENV_HBITS - 7) < 0) - sl.TLL = sl.TL >> (7 - ENV_HBITS); -#else - sl.TLL = sl.TL << (ENV_HBITS - 7); -#endif - - break; - - case 0x50: - sl.KSR_S = 3 - (data >> 6); - - ch.SLOT [0].Finc = -1; - - if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1]; - else sl.AR = (int*) &g.NULL_RATE [0]; - - sl.EincA = sl.AR [sl.KSR]; - if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA; - break; - - case 0x60: - if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS; - else sl.AMS = 31; - - if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1]; - else sl.DR = (int*) &g.NULL_RATE [0]; - - sl.EincD = sl.DR [sl.KSR]; - if (sl.Ecurp == DECAY) sl.Einc = sl.EincD; - break; - - case 0x70: - if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1]; - else sl.SR = (int*) &g.NULL_RATE [0]; - - sl.EincS = sl.SR [sl.KSR]; - if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS; - break; - - case 0x80: - sl.SLL = g.SL_TAB [data >> 4]; - - sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2]; - - sl.EincR = sl.RR [sl.KSR]; - if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR; - break; - - case 0x90: - // SSG-EG envelope shapes : - /* - E At Al H - - 1 0 0 0 \\\\ - 1 0 0 1 \___ - 1 0 1 0 \/\/ - 1 0 1 1 \ - 1 1 0 0 //// - 1 1 0 1 / - 1 1 1 0 /\/\ - 1 1 1 1 /___ - - E = SSG-EG enable - At = Start negate - Al = Altern - H = Hold */ - - if(data & 0x08) sl.SEG = data & 0x0F; - else sl.SEG = 0; - break; - } - - return 0; -} - - -int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) -{ - int num = Adr & 3; - if ( num == 3 ) - return 1; - - channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)]; - - switch ( Adr & 0xFC ) - { - case 0xA0: - YM2612_Special_Update(); - - ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data; - ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; - - ch.SLOT [0].Finc = -1; - break; - - case 0xA4: - YM2612_Special_Update(); - - ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8); - ch.FOCT [0] = (data & 0x38) >> 3; - ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; - - ch.SLOT [0].Finc = -1; - break; - - case 0xA8: - if ( Adr < 0x100 ) - { - num++; - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data; - YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | - FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; - } - break; - - case 0xAC: - if ( Adr < 0x100 ) - { - num++; - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8); - YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3; - YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | - FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; - } - break; - - case 0xB0: - if ( ch.ALGO != (data & 7) ) - { - // Fix VectorMan 2 heli sound (level 1) - YM2612_Special_Update(); - - ch.ALGO = data & 7; - - ch.SLOT [0].ChgEnM = 0; - ch.SLOT [1].ChgEnM = 0; - ch.SLOT [2].ChgEnM = 0; - ch.SLOT [3].ChgEnM = 0; - } - - ch.FB = 9 - ((data >> 3) & 7); // Real thing ? - -// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... -// else ch.FB = 31; - break; - - case 0xB4: { - YM2612_Special_Update(); - - ch.LEFT = 0 - ((data >> 7) & 1); - ch.RIGHT = 0 - ((data >> 6) & 1); - - ch.AMS = LFO_AMS_TAB [(data >> 4) & 3]; - ch.FMS = LFO_FMS_TAB [data & 7]; - - for ( int i = 0; i < 4; i++ ) - { - slot_t& sl = ch.SLOT [i]; - sl.AMS = (sl.AMSon ? ch.AMS : 31); - } - break; - } - } - - return 0; -} - - -int Ym2612_Impl::YM_SET(int Adr, int data) -{ - switch ( Adr ) - { - case 0x22: - if (data & 8) // LFO enable - { - // Cool Spot music 1, LFO modified severals time which - // distord the sound, have to check that on a real genesis... - - g.LFOinc = g.LFO_INC_TAB [data & 7]; - } - else - { - g.LFOinc = g.LFOcnt = 0; - } - break; - - case 0x24: - YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2); - - if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) - { - YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; - } - break; - - case 0x25: - YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3); - - if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) - { - YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; - } - break; - - case 0x26: - YM2612.TimerB = data; - - if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12)) - { - YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12); - } - break; - - case 0x27: - // Parametre divers - // b7 = CSM MODE - // b6 = 3 slot mode - // b5 = reset b - // b4 = reset a - // b3 = timer enable b - // b2 = timer enable a - // b1 = load b - // b0 = load a - - if ((data ^ YM2612.Mode) & 0x40) - { - // We changed the channel 2 mode, so recalculate phase step - // This fix the punch sound in Street of Rage 2 - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step - } - -// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL; -// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL; - -// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande - YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status - - YM2612.Mode = data; - break; - - case 0x28: { - int nch = data & 3; - if ( nch == 3 ) - return 1; - if ( data & 4 ) - nch += 3; - channel_t& ch = YM2612.CHANNEL [nch]; - - YM2612_Special_Update(); - - if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1 - else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1 - if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3 - else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3 - if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2 - else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2 - if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4 - else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4 - break; - } - - case 0x2B: - if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update(); - - YM2612.DAC = data & 0x80; // activation/desactivation du DAC - break; - } - - return 0; -} - -void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) -{ - assert( sample_rate ); - assert( !clock_rate || clock_rate > sample_rate ); - - int i; - - // 144 = 12 * (prescale * 2) = 12 * 6 * 2 - // prescale set to 6 by default - - double Frequence = (clock_rate ? clock_rate / sample_rate / 144.0 : 1.0); - if ( fabs( Frequence - 1.0 ) < 0.0000001 ) - Frequence = 1.0; - YM2612.TimerBase = int (Frequence * 4096.0); - - // Tableau TL : - // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) - // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) - - for ( i = 0; i < TL_LENGHT; i++ ) - { - if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) - { - g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0; - } - else - { - // Decibel -> Voltage - g.TL_TAB [i] = int (MAX_OUT / pow( 10.0, ENV_STEP / 20.0f * i )); - g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; - } - } - - // Tableau SIN : - // g.SIN_TAB [x] [y] = sin(x) * y; - // x = phase and y = volume - - g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF; - - for ( i = 1; i <= SIN_LENGHT / 4; i++ ) - { - // Sinus in dB - double x = 20 * log10( 1 / sin( 2.0 * PI * i / SIN_LENGHT ) ); // convert to dB - - int j = (int) (x / ENV_STEP); // Get TL range - - if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF; - - g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j; - g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j; - } - - // Tableau LFO (LFO wav) : - - for ( i = 0; i < LFO_LENGHT; i++ ) - { - double x = 1 + sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) ); // Sinus - x *= 11.8 / ENV_STEP / 2; // ajusted to MAX enveloppe modulation - - g.LFO_ENV_TAB [i] = (int) x; - - x = sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) ); // Sinus - x *= (1 << (LFO_HBITS - 1)) - 1; - - g.LFO_FREQ_TAB [i] = (int) x; - } - - // Tableau Enveloppe : - // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve - // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve - - for ( i = 0; i < ENV_LENGHT; i++ ) - { - // Attack curve (x^8 - music level 2 Vectorman 2) - double x = pow( ((ENV_LENGHT - 1) - i) / (double) ENV_LENGHT, 8.0 ); - x *= ENV_LENGHT; - - g.ENV_TAB [i] = (int) x; - - // Decay curve (just linear) - g.ENV_TAB [ENV_LENGHT + i] = i; - } - for ( i = 0; i < 8; i++ ) - g.ENV_TAB [i + ENV_LENGHT * 2] = 0; - - g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state - - // Tableau pour la conversion Attack -> Decay and Decay -> Attack - - int j = ENV_LENGHT - 1; - for ( i = 0; i < ENV_LENGHT; i++ ) - { - while ( j && g.ENV_TAB [j] < i ) - j--; - - g.DECAY_TO_ATTACK [i] = j << ENV_LBITS; - } - - // Tableau pour le Substain Level - - for ( i = 0; i < 15; i++ ) - { - double x = i * 3 / ENV_STEP; // 3 and not 6 (Mickey Mania first music for test) - - g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; - } - - g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off - - // Tableau Frequency Step - { - // 0.5 because MUL = value * 2 - #if SIN_LBITS + SIN_HBITS - (21 - 7) < 0 - double const factor = 0.5 / (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)) * Frequence; - #else - double const factor = 0.5 * (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))) * Frequence; - #endif - for ( i = 0; i < 2048; i++ ) - g.FINC_TAB [i] = unsigned (i * factor); - } - - // Tableaux Attack & Decay Rate - - for ( i = 0; i < 4; i++ ) - { - g.AR_TAB [i] = 0; - g.DR_TAB [i] = 0; - } - - for ( i = 0; i < 60; i++ ) - { - double x = - (1.0 + ((i & 3) * 0.25)) * // bits 0-1 : x1.00, x1.25, x1.50, x1.75 - (ENV_LENGHT << ENV_LBITS) * // on ajuste pour le tableau g.ENV_TAB - Frequence * - (1 << (i >> 2)); // bits 2-5 : shift bits (x2^0 - x2^15) - - g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE); - g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE); - } - - for ( i = 64; i < 96; i++ ) - { - g.AR_TAB [i] = g.AR_TAB [63]; - g.DR_TAB [i] = g.DR_TAB [63]; - - g.NULL_RATE [i - 64] = 0; - } - - for ( i = 96; i < 128; i++ ) - g.AR_TAB [i] = 0; - - // Tableau Detune - { - #if SIN_LBITS + SIN_HBITS - 21 < 0 - double const factor = 1.0 / (1 << (21 - SIN_LBITS - SIN_HBITS)) * Frequence; - #else - double const factor = (1 << (SIN_LBITS + SIN_HBITS - 21)) * Frequence; - #endif - for ( i = 0; i < 4; i++ ) - { - for ( int j = 0; j < 32; j++ ) - { - double y = DT_DEF_TAB [(i << 5) + j] * factor; - - g.DT_TAB [i + 0] [j] = (int) y; - g.DT_TAB [i + 4] [j] = (int) -y; - } - } - } - - // Tableau LFO - g.LFO_INC_TAB [0] = unsigned (3.98 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [1] = unsigned (5.56 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [2] = unsigned (6.02 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [3] = unsigned (6.37 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [4] = unsigned (6.88 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [5] = unsigned (9.63 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [6] = unsigned (48.1 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [7] = unsigned (72.2 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - - reset(); -} - -const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) -{ - if ( !impl ) - { - impl = (Ym2612_Impl*) malloc( sizeof *impl ); - if ( !impl ) - return "Out of memory"; - impl->mute_mask = 0; - } - memset( &impl->YM2612, 0, sizeof impl->YM2612 ); - - impl->set_rate( sample_rate, clock_rate ); - - return 0; -} - -Ym2612_Emu::~Ym2612_Emu() -{ - free( impl ); -} - -inline void Ym2612_Impl::write0( int opn_addr, int data ) -{ - assert( (unsigned) data <= 0xFF ); - - if ( opn_addr < 0x30 ) - { - YM2612.REG [0] [opn_addr] = data; - YM_SET( opn_addr, data ); - } - else if ( YM2612.REG [0] [opn_addr] != data ) - { - YM2612.REG [0] [opn_addr] = data; - - if ( opn_addr < 0xA0 ) - SLOT_SET( opn_addr, data ); - else - CHANNEL_SET( opn_addr, data ); - } -} - -inline void Ym2612_Impl::write1( int opn_addr, int data ) -{ - assert( (unsigned) data <= 0xFF ); - - if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data ) - { - YM2612.REG [1] [opn_addr] = data; - - if ( opn_addr < 0xA0 ) - SLOT_SET( opn_addr + 0x100, data ); - else - CHANNEL_SET( opn_addr + 0x100, data ); - } -} - -void Ym2612_Emu::reset() -{ - impl->reset(); -} - -void Ym2612_Impl::reset() -{ - g.LFOcnt = 0; - YM2612.TimerA = 0; - YM2612.TimerAL = 0; - YM2612.TimerAcnt = 0; - YM2612.TimerB = 0; - YM2612.TimerBL = 0; - YM2612.TimerBcnt = 0; - YM2612.DAC = 0; - - YM2612.Status = 0; - - int i; - for ( i = 0; i < channel_count; i++ ) - { - channel_t& ch = YM2612.CHANNEL [i]; - - ch.LEFT = ~0; - ch.RIGHT = ~0; - ch.ALGO = 0; - ch.FB = 31; - ch.FMS = 0; - ch.AMS = 0; - - for ( int j = 0 ;j < 4 ; j++ ) - { - ch.S0_OUT [j] = 0; - ch.FNUM [j] = 0; - ch.FOCT [j] = 0; - ch.KC [j] = 0; - - ch.SLOT [j].Fcnt = 0; - ch.SLOT [j].Finc = 0; - ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase... - ch.SLOT [j].Einc = 0; - ch.SLOT [j].Ecmp = 0; - ch.SLOT [j].Ecurp = RELEASE; - - ch.SLOT [j].ChgEnM = 0; - } - } - - for ( i = 0; i < 0x100; i++ ) - { - YM2612.REG [0] [i] = -1; - YM2612.REG [1] [i] = -1; - } - - for ( i = 0xB6; i >= 0xB4; i-- ) - { - write0( i, 0xC0 ); - write1( i, 0xC0 ); - } - - for ( i = 0xB2; i >= 0x22; i-- ) - { - write0( i, 0 ); - write1( i, 0 ); - } - - write0( 0x2A, 0x80 ); -} - -void Ym2612_Emu::write0( int addr, int data ) -{ - impl->write0( addr, data ); -} - -void Ym2612_Emu::write1( int addr, int data ) -{ - impl->write1( addr, data ); -} - -void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } - -static void update_envelope_( slot_t* sl ) -{ - switch ( sl->Ecurp ) - { - case 0: - // Env_Attack_Next - - // Verified with Gynoug even in HQ (explode SFX) - sl->Ecnt = ENV_DECAY; - - sl->Einc = sl->EincD; - sl->Ecmp = sl->SLL; - sl->Ecurp = DECAY; - break; - - case 1: - // Env_Decay_Next - - // Verified with Gynoug even in HQ (explode SFX) - sl->Ecnt = sl->SLL; - - sl->Einc = sl->EincS; - sl->Ecmp = ENV_END; - sl->Ecurp = SUBSTAIN; - break; - - case 2: - // Env_Substain_Next(slot_t *SL) - if(sl->SEG & 8) // SSG envelope type - { - if(sl->SEG & 1) - { - sl->Ecnt = ENV_END; - sl->Einc = 0; - sl->Ecmp = ENV_END + 1; - } - else - { - // re KEY ON - - // SL->Fcnt = 0; - // SL->ChgEnM = 0xFFFFFFFF; - - sl->Ecnt = 0; - sl->Einc = sl->EincA; - sl->Ecmp = ENV_DECAY; - sl->Ecurp = ATTACK; - } - - sl->SEG ^= (sl->SEG & 2) << 1; - - break; - } - // fall through - - case 3: - // Env_Release_Next - sl->Ecnt = ENV_END; - sl->Einc = 0; - sl->Ecmp = ENV_END + 1; - break; - - // default: no op - } -} - -inline void update_envelope( slot_t& sl ) -{ - int ecmp = sl.Ecmp; - if ( (sl.Ecnt += sl.Einc) >= ecmp ) - update_envelope_( &sl ); -} - -template -struct ym2612_update_chan { - static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t [], int ); -}; - -typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); - -template -void ym2612_update_chan::func( tables_t& g, channel_t& ch, - Ym2612_Emu::sample_t* buf, int length ) -{ - int not_end = ch.SLOT [S3].Ecnt - ENV_END; - - // algo is a compile-time constant, so all conditions based on it are resolved - // during compilation - - // special cases - if ( algo == 7 ) - not_end |= ch.SLOT [S0].Ecnt - ENV_END; - - if ( algo >= 5 ) - not_end |= ch.SLOT [S2].Ecnt - ENV_END; - - if ( algo >= 4 ) - not_end |= ch.SLOT [S1].Ecnt - ENV_END; - - int CH_S0_OUT_1 = ch.S0_OUT [1]; - - int in0 = ch.SLOT [S0].Fcnt; - int in1 = ch.SLOT [S1].Fcnt; - int in2 = ch.SLOT [S2].Fcnt; - int in3 = ch.SLOT [S3].Fcnt; - - int YM2612_LFOinc = g.LFOinc; - int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc; - - if ( !not_end ) - return; - - do - { - // envelope - int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; - - short const* const ENV_TAB = g.ENV_TAB; - - #define CALC_EN( x ) \ - int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS]; \ - int en##x; \ - if (ch.SLOT [S##x].SEG & 4) { \ - if (temp##x > ENV_MASK) en##x = 0; \ - else en##x = (temp##x ^ ENV_MASK) + ch.SLOT [S##x].TLL + (env_LFO >> ch.SLOT [S##x].AMS); \ - } else en##x = temp##x + ch.SLOT [S##x].TLL + (env_LFO >> ch.SLOT [S##x].AMS); - - CALC_EN( 0 ) - CALC_EN( 1 ) - CALC_EN( 2 ) - CALC_EN( 3 ) - - int const* const TL_TAB = g.TL_TAB; - - #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)]) - - // feedback - int CH_S0_OUT_0 = ch.S0_OUT [0]; - { - int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB); - CH_S0_OUT_1 = CH_S0_OUT_0; - CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); - } - - int CH_OUTd; - if ( algo == 0 ) - { - int temp = in1 + CH_S0_OUT_1; - temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 1 ) - { - int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 2 ) - { - int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 3 ) - { - int temp = in1 + CH_S0_OUT_1; - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 4 ) - { - int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ); - //DO_LIMIT - } - else if ( algo == 5 ) - { - int temp = CH_S0_OUT_1; - CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 ); - //DO_LIMIT - } - else if ( algo == 6 ) - { - CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - //DO_LIMIT - } - else if ( algo == 7 ) - { - CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1; - //DO_LIMIT - } - - CH_OUTd *= 3; - CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; - - // update phase - unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * - ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1 << (LFO_FMS_LBITS - 1)); - YM2612_LFOcnt += YM2612_LFOinc; - in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - - int t0 = buf [0] + (CH_OUTd & ch.LEFT); - int t1 = buf [1] + (CH_OUTd & ch.RIGHT); - - update_envelope( ch.SLOT [0] ); - update_envelope( ch.SLOT [1] ); - update_envelope( ch.SLOT [2] ); - update_envelope( ch.SLOT [3] ); - - ch.S0_OUT [0] = CH_S0_OUT_0; - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - while ( --length ); - - ch.S0_OUT [1] = CH_S0_OUT_1; - - ch.SLOT [S0].Fcnt = in0; - ch.SLOT [S1].Fcnt = in1; - ch.SLOT [S2].Fcnt = in2; - ch.SLOT [S3].Fcnt = in3; -} - -static const ym2612_update_chan_t UPDATE_CHAN [8] = { - &ym2612_update_chan<0>::func, - &ym2612_update_chan<1>::func, - &ym2612_update_chan<2>::func, - &ym2612_update_chan<3>::func, - &ym2612_update_chan<4>::func, - &ym2612_update_chan<5>::func, - &ym2612_update_chan<6>::func, - &ym2612_update_chan<7>::func -}; - -void Ym2612_Impl::run_timer( int length ) -{ - int const step = 6; - int remain = length; - do - { - int n = step; - if ( n > remain ) - n = remain; - remain -= n; - - int i = n * YM2612.TimerBase; - if (YM2612.Mode & 1) // Timer A ON ? - { - // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) - if ((YM2612.TimerAcnt -= i) <= 0) - { - // timer a overflow - - YM2612.Status |= (YM2612.Mode & 0x04) >> 2; - YM2612.TimerAcnt += YM2612.TimerAL; - - if (YM2612.Mode & 0x80) - { - KEY_ON( YM2612.CHANNEL [2], 0 ); - KEY_ON( YM2612.CHANNEL [2], 1 ); - KEY_ON( YM2612.CHANNEL [2], 2 ); - KEY_ON( YM2612.CHANNEL [2], 3 ); - } - } - } - - if (YM2612.Mode & 2) // Timer B ON ? - { - // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) - if ((YM2612.TimerBcnt -= i) <= 0) - { - // timer b overflow - YM2612.Status |= (YM2612.Mode & 0x08) >> 2; - YM2612.TimerBcnt += YM2612.TimerBL; - } - } - } - while ( remain > 0 ); -} - -void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t out [] ) -{ - if ( pair_count <= 0 ) - return; - - if ( YM2612.Mode & 3 ) - run_timer( pair_count ); - - // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies - - for ( int chi = 0; chi < channel_count; chi++ ) - { - channel_t& ch = YM2612.CHANNEL [chi]; - if ( ch.SLOT [0].Finc != -1 ) - continue; - - int i2 = 0; - if ( chi == 2 && (YM2612.Mode & 0x40) ) - i2 = 2; - - for ( int i = 0; i < 4; i++ ) - { - // static int seq [4] = { 2, 1, 3, 0 }; - // if ( i2 ) i2 = seq [i]; - - slot_t& sl = ch.SLOT [i]; - int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]); - int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation - sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; - if (sl.KSR != ksr) // si le KSR a change alors - { // les differents taux pour l'enveloppe sont mis à jour - sl.KSR = ksr; - - sl.EincA = sl.AR [ksr]; - sl.EincD = sl.DR [ksr]; - sl.EincS = sl.SR [ksr]; - sl.EincR = sl.RR [ksr]; - - if (sl.Ecurp == ATTACK) - { - sl.Einc = sl.EincA; - } - else if (sl.Ecurp == DECAY) - { - sl.Einc = sl.EincD; - } - else if (sl.Ecnt < ENV_END) - { - if (sl.Ecurp == SUBSTAIN) - sl.Einc = sl.EincS; - else if (sl.Ecurp == RELEASE) - sl.Einc = sl.EincR; - } - } - - if ( i2 ) - i2 = (i2 ^ 2) ^ (i2 >> 1); - } - } - - for ( int i = 0; i < channel_count; i++ ) - { - if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) ) - UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count ); - } - - g.LFOcnt += g.LFOinc * pair_count; -} - -void Ym2612_Emu::run( int pair_count, sample_t out [] ) { impl->run( pair_count, out ); } +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +// Based on Gens 2.10 ym2612.c + +#ifdef YM2612_EMU_CPP + +#include "Ym2612_Emu.h" + +#include +#include +#include +#include +#include +#include + +/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */ +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// This is mostly the original source in its C style and all. +// +// Somewhat optimized and simplified. Uses a template to generate the many +// variants of Update_Chan. Rewrote header file. In need of full rewrite by +// someone more familiar with FM sound and the YM2612. Has some inaccuracies +// compared to the Sega Genesis sound, particularly being mixed at such a +// high sample accuracy (the Genesis sounds like it has onn(ly 8 bit samples). +// - Shay + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +const int output_bits = 14; + +struct slot_t +{ + const int *DT; // parametre detune + int MUL; // parametre "multiple de frequence" + int TL; // Total Level = volume lorsque l'enveloppe est au plus haut + int TLL; // Total Level ajusted + int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression + int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe + int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer + // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite ! + int SEG; // Type enveloppe SSG + const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR [KSR]) + const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR [KSR]) + const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR [KSR]) + const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR [KSR]) + int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN [Finc >> 16]) + int Finc; // frequency step = pas d'incrementation du compteur-frequence + // plus le pas est grand, plus la frequence est aïgu (ou haute) + int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase + // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... + // en fonction de la valeur de cette variable, on va appeler une fonction permettant + // de mettre à jour l'enveloppe courante. + int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe + int Einc; // Envelope step courant + int Ecmp; // Envelope counter limite pour la prochaine phase + int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque + // cette valeur est egal à AR [KSR] + int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression + // cette valeur est egal à DR [KSR] + int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue + // cette valeur est egal à SR [KSR] + int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement + // cette valeur est egal à RR [KSR] + int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree + // d'un autre ou carrement à la sortie de la voie + int INd; // input data of the slot = donnees en entree du slot + int ChgEnM; // Change envelop mask. + int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO + int AMSon; // AMS enable flag = drapeau d'activation de l'AMS +}; + +struct channel_t +{ + int S0_OUT [4]; // anciennes sorties slot 0 (pour le feed back) + int LEFT; // LEFT enable flag + int RIGHT; // RIGHT enable flag + int ALGO; // Algorythm = determine les connections entre les operateurs + int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree) + int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO + int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO + int FNUM [4]; // hauteur frequence de la voie (+ 3 pour le mode special) + int FOCT [4]; // octave de la voie (+ 3 pour le mode special) + int KC [4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) + slot_t SLOT [4]; // four slot.operators = les 4 slots de la voie + int FFlag; // Frequency step recalculation flag +}; + +struct state_t +{ + int TimerBase; // TimerBase calculation + int Status; // YM2612 Status (timer overflow) + int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter + int TimerAL; + int TimerAcnt; // timerA counter = valeur courante du Timer A + int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter + int TimerBL; + int TimerBcnt; // timerB counter = valeur courante du Timer B + int Mode; // Mode actuel des voie 3 et 6 (normal / special) + int DAC; // DAC enabled flag + channel_t CHANNEL [Ym2612_Emu::channel_count]; // Les 6 voies du YM2612 + int REG [2] [0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif + // cela nous rend le debuggage plus facile +}; + +#undef PI +#define PI 3.14159265358979323846 + +#define ATTACK 0 +#define DECAY 1 +#define SUBSTAIN 2 +#define RELEASE 3 + +// SIN_LBITS <= 16 +// LFO_HBITS <= 16 +// (SIN_LBITS + SIN_HBITS) <= 26 +// (ENV_LBITS + ENV_HBITS) <= 28 +// (LFO_LBITS + LFO_HBITS) <= 28 + +#define SIN_HBITS 12 // Sinus phase counter int part +#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) + +#if (SIN_LBITS > 16) +#define SIN_LBITS 16 // Can't be greater than 16 bits +#endif + +#define ENV_HBITS 12 // Env phase counter int part +#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) + +#define LFO_HBITS 10 // LFO phase counter int part +#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) + +#define SIN_LENGHT (1 << SIN_HBITS) +#define ENV_LENGHT (1 << ENV_HBITS) +#define LFO_LENGHT (1 << LFO_HBITS) + +#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO + +#define SIN_MASK (SIN_LENGHT - 1) +#define ENV_MASK (ENV_LENGHT - 1) +#define LFO_MASK (LFO_LENGHT - 1) + +#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB + +#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) +#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) +#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) + +#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 +#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) + +#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) +//#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) + +#define AR_RATE 399128 +#define DR_RATE 5514396 + +//#define AR_RATE 426136 +//#define DR_RATE (AR_RATE * 12) + +#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) +#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) + +#define S0 0 // Stupid typo of the YM2612 +#define S1 2 +#define S2 1 +#define S3 3 + +struct tables_t +{ + short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE) + int LFOcnt; // LFO counter = compteur-frequence pour le LFO + int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO + // plus le pas est grand, plus la frequence est grande + unsigned int AR_TAB [128]; // Attack rate table + unsigned int DR_TAB [96]; // Decay rate table + unsigned int DT_TAB [8] [32]; // Detune table + unsigned int SL_TAB [16]; // Substain level table + unsigned int NULL_RATE [32]; // Table for NULL rate + int LFO_INC_TAB [8]; // LFO step table + + short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) + + short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) + short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE + int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) + unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase + unsigned int FINC_TAB [2048]; // Frequency step table +}; + +static const unsigned char DT_DEF_TAB [4 * 32] = +{ +// FD = 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + +// FD = 1 + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + +// FD = 2 + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, + +// FD = 3 + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 +}; + +static const unsigned char FKEY_TAB [16] = +{ + 0, 0, 0, 0, + 0, 0, 0, 1, + 2, 3, 3, 3, + 3, 3, 3, 3 +}; + +static const unsigned char LFO_AMS_TAB [4] = +{ + 31, 4, 1, 0 +}; + +static const unsigned char LFO_FMS_TAB [8] = +{ + LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, + LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, + LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, + LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 +}; + +inline void YM2612_Special_Update() { } + +struct Ym2612_Impl +{ + enum { channel_count = Ym2612_Emu::channel_count }; + + state_t YM2612; + int mute_mask; + tables_t g; + + void KEY_ON( channel_t&, int ); + void KEY_OFF( channel_t&, int ); + int SLOT_SET( int, int ); + int CHANNEL_SET( int, int ); + int YM_SET( int, int ); + + void set_rate( double sample_rate, double clock_factor ); + void reset(); + void write0( int addr, int data ); + void write1( int addr, int data ); + void run_timer( int ); + void run( int pair_count, Ym2612_Emu::sample_t [] ); +}; + +void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) +{ + slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot + + if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ? + { + SL->Fcnt = 0; + + // Fix Ecco 2 splash sound + + SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; + SL->ChgEnM = ~0; + +// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; +// SL->Ecnt = 0; + + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } +} + + +void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) +{ + slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot + + if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ? + { + if (SL->Ecnt < ENV_DECAY) // attack phase ? + { + SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; + } + + SL->Einc = SL->EincR; + SL->Ecmp = ENV_END; + SL->Ecurp = RELEASE; + } +} + + +int Ym2612_Impl::SLOT_SET( int Adr, int data ) +{ + int nch = Adr & 3; + if ( nch == 3 ) + return 1; + + channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)]; + slot_t& sl = ch.SLOT [(Adr >> 2) & 3]; + + switch ( Adr & 0xF0 ) + { + case 0x30: + if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1; + else sl.MUL = 1; + + sl.DT = (int*) g.DT_TAB [(data >> 4) & 7]; + + ch.SLOT [0].Finc = -1; + + break; + + case 0x40: + sl.TL = data & 0x7F; + + // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... + YM2612_Special_Update(); + +#if ((ENV_HBITS - 7) < 0) + sl.TLL = sl.TL >> (7 - ENV_HBITS); +#else + sl.TLL = sl.TL << (ENV_HBITS - 7); +#endif + + break; + + case 0x50: + sl.KSR_S = 3 - (data >> 6); + + ch.SLOT [0].Finc = -1; + + if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1]; + else sl.AR = (int*) &g.NULL_RATE [0]; + + sl.EincA = sl.AR [sl.KSR]; + if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA; + break; + + case 0x60: + if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS; + else sl.AMS = 31; + + if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1]; + else sl.DR = (int*) &g.NULL_RATE [0]; + + sl.EincD = sl.DR [sl.KSR]; + if (sl.Ecurp == DECAY) sl.Einc = sl.EincD; + break; + + case 0x70: + if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1]; + else sl.SR = (int*) &g.NULL_RATE [0]; + + sl.EincS = sl.SR [sl.KSR]; + if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS; + break; + + case 0x80: + sl.SLL = g.SL_TAB [data >> 4]; + + sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2]; + + sl.EincR = sl.RR [sl.KSR]; + if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR; + break; + + case 0x90: + // SSG-EG envelope shapes : + /* + E At Al H + + 1 0 0 0 \\\\ + 1 0 0 1 \___ + 1 0 1 0 \/\/ + 1 0 1 1 \ + 1 1 0 0 //// + 1 1 0 1 / + 1 1 1 0 /\/\ + 1 1 1 1 /___ + + E = SSG-EG enable + At = Start negate + Al = Altern + H = Hold */ + + if(data & 0x08) sl.SEG = data & 0x0F; + else sl.SEG = 0; + break; + } + + return 0; +} + + +int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) +{ + int num = Adr & 3; + if ( num == 3 ) + return 1; + + channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)]; + + switch ( Adr & 0xFC ) + { + case 0xA0: + YM2612_Special_Update(); + + ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data; + ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; + + ch.SLOT [0].Finc = -1; + break; + + case 0xA4: + YM2612_Special_Update(); + + ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8); + ch.FOCT [0] = (data & 0x38) >> 3; + ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; + + ch.SLOT [0].Finc = -1; + break; + + case 0xA8: + if ( Adr < 0x100 ) + { + num++; + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data; + YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | + FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; + } + break; + + case 0xAC: + if ( Adr < 0x100 ) + { + num++; + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8); + YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3; + YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | + FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; + } + break; + + case 0xB0: + if ( ch.ALGO != (data & 7) ) + { + // Fix VectorMan 2 heli sound (level 1) + YM2612_Special_Update(); + + ch.ALGO = data & 7; + + ch.SLOT [0].ChgEnM = 0; + ch.SLOT [1].ChgEnM = 0; + ch.SLOT [2].ChgEnM = 0; + ch.SLOT [3].ChgEnM = 0; + } + + ch.FB = 9 - ((data >> 3) & 7); // Real thing ? + +// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... +// else ch.FB = 31; + break; + + case 0xB4: { + YM2612_Special_Update(); + + ch.LEFT = 0 - ((data >> 7) & 1); + ch.RIGHT = 0 - ((data >> 6) & 1); + + ch.AMS = LFO_AMS_TAB [(data >> 4) & 3]; + ch.FMS = LFO_FMS_TAB [data & 7]; + + for ( int i = 0; i < 4; i++ ) + { + slot_t& sl = ch.SLOT [i]; + sl.AMS = (sl.AMSon ? ch.AMS : 31); + } + break; + } + } + + return 0; +} + + +int Ym2612_Impl::YM_SET(int Adr, int data) +{ + switch ( Adr ) + { + case 0x22: + if (data & 8) // LFO enable + { + // Cool Spot music 1, LFO modified severals time which + // distord the sound, have to check that on a real genesis... + + g.LFOinc = g.LFO_INC_TAB [data & 7]; + } + else + { + g.LFOinc = g.LFOcnt = 0; + } + break; + + case 0x24: + YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2); + + if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) + { + YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; + } + break; + + case 0x25: + YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3); + + if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) + { + YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; + } + break; + + case 0x26: + YM2612.TimerB = data; + + if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12)) + { + YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12); + } + break; + + case 0x27: + // Parametre divers + // b7 = CSM MODE + // b6 = 3 slot mode + // b5 = reset b + // b4 = reset a + // b3 = timer enable b + // b2 = timer enable a + // b1 = load b + // b0 = load a + + if ((data ^ YM2612.Mode) & 0x40) + { + // We changed the channel 2 mode, so recalculate phase step + // This fix the punch sound in Street of Rage 2 + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step + } + +// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL; +// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL; + +// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande + YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status + + YM2612.Mode = data; + break; + + case 0x28: { + int nch = data & 3; + if ( nch == 3 ) + return 1; + if ( data & 4 ) + nch += 3; + channel_t& ch = YM2612.CHANNEL [nch]; + + YM2612_Special_Update(); + + if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1 + else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1 + if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3 + else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3 + if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2 + else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2 + if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4 + else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4 + break; + } + + case 0x2B: + if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update(); + + YM2612.DAC = data & 0x80; // activation/desactivation du DAC + break; + } + + return 0; +} + +void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) +{ + assert( sample_rate ); + assert( !clock_rate || clock_rate > sample_rate ); + + int i; + + // 144 = 12 * (prescale * 2) = 12 * 6 * 2 + // prescale set to 6 by default + + double Frequence = (clock_rate ? clock_rate / sample_rate / 144.0 : 1.0); + if ( fabs( Frequence - 1.0 ) < 0.0000001 ) + Frequence = 1.0; + YM2612.TimerBase = int (Frequence * 4096.0); + + // Tableau TL : + // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) + // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) + + for ( i = 0; i < TL_LENGHT; i++ ) + { + if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) + { + g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0; + } + else + { + // Decibel -> Voltage + g.TL_TAB [i] = int (MAX_OUT / pow( 10.0, ENV_STEP / 20.0f * i )); + g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; + } + } + + // Tableau SIN : + // g.SIN_TAB [x] [y] = sin(x) * y; + // x = phase and y = volume + + g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF; + + for ( i = 1; i <= SIN_LENGHT / 4; i++ ) + { + // Sinus in dB + double x = 20 * log10( 1 / sin( 2.0 * PI * i / SIN_LENGHT ) ); // convert to dB + + int j = (int) (x / ENV_STEP); // Get TL range + + if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF; + + g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j; + g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j; + } + + // Tableau LFO (LFO wav) : + + for ( i = 0; i < LFO_LENGHT; i++ ) + { + double x = 1 + sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) ); // Sinus + x *= 11.8 / ENV_STEP / 2; // ajusted to MAX enveloppe modulation + + g.LFO_ENV_TAB [i] = (int) x; + + x = sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) ); // Sinus + x *= (1 << (LFO_HBITS - 1)) - 1; + + g.LFO_FREQ_TAB [i] = (int) x; + } + + // Tableau Enveloppe : + // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve + // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve + + for ( i = 0; i < ENV_LENGHT; i++ ) + { + // Attack curve (x^8 - music level 2 Vectorman 2) + double x = pow( ((ENV_LENGHT - 1) - i) / (double) ENV_LENGHT, 8.0 ); + x *= ENV_LENGHT; + + g.ENV_TAB [i] = (int) x; + + // Decay curve (just linear) + g.ENV_TAB [ENV_LENGHT + i] = i; + } + for ( i = 0; i < 8; i++ ) + g.ENV_TAB [i + ENV_LENGHT * 2] = 0; + + g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state + + // Tableau pour la conversion Attack -> Decay and Decay -> Attack + + int j = ENV_LENGHT - 1; + for ( i = 0; i < ENV_LENGHT; i++ ) + { + while ( j && g.ENV_TAB [j] < i ) + j--; + + g.DECAY_TO_ATTACK [i] = j << ENV_LBITS; + } + + // Tableau pour le Substain Level + + for ( i = 0; i < 15; i++ ) + { + double x = i * 3 / ENV_STEP; // 3 and not 6 (Mickey Mania first music for test) + + g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; + } + + g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off + + // Tableau Frequency Step + { + // 0.5 because MUL = value * 2 + #if SIN_LBITS + SIN_HBITS - (21 - 7) < 0 + double const factor = 0.5 / (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)) * Frequence; + #else + double const factor = 0.5 * (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))) * Frequence; + #endif + for ( i = 0; i < 2048; i++ ) + g.FINC_TAB [i] = unsigned (i * factor); + } + + // Tableaux Attack & Decay Rate + + for ( i = 0; i < 4; i++ ) + { + g.AR_TAB [i] = 0; + g.DR_TAB [i] = 0; + } + + for ( i = 0; i < 60; i++ ) + { + double x = + (1.0 + ((i & 3) * 0.25)) * // bits 0-1 : x1.00, x1.25, x1.50, x1.75 + (ENV_LENGHT << ENV_LBITS) * // on ajuste pour le tableau g.ENV_TAB + Frequence * + (1 << (i >> 2)); // bits 2-5 : shift bits (x2^0 - x2^15) + + g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE); + g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE); + } + + for ( i = 64; i < 96; i++ ) + { + g.AR_TAB [i] = g.AR_TAB [63]; + g.DR_TAB [i] = g.DR_TAB [63]; + + g.NULL_RATE [i - 64] = 0; + } + + for ( i = 96; i < 128; i++ ) + g.AR_TAB [i] = 0; + + // Tableau Detune + { + #if SIN_LBITS + SIN_HBITS - 21 < 0 + double const factor = 1.0 / (1 << (21 - SIN_LBITS - SIN_HBITS)) * Frequence; + #else + double const factor = (1 << (SIN_LBITS + SIN_HBITS - 21)) * Frequence; + #endif + for ( i = 0; i < 4; i++ ) + { + for ( int j = 0; j < 32; j++ ) + { + double y = DT_DEF_TAB [(i << 5) + j] * factor; + + g.DT_TAB [i + 0] [j] = (int) y; + g.DT_TAB [i + 4] [j] = (int) -y; + } + } + } + + // Tableau LFO + g.LFO_INC_TAB [0] = unsigned (3.98 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [1] = unsigned (5.56 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [2] = unsigned (6.02 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [3] = unsigned (6.37 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [4] = unsigned (6.88 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [5] = unsigned (9.63 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [6] = unsigned (48.1 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [7] = unsigned (72.2 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + + reset(); +} + +const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) +{ + if ( !impl ) + { + impl = (Ym2612_Impl*) malloc( sizeof *impl ); + if ( !impl ) + return "Out of memory"; + impl->mute_mask = 0; + } + memset( &impl->YM2612, 0, sizeof impl->YM2612 ); + + impl->set_rate( sample_rate, clock_rate ); + + return 0; +} + +Ym2612_Emu::~Ym2612_Emu() +{ + free( impl ); +} + +inline void Ym2612_Impl::write0( int opn_addr, int data ) +{ + assert( (unsigned) data <= 0xFF ); + + if ( opn_addr < 0x30 ) + { + YM2612.REG [0] [opn_addr] = data; + YM_SET( opn_addr, data ); + } + else if ( YM2612.REG [0] [opn_addr] != data ) + { + YM2612.REG [0] [opn_addr] = data; + + if ( opn_addr < 0xA0 ) + SLOT_SET( opn_addr, data ); + else + CHANNEL_SET( opn_addr, data ); + } +} + +inline void Ym2612_Impl::write1( int opn_addr, int data ) +{ + assert( (unsigned) data <= 0xFF ); + + if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data ) + { + YM2612.REG [1] [opn_addr] = data; + + if ( opn_addr < 0xA0 ) + SLOT_SET( opn_addr + 0x100, data ); + else + CHANNEL_SET( opn_addr + 0x100, data ); + } +} + +void Ym2612_Emu::reset() +{ + impl->reset(); +} + +void Ym2612_Impl::reset() +{ + g.LFOcnt = 0; + YM2612.TimerA = 0; + YM2612.TimerAL = 0; + YM2612.TimerAcnt = 0; + YM2612.TimerB = 0; + YM2612.TimerBL = 0; + YM2612.TimerBcnt = 0; + YM2612.DAC = 0; + + YM2612.Status = 0; + + int i; + for ( i = 0; i < channel_count; i++ ) + { + channel_t& ch = YM2612.CHANNEL [i]; + + ch.LEFT = ~0; + ch.RIGHT = ~0; + ch.ALGO = 0; + ch.FB = 31; + ch.FMS = 0; + ch.AMS = 0; + + for ( int j = 0 ;j < 4 ; j++ ) + { + ch.S0_OUT [j] = 0; + ch.FNUM [j] = 0; + ch.FOCT [j] = 0; + ch.KC [j] = 0; + + ch.SLOT [j].Fcnt = 0; + ch.SLOT [j].Finc = 0; + ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase... + ch.SLOT [j].Einc = 0; + ch.SLOT [j].Ecmp = 0; + ch.SLOT [j].Ecurp = RELEASE; + + ch.SLOT [j].ChgEnM = 0; + } + } + + for ( i = 0; i < 0x100; i++ ) + { + YM2612.REG [0] [i] = -1; + YM2612.REG [1] [i] = -1; + } + + for ( i = 0xB6; i >= 0xB4; i-- ) + { + write0( i, 0xC0 ); + write1( i, 0xC0 ); + } + + for ( i = 0xB2; i >= 0x22; i-- ) + { + write0( i, 0 ); + write1( i, 0 ); + } + + write0( 0x2A, 0x80 ); +} + +void Ym2612_Emu::write0( int addr, int data ) +{ + impl->write0( addr, data ); +} + +void Ym2612_Emu::write1( int addr, int data ) +{ + impl->write1( addr, data ); +} + +void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } + +static void update_envelope_( slot_t* sl ) +{ + switch ( sl->Ecurp ) + { + case 0: + // Env_Attack_Next + + // Verified with Gynoug even in HQ (explode SFX) + sl->Ecnt = ENV_DECAY; + + sl->Einc = sl->EincD; + sl->Ecmp = sl->SLL; + sl->Ecurp = DECAY; + break; + + case 1: + // Env_Decay_Next + + // Verified with Gynoug even in HQ (explode SFX) + sl->Ecnt = sl->SLL; + + sl->Einc = sl->EincS; + sl->Ecmp = ENV_END; + sl->Ecurp = SUBSTAIN; + break; + + case 2: + // Env_Substain_Next(slot_t *SL) + if(sl->SEG & 8) // SSG envelope type + { + if(sl->SEG & 1) + { + sl->Ecnt = ENV_END; + sl->Einc = 0; + sl->Ecmp = ENV_END + 1; + } + else + { + // re KEY ON + + // SL->Fcnt = 0; + // SL->ChgEnM = 0xFFFFFFFF; + + sl->Ecnt = 0; + sl->Einc = sl->EincA; + sl->Ecmp = ENV_DECAY; + sl->Ecurp = ATTACK; + } + + sl->SEG ^= (sl->SEG & 2) << 1; + + break; + } + // fall through + + case 3: + // Env_Release_Next + sl->Ecnt = ENV_END; + sl->Einc = 0; + sl->Ecmp = ENV_END + 1; + break; + + // default: no op + } +} + +inline void update_envelope( slot_t& sl ) +{ + int ecmp = sl.Ecmp; + if ( (sl.Ecnt += sl.Einc) >= ecmp ) + update_envelope_( &sl ); +} + +template +struct ym2612_update_chan { + static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t [], int ); +}; + +typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); + +template +void ym2612_update_chan::func( tables_t& g, channel_t& ch, + Ym2612_Emu::sample_t* buf, int length ) +{ + int not_end = ch.SLOT [S3].Ecnt - ENV_END; + + // algo is a compile-time constant, so all conditions based on it are resolved + // during compilation + + // special cases + if ( algo == 7 ) + not_end |= ch.SLOT [S0].Ecnt - ENV_END; + + if ( algo >= 5 ) + not_end |= ch.SLOT [S2].Ecnt - ENV_END; + + if ( algo >= 4 ) + not_end |= ch.SLOT [S1].Ecnt - ENV_END; + + int CH_S0_OUT_1 = ch.S0_OUT [1]; + + int in0 = ch.SLOT [S0].Fcnt; + int in1 = ch.SLOT [S1].Fcnt; + int in2 = ch.SLOT [S2].Fcnt; + int in3 = ch.SLOT [S3].Fcnt; + + int YM2612_LFOinc = g.LFOinc; + int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc; + + if ( !not_end ) + return; + + do + { + // envelope + int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; + + short const* const ENV_TAB = g.ENV_TAB; + + #define CALC_EN( x ) \ + int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS]; \ + int en##x; \ + if (ch.SLOT [S##x].SEG & 4) { \ + if (temp##x > ENV_MASK) en##x = 0; \ + else en##x = (temp##x ^ ENV_MASK) + ch.SLOT [S##x].TLL + (env_LFO >> ch.SLOT [S##x].AMS); \ + } else en##x = temp##x + ch.SLOT [S##x].TLL + (env_LFO >> ch.SLOT [S##x].AMS); + + CALC_EN( 0 ) + CALC_EN( 1 ) + CALC_EN( 2 ) + CALC_EN( 3 ) + + int const* const TL_TAB = g.TL_TAB; + + #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)]) + + // feedback + int CH_S0_OUT_0 = ch.S0_OUT [0]; + { + int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB); + CH_S0_OUT_1 = CH_S0_OUT_0; + CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); + } + + int CH_OUTd; + if ( algo == 0 ) + { + int temp = in1 + CH_S0_OUT_1; + temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 1 ) + { + int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 2 ) + { + int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 3 ) + { + int temp = in1 + CH_S0_OUT_1; + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 4 ) + { + int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ); + //DO_LIMIT + } + else if ( algo == 5 ) + { + int temp = CH_S0_OUT_1; + CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 ); + //DO_LIMIT + } + else if ( algo == 6 ) + { + CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + //DO_LIMIT + } + else if ( algo == 7 ) + { + CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1; + //DO_LIMIT + } + + CH_OUTd *= 3; + CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; + + // update phase + unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * + ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1 << (LFO_FMS_LBITS - 1)); + YM2612_LFOcnt += YM2612_LFOinc; + in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + + int t0 = buf [0] + (CH_OUTd & ch.LEFT); + int t1 = buf [1] + (CH_OUTd & ch.RIGHT); + + update_envelope( ch.SLOT [0] ); + update_envelope( ch.SLOT [1] ); + update_envelope( ch.SLOT [2] ); + update_envelope( ch.SLOT [3] ); + + ch.S0_OUT [0] = CH_S0_OUT_0; + buf [0] = t0; + buf [1] = t1; + buf += 2; + } + while ( --length ); + + ch.S0_OUT [1] = CH_S0_OUT_1; + + ch.SLOT [S0].Fcnt = in0; + ch.SLOT [S1].Fcnt = in1; + ch.SLOT [S2].Fcnt = in2; + ch.SLOT [S3].Fcnt = in3; +} + +static const ym2612_update_chan_t UPDATE_CHAN [8] = { + &ym2612_update_chan<0>::func, + &ym2612_update_chan<1>::func, + &ym2612_update_chan<2>::func, + &ym2612_update_chan<3>::func, + &ym2612_update_chan<4>::func, + &ym2612_update_chan<5>::func, + &ym2612_update_chan<6>::func, + &ym2612_update_chan<7>::func +}; + +void Ym2612_Impl::run_timer( int length ) +{ + int const step = 6; + int remain = length; + do + { + int n = step; + if ( n > remain ) + n = remain; + remain -= n; + + int i = n * YM2612.TimerBase; + if (YM2612.Mode & 1) // Timer A ON ? + { + // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((YM2612.TimerAcnt -= i) <= 0) + { + // timer a overflow + + YM2612.Status |= (YM2612.Mode & 0x04) >> 2; + YM2612.TimerAcnt += YM2612.TimerAL; + + if (YM2612.Mode & 0x80) + { + KEY_ON( YM2612.CHANNEL [2], 0 ); + KEY_ON( YM2612.CHANNEL [2], 1 ); + KEY_ON( YM2612.CHANNEL [2], 2 ); + KEY_ON( YM2612.CHANNEL [2], 3 ); + } + } + } + + if (YM2612.Mode & 2) // Timer B ON ? + { + // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((YM2612.TimerBcnt -= i) <= 0) + { + // timer b overflow + YM2612.Status |= (YM2612.Mode & 0x08) >> 2; + YM2612.TimerBcnt += YM2612.TimerBL; + } + } + } + while ( remain > 0 ); +} + +void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t out [] ) +{ + if ( pair_count <= 0 ) + return; + + if ( YM2612.Mode & 3 ) + run_timer( pair_count ); + + // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies + + for ( int chi = 0; chi < channel_count; chi++ ) + { + channel_t& ch = YM2612.CHANNEL [chi]; + if ( ch.SLOT [0].Finc != -1 ) + continue; + + int i2 = 0; + if ( chi == 2 && (YM2612.Mode & 0x40) ) + i2 = 2; + + for ( int i = 0; i < 4; i++ ) + { + // static int seq [4] = { 2, 1, 3, 0 }; + // if ( i2 ) i2 = seq [i]; + + slot_t& sl = ch.SLOT [i]; + int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]); + int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation + sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; + if (sl.KSR != ksr) // si le KSR a change alors + { // les differents taux pour l'enveloppe sont mis à jour + sl.KSR = ksr; + + sl.EincA = sl.AR [ksr]; + sl.EincD = sl.DR [ksr]; + sl.EincS = sl.SR [ksr]; + sl.EincR = sl.RR [ksr]; + + if (sl.Ecurp == ATTACK) + { + sl.Einc = sl.EincA; + } + else if (sl.Ecurp == DECAY) + { + sl.Einc = sl.EincD; + } + else if (sl.Ecnt < ENV_END) + { + if (sl.Ecurp == SUBSTAIN) + sl.Einc = sl.EincS; + else if (sl.Ecurp == RELEASE) + sl.Einc = sl.EincR; + } + } + + if ( i2 ) + i2 = (i2 ^ 2) ^ (i2 >> 1); + } + } + + for ( int i = 0; i < channel_count; i++ ) + { + if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) ) + UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count ); + } + + g.LFOcnt += g.LFOinc * pair_count; +} + +void Ym2612_Emu::run( int pair_count, sample_t out [] ) { impl->run( pair_count, out ); } + +#endif diff --git a/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp b/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp index 8765d2906..c0f86648e 100644 --- a/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp +++ b/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp @@ -1,7 +1,13 @@ // Game_Music_Emu $vers. http://www.slack.net/~ant/ +#ifdef YM2612_EMU_CPP + #include "Ym2612_Emu.h" -#include "fm.h" + +extern "C" { +#include "../vgmplay/chips/mamedef.h" +#include "../vgmplay/chips/fm.h" +} #include "blargg_errors.h" @@ -13,6 +19,8 @@ Ym2612_Emu::~Ym2612_Emu() ym2612_shutdown( impl ); } +static BOOST::uint8_t dummy = 0; + const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) { if ( impl ) @@ -24,10 +32,10 @@ const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) if ( !clock_rate ) clock_rate = sample_rate * 144.; - impl = ym2612_init( (long) (clock_rate + 0.5), (long) (sample_rate + 0.5) ); + impl = ym2612_init( 0, (int) (clock_rate + 0.5), (int) (sample_rate + 0.5), 0, 0, &dummy, 0 ); if ( !impl ) return blargg_err_memory; - + return 0; } @@ -85,3 +93,5 @@ void Ym2612_Emu::run( int pair_count, sample_t* out ) pair_count -= todo; } } + +#endif diff --git a/Frameworks/GME/gme/Ym3812_Emu.cpp b/Frameworks/GME/gme/Ym3812_Emu.cpp deleted file mode 100644 index ddec0357b..000000000 --- a/Frameworks/GME/gme/Ym3812_Emu.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ym3812_Emu.h" - -#include -#include "dbopl.h" - -Ym3812_Emu::Ym3812_Emu() { opl = 0; } - -Ym3812_Emu::~Ym3812_Emu() -{ - delete opl; -} - -int Ym3812_Emu::set_rate( int sample_rate, int clock_rate ) -{ - delete opl; - opl = 0; - - opl = new DBOPL::Chip; - if ( !opl ) - return 1; - - this->sample_rate = sample_rate; - this->clock_rate = clock_rate * 4; - - reset(); - return 0; -} - -void Ym3812_Emu::reset() -{ - opl->Setup( clock_rate, sample_rate ); -} - -void Ym3812_Emu::write( int addr, int data ) -{ - opl->WriteReg( addr, data ); -} - -void Ym3812_Emu::run( int pair_count, sample_t* out ) -{ - Bit32s buf[ 1024 ]; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - opl->GenerateBlock2( todo, buf ); - - for (int i = 0; i < todo; i++) - { - int output_l, output_r; - int output = buf [i]; - output_l = output + out [0]; - output_r = output + out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Ym3812_Emu.h b/Frameworks/GME/gme/Ym3812_Emu.h deleted file mode 100644 index a3db4d85a..000000000 --- a/Frameworks/GME/gme/Ym3812_Emu.h +++ /dev/null @@ -1,35 +0,0 @@ -// YM3812 FM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YM3812_EMU_H -#define YM3812_EMU_H - -namespace DBOPL { - struct Chip; -} - -class Ym3812_Emu { - DBOPL::Chip * opl; - unsigned sample_rate; - unsigned clock_rate; -public: - Ym3812_Emu(); - ~Ym3812_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int sample_rate, int clock_rate ); - - // Resets to power-up state - void reset(); - - // Writes data to addr - void write( int addr, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Ymf262_Emu.cpp b/Frameworks/GME/gme/Ymf262_Emu.cpp deleted file mode 100644 index f96ab6a36..000000000 --- a/Frameworks/GME/gme/Ymf262_Emu.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ymf262_Emu.h" - -#include -#include "dbopl.h" - -Ymf262_Emu::Ymf262_Emu() { opl = 0; } - -Ymf262_Emu::~Ymf262_Emu() -{ - delete opl; -} - -int Ymf262_Emu::set_rate( int sample_rate, int clock_rate ) -{ - delete opl; - opl = 0; - - opl = new DBOPL::Chip; - if ( !opl ) - return 1; - - this->sample_rate = sample_rate; - this->clock_rate = clock_rate; - - reset(); - return 0; -} - -void Ymf262_Emu::reset() -{ - opl->Setup( clock_rate, sample_rate ); -} - -void Ymf262_Emu::write0( int addr, int data ) -{ - opl->WriteReg( addr, data ); -} - -void Ymf262_Emu::write1( int addr, int data ) -{ - opl->WriteReg( 0x100 + addr, data ); -} - -void Ymf262_Emu::run( int pair_count, sample_t* out ) -{ - Bit32s buf[ 2048 ]; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - - if ( opl->opl3Active ) - { - opl->GenerateBlock3( todo, buf ); - - for (int i = 0; i < todo; i++) - { - int output_l, output_r; - output_l = buf [i * 2]; - output_r = buf [i * 2 + 1]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - } - else - { - opl->GenerateBlock2( todo, buf ); - - for (int i = 0; i < todo; i++) - { - int output_l, output_r; - int output = buf [i]; - output_l = output + out [0]; - output_r = output + out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Ymf262_Emu.h b/Frameworks/GME/gme/Ymf262_Emu.h deleted file mode 100644 index c44cb3d3c..000000000 --- a/Frameworks/GME/gme/Ymf262_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// YMF262 FM sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YMF262_EMU_H -#define YMF262_EMU_H - -namespace DBOPL { - struct Chip; -} - -class Ymf262_Emu { - DBOPL::Chip * opl; - unsigned sample_rate; - unsigned clock_rate; -public: - Ymf262_Emu(); - ~Ymf262_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int sample_rate, int clock_rate ); - - // Resets to power-up state - void reset(); - - // Writes data to addr - void write0( int addr, int data ); - void write1( int addr, int data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Ymz280b_Emu.cpp b/Frameworks/GME/gme/Ymz280b_Emu.cpp deleted file mode 100644 index 3bce2505e..000000000 --- a/Frameworks/GME/gme/Ymz280b_Emu.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Game_Music_Emu $vers. http://www.slack.net/~ant/ - -#include "Ymz280b_Emu.h" -#include "ymz280b.h" - -Ymz280b_Emu::Ymz280b_Emu() { chip = 0; } - -Ymz280b_Emu::~Ymz280b_Emu() -{ - if ( chip ) device_stop_ymz280b( chip ); -} - -int Ymz280b_Emu::set_rate( int clock_rate ) -{ - if ( chip ) - { - device_stop_ymz280b( chip ); - chip = 0; - } - - chip = device_start_ymz280b( clock_rate ); - if ( !chip ) - return 0; - - reset(); - return clock_rate * 2 / 384; -} - -void Ymz280b_Emu::reset() -{ - device_reset_ymz280b( chip ); - ymz280b_set_mute_mask( chip, 0 ); -} - -void Ymz280b_Emu::write( int addr, int data ) -{ - ymz280b_w( chip, 0, addr ); - ymz280b_w( chip, 1, data ); -} - -void Ymz280b_Emu::write_rom( int size, int start, int length, void * data ) -{ - ymz280b_write_rom( chip, size, start, length, (const UINT8 *) data ); -} - -void Ymz280b_Emu::mute_voices( int mask ) -{ - ymz280b_set_mute_mask( chip, mask ); -} - -void Ymz280b_Emu::run( int pair_count, sample_t* out ) -{ - stream_sample_t bufL[ 1024 ]; - stream_sample_t bufR[ 1024 ]; - stream_sample_t * buffers[2] = { bufL, bufR }; - - while (pair_count > 0) - { - int todo = pair_count; - if (todo > 1024) todo = 1024; - ymz280b_update( chip, buffers, todo ); - - for (int i = 0; i < todo; i++) - { - int output_l = bufL [i]; - int output_r = bufR [i]; - output_l += out [0]; - output_r += out [1]; - if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); - if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); - out [0] = output_l; - out [1] = output_r; - out += 2; - } - - pair_count -= todo; - } -} diff --git a/Frameworks/GME/gme/Ymz280b_Emu.h b/Frameworks/GME/gme/Ymz280b_Emu.h deleted file mode 100644 index 8c899d3ad..000000000 --- a/Frameworks/GME/gme/Ymz280b_Emu.h +++ /dev/null @@ -1,36 +0,0 @@ -// YMZ280B sound chip emulator interface - -// Game_Music_Emu $vers -#ifndef YMZ280B_EMU_H -#define YMZ280B_EMU_H - -class Ymz280b_Emu { - void* chip; -public: - Ymz280b_Emu(); - ~Ymz280b_Emu(); - - // Sets output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( int clock_rate ); - - // Resets to power-up state - void reset(); - - // Mutes voice n if bit n (1 << n) of mask is set - enum { channel_count = 8 }; - void mute_voices( int mask ); - - // Writes data to addr - void write( int addr, int data ); - - // Scales ROM size, then writes length bytes from data at start offset - void write_rom( int size, int start, int length, void * data ); - - // Runs and writes pair_count*2 samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif diff --git a/Frameworks/GME/gme/Z80_Cpu.cpp b/Frameworks/GME/gme/Z80_Cpu.cpp index 80fb28dea..f1e604c57 100644 --- a/Frameworks/GME/gme/Z80_Cpu.cpp +++ b/Frameworks/GME/gme/Z80_Cpu.cpp @@ -1,82 +1,82 @@ -// $package. http://www.slack.net/~ant/ - -#include "Z80_Cpu.h" - -/* Copyright (C) 2006-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// flags, named with hex value for clarity -int const S80 = 0x80; -int const Z40 = 0x40; -int const F20 = 0x20; -int const H10 = 0x10; -int const F08 = 0x08; -int const V04 = 0x04; -int const P04 = 0x04; -int const N02 = 0x02; -int const C01 = 0x01; - -Z80_Cpu::Z80_Cpu() -{ - cpu_state = &cpu_state_; - - for ( int i = 0x100; --i >= 0; ) - { - int even = 1; - for ( int p = i; p; p >>= 1 ) - even ^= p; - int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); - szpc [i] = n; - szpc [i + 0x100] = n | C01; - } - szpc [0x000] |= Z40; - szpc [0x100] |= Z40; -} - -inline void Z80_Cpu::set_page( int i, void* write, void const* read ) -{ - int offset = Z80_CPU_OFFSET( i * page_size ); - byte * write2 = STATIC_CAST(byte *,write) - offset; - byte const* read2 = STATIC_CAST(byte const*,read ) - offset; - cpu_state_.write [i] = write2; - cpu_state_.read [i] = read2; - cpu_state->write [i] = write2; - cpu_state->read [i] = read2; -} - -void Z80_Cpu::reset( void* unmapped_write, void const* unmapped_read ) -{ - check( cpu_state == &cpu_state_ ); - cpu_state = &cpu_state_; - cpu_state_.time = 0; - cpu_state_.base = 0; - end_time_ = 0; - - for ( int i = 0; i < page_count + 1; i++ ) - set_page( i, unmapped_write, unmapped_read ); - - memset( &r, 0, sizeof r ); -} - -void Z80_Cpu::map_mem( addr_t start, int size, void* write, void const* read ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 ); - - for ( int offset = 0; offset < size; offset += page_size ) - set_page( (start + offset) >> page_bits, - STATIC_CAST(char *,write) + offset, - STATIC_CAST(char const*,read ) + offset ); -} +// $package. http://www.slack.net/~ant/ + +#include "Z80_Cpu.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// flags, named with hex value for clarity +int const S80 = 0x80; +int const Z40 = 0x40; +int const F20 = 0x20; +int const H10 = 0x10; +int const F08 = 0x08; +int const V04 = 0x04; +int const P04 = 0x04; +int const N02 = 0x02; +int const C01 = 0x01; + +Z80_Cpu::Z80_Cpu() +{ + cpu_state = &cpu_state_; + + for ( int i = 0x100; --i >= 0; ) + { + int even = 1; + for ( int p = i; p; p >>= 1 ) + even ^= p; + int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); + szpc [i] = n; + szpc [i + 0x100] = n | C01; + } + szpc [0x000] |= Z40; + szpc [0x100] |= Z40; +} + +inline void Z80_Cpu::set_page( int i, void* write, void const* read ) +{ + int offset = Z80_CPU_OFFSET( i * page_size ); + byte * write2 = STATIC_CAST(byte *,write) - offset; + byte const* read2 = STATIC_CAST(byte const*,read ) - offset; + cpu_state_.write [i] = write2; + cpu_state_.read [i] = read2; + cpu_state->write [i] = write2; + cpu_state->read [i] = read2; +} + +void Z80_Cpu::reset( void* unmapped_write, void const* unmapped_read ) +{ + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; + cpu_state_.time = 0; + cpu_state_.base = 0; + end_time_ = 0; + + for ( int i = 0; i < page_count + 1; i++ ) + set_page( i, unmapped_write, unmapped_read ); + + memset( &r, 0, sizeof r ); +} + +void Z80_Cpu::map_mem( addr_t start, int size, void* write, void const* read ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + + for ( int offset = 0; offset < size; offset += page_size ) + set_page( (start + offset) >> page_bits, + STATIC_CAST(char *,write) + offset, + STATIC_CAST(char const*,read ) + offset ); +} diff --git a/Frameworks/GME/gme/adlib.h b/Frameworks/GME/gme/adlib.h deleted file mode 100644 index 7d5ef6846..000000000 --- a/Frameworks/GME/gme/adlib.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2002-2009 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* $Id: adlib.h,v 1.4 2009/04/26 10:33:53 harekiet Exp $ */ - -#ifndef DOSBOX_ADLIB_H -#define DOSBOX_ADLIB_H - -/*#include "dosbox.h" -#include "mixer.h" -#include "inout.h" -#include "mixer.h" -#include "setup.h" -#include "pic.h" -#include "hardware.h"*/ - - -namespace Adlib { - -struct Timer { - double start; - double delay; - bool enabled, overflow, masked; - Bit8u counter; - Timer() { - masked = false; - overflow = false; - enabled = false; - counter = 0; - delay = 0; - } - //Call update before making any further changes - void Update( double time ) { - if ( !enabled || !delay ) - return; - double deltaStart = time - start; - //Only set the overflow flag when not masked - if ( deltaStart >= 0 && !masked ) { - overflow = 1; - } - } - //On a reset make sure the start is in sync with the next cycle - void Reset(const double& time ) { - overflow = false; - if ( !delay || !enabled ) - return; - double delta = (time - start); - double rem = fmod( delta, delay ); - double next = delay - rem; - start = time + next; - } - void Stop( ) { - enabled = false; - } - void Start( const double& time, Bits scale ) { - //Don't enable again - if ( enabled ) { - return; - } - enabled = true; - delay = 0.001 * (256 - counter ) * scale; - start = time + delay; - } - -}; - -struct Chip { - //Last selected register - Timer timer[2]; - //Check for it being a write to the timer - bool Write( Bit32u addr, Bit8u val ); - //Read the current timer state, will use current double - Bit8u Read( ); -}; - -//The type of handler this is -typedef enum { - MODE_OPL2, - MODE_DUALOPL2, - MODE_OPL3 -} Mode; - -/*class Handler { -public: - //Write an address to a chip, returns the address the chip sets - virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) = 0; - //Write to a specific register in the chip - virtual void WriteReg( Bit32u addr, Bit8u val ) = 0; - //Generate a certain amount of samples - virtual void Generate( MixerChannel* chan, Bitu samples ) = 0; - //Initialize at a specific sample rate and mode - virtual void Init( Bitu rate ) = 0; - virtual ~Handler() { - } -};*/ - -//The cache for 2 chips or an opl3 -typedef Bit8u RegisterCache[512]; - -//Internal class used for dro capturing -class Capture; - -/*class Module: public Module_base { - IO_ReadHandleObject ReadHandler[3]; - IO_WriteHandleObject WriteHandler[3]; - MixerObject mixerObject; - - //Mode we're running in - Mode mode; - //Last selected address in the chip for the different modes - union { - Bit32u normal; - Bit8u dual[2]; - } reg; - void CacheWrite( Bit32u reg, Bit8u val ); - void DualWrite( Bit8u index, Bit8u reg, Bit8u val ); -public: - static OPL_Mode oplmode; - MixerChannel* mixerChan; - Bit32u lastUsed; //Ticks when adlib was last used to turn of mixing after a few second - - Handler* handler; //Handler that will generate the sound - RegisterCache cache; - Capture* capture; - Chip chip[2]; - - //Handle port writes - void PortWrite( Bitu port, Bitu val, Bitu iolen ); - Bitu PortRead( Bitu port, Bitu iolen ); - void Init( Mode m ); - - Module( Section* configuration); - ~Module(); -};*/ - - -} //Adlib namespace - -#endif diff --git a/Frameworks/GME/gme/blargg_common.cpp b/Frameworks/GME/gme/blargg_common.cpp index ed548c4ca..35898d8fe 100644 --- a/Frameworks/GME/gme/blargg_common.cpp +++ b/Frameworks/GME/gme/blargg_common.cpp @@ -55,4 +55,270 @@ blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size ) return blargg_ok; } +static const BOOST::uint8_t mask_tab[6]={0x80,0xE0,0xF0,0xF8,0xFC,0xFE}; + +static const BOOST::uint8_t val_tab[6]={0,0xC0,0xE0,0xF0,0xF8,0xFC}; + +size_t utf8_char_len_from_header( char p_c ) +{ + size_t cnt = 0; + for(;;) + { + if ( ( p_c & mask_tab[cnt] ) == val_tab[cnt] ) break; + if ( ++cnt >= 6 ) return 0; + } + + return cnt + 1; +} + +size_t utf8_decode_char( const char *p_utf8, unsigned & wide, size_t mmax ) +{ + const BOOST::uint8_t * utf8 = ( const BOOST::uint8_t* )p_utf8; + + if ( mmax == 0 ) + { + wide = 0; + return 0; + } + + if ( utf8[0] < 0x80 ) + { + wide = utf8[0]; + return utf8[0]>0 ? 1 : 0; + } + if ( mmax > 6 ) mmax = 6; + wide = 0; + + unsigned res=0; + unsigned n; + unsigned cnt=0; + for(;;) + { + if ( ( *utf8 & mask_tab[cnt] ) == val_tab[cnt] ) break; + if ( ++cnt >= mmax ) return 0; + } + cnt++; + + if ( cnt==2 && !( *utf8 & 0x1E ) ) return 0; + + if ( cnt == 1 ) + res = *utf8; + else + res = ( 0xFF >> ( cnt + 1 ) ) & *utf8; + + for ( n = 1; n < cnt; n++ ) + { + if ( ( utf8[n] & 0xC0 ) != 0x80 ) + return 0; + if ( !res && n == 2 && !( ( utf8[n] & 0x7F ) >> ( 7 - cnt ) ) ) + return 0; + + res = ( res << 6 ) | ( utf8[n] & 0x3F ); + } + + wide = res; + + return cnt; +} + +size_t utf8_encode_char( unsigned wide, char * target ) +{ + size_t count; + + if ( wide < 0x80 ) + count = 1; + else if ( wide < 0x800 ) + count = 2; + else if ( wide < 0x10000 ) + count = 3; + else if ( wide < 0x200000 ) + count = 4; + else if ( wide < 0x4000000 ) + count = 5; + else if ( wide <= 0x7FFFFFFF ) + count = 6; + else + return 0; + + if ( target == 0 ) + return count; + + switch ( count ) + { + case 6: + target[5] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x4000000; + case 5: + target[4] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x200000; + case 4: + target[3] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x10000; + case 3: + target[2] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0x800; + case 2: + target[1] = 0x80 | ( wide & 0x3F ); + wide = wide >> 6; + wide |= 0xC0; + case 1: + target[0] = wide; + } + + return count; +} + +size_t utf16_encode_char( unsigned cur_wchar, blargg_wchar_t * out ) +{ + if ( cur_wchar < 0x10000 ) + { + if ( out ) *out = (blargg_wchar_t) cur_wchar; return 1; + } + else if ( cur_wchar < ( 1 << 20 ) ) + { + unsigned c = cur_wchar - 0x10000; + //MSDN: + //The first (high) surrogate is a 16-bit code value in the range U+D800 to U+DBFF. The second (low) surrogate is a 16-bit code value in the range U+DC00 to U+DFFF. Using surrogates, Unicode can support over one million characters. For more details about surrogates, refer to The Unicode Standard, version 2.0. + if ( out ) + { + out[0] = ( blargg_wchar_t )( 0xD800 | ( 0x3FF & ( c >> 10 ) ) ); + out[1] = ( blargg_wchar_t )( 0xDC00 | ( 0x3FF & c ) ) ; + } + return 2; + } + else + { + if ( out ) *out = '?'; return 1; + } +} + +size_t utf16_decode_char( const blargg_wchar_t * p_source, unsigned * p_out, size_t p_source_length ) +{ + if ( p_source_length == 0 ) return 0; + else if ( p_source_length == 1 ) + { + *p_out = p_source[0]; + return 1; + } + else + { + size_t retval = 0; + unsigned decoded = p_source[0]; + if ( decoded != 0 ) + { + retval = 1; + if ( ( decoded & 0xFC00 ) == 0xD800 ) + { + unsigned low = p_source[1]; + if ( ( low & 0xFC00 ) == 0xDC00 ) + { + decoded = 0x10000 + ( ( ( decoded & 0x3FF ) << 10 ) | ( low & 0x3FF ) ); + retval = 2; + } + } + } + *p_out = decoded; + return retval; + } +} + +// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows. +char* blargg_to_utf8( const blargg_wchar_t* wpath ) +{ + if ( wpath == NULL ) + return NULL; + + size_t needed = 0; + size_t mmax = blargg_wcslen( wpath ); + if ( mmax <= 0 ) + return NULL; + + size_t ptr = 0; + while ( ptr < mmax ) + { + unsigned wide = 0; + size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + needed += utf8_encode_char( wide, 0 ); + } + if ( needed <= 0 ) + return NULL; + + char* path = (char*) calloc( needed + 1, 1 ); + if ( path == NULL ) + return NULL; + + ptr = 0; + size_t actual = 0; + while ( ptr < mmax && actual < needed ) + { + unsigned wide = 0; + size_t char_len = utf16_decode_char( wpath + ptr, &wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + actual += utf8_encode_char( wide, path + actual ); + } + + if ( actual == 0 ) + { + free( path ); + return NULL; + } + + assert( actual == needed ); + return path; +} + +// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows. +blargg_wchar_t* blargg_to_wide( const char* path ) +{ + if ( path == NULL ) + return NULL; + + size_t mmax = strlen( path ); + if ( mmax <= 0 ) + return NULL; + + size_t needed = 0; + size_t ptr = 0; + while ( ptr < mmax ) + { + unsigned wide = 0; + size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + needed += utf16_encode_char( wide, 0 ); + } + if ( needed <= 0 ) + return NULL; + + blargg_wchar_t* wpath = (blargg_wchar_t*) calloc( needed + 1, sizeof *wpath ); + if ( wpath == NULL ) + return NULL; + + ptr = 0; + size_t actual = 0; + while ( ptr < mmax && actual < needed ) + { + unsigned wide = 0; + size_t char_len = utf8_decode_char( path + ptr, wide, mmax - ptr ); + if ( !char_len ) break; + ptr += char_len; + actual += utf16_encode_char( wide, wpath + actual ); + } + if ( actual == 0 ) + { + free( wpath ); + return NULL; + } + + assert( actual == needed ); + return wpath; +} + BLARGG_NAMESPACE_END diff --git a/Frameworks/GME/gme/blargg_common.h b/Frameworks/GME/gme/blargg_common.h index 8b0e34284..7fa4b4f27 100644 --- a/Frameworks/GME/gme/blargg_common.h +++ b/Frameworks/GME/gme/blargg_common.h @@ -77,7 +77,7 @@ arithmetic on smaller types. */ #ifdef BLARGG_NAMESPACE #define BLARGG_NAMESPACE_BEGIN namespace BLARGG_NAMESPACE { #define BLARGG_NAMESPACE_END } - + BLARGG_NAMESPACE_BEGIN BLARGG_NAMESPACE_END using namespace BLARGG_NAMESPACE; @@ -173,21 +173,21 @@ class blargg_vector : public blargg_vector_ { public: blargg_vector() { init(); } ~blargg_vector() { clear(); } - + blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } - + T* begin() { return static_cast (begin_); } const T* begin() const { return static_cast (begin_); } - + T* end() { return static_cast (begin_) + size_; } const T* end() const { return static_cast (begin_) + size_; } - + T& operator [] ( size_t n ) { assert( n < size_ ); return static_cast (begin_) [n]; } - + const T& operator [] ( size_t n ) const { assert( n < size_ ); @@ -219,6 +219,25 @@ BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT #endif +#ifdef _WIN32 +typedef wchar_t blargg_wchar_t; +#elif defined(HAVE_STDINT_H) +#include +typedef uint16_t blargg_wchar_t; +#else +typedef unsigned short blargg_wchar_t; +#endif + +inline size_t blargg_wcslen( const blargg_wchar_t* str ) +{ + size_t length = 0; + while ( *str++ ) length++; + return length; +} + +char* blargg_to_utf8( const blargg_wchar_t* ); +blargg_wchar_t* blargg_to_wide( const char* ); + BLARGG_NAMESPACE_END #endif diff --git a/Frameworks/GME/gme/blargg_source.h b/Frameworks/GME/gme/blargg_source.h index a81673619..349e36fa3 100644 --- a/Frameworks/GME/gme/blargg_source.h +++ b/Frameworks/GME/gme/blargg_source.h @@ -130,6 +130,8 @@ BLARGG_DEF_MIN_MAX( int ) BLARGG_DEF_MIN_MAX( unsigned ) BLARGG_DEF_MIN_MAX( long ) BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( BOOST::int64_t ) +BLARGG_DEF_MIN_MAX( BOOST::uint64_t ) BLARGG_DEF_MIN_MAX( float ) BLARGG_DEF_MIN_MAX( double ) diff --git a/Frameworks/GME/gme/c140.h b/Frameworks/GME/gme/c140.h deleted file mode 100644 index 9345a38ed..000000000 --- a/Frameworks/GME/gme/c140.h +++ /dev/null @@ -1,58 +0,0 @@ -/* C140.h */ - -#pragma once - -#ifndef __OSDCOMM_H__ -#define __OSDCOMM_H__ -typedef unsigned char UINT8; /* unsigned 8bit */ -typedef unsigned short UINT16; /* unsigned 16bit */ -typedef unsigned int UINT32; /* unsigned 32bit */ -typedef signed char INT8; /* signed 8bit */ -typedef signed short INT16; /* signed 16bit */ -typedef signed int INT32; /* signed 32bit */ - -typedef INT32 stream_sample_t; -typedef UINT32 offs_t; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void c140_update(void * chip, stream_sample_t **outputs, int samples); -void * device_start_c140(int sample_rate, int clock, int banking_type); -void device_stop_c140(void * chip); -void device_reset_c140(void * chip); - -//READ8_DEVICE_HANDLER( c140_r ); -//WRITE8_DEVICE_HANDLER( c140_w ); -UINT8 c140_r(void * chip, offs_t offset); -void c140_w(void * chip, offs_t offset, UINT8 data); - -//void c140_set_base(device_t *device, void *base); -void c140_set_base(void *chip, void *base); - -enum -{ - C140_TYPE_SYSTEM2, - C140_TYPE_SYSTEM21_A, - C140_TYPE_SYSTEM21_B, - C140_TYPE_ASIC219 -}; - -/*typedef struct _c140_interface c140_interface; -struct _c140_interface { - int banking_type; -};*/ - - -void c140_write_rom(void * chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, - const UINT8* ROMData); - -void c140_set_mute_mask(void * chip, UINT32 MuteMask); - -#ifdef __cplusplus -} -#endif - -//DECLARE_LEGACY_SOUND_DEVICE(C140, c140); diff --git a/Frameworks/GME/gme/dac_control.h b/Frameworks/GME/gme/dac_control.h deleted file mode 100644 index 4cf75656a..000000000 --- a/Frameworks/GME/gme/dac_control.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _DAC_CONTROL_H_ -#define _DAC_CONTROL_H_ - -#ifndef __OSDCOMM_H__ -#define __OSDCOMM_H__ -typedef unsigned char UINT8; /* unsigned 8bit */ -typedef unsigned short UINT16; /* unsigned 16bit */ -typedef unsigned int UINT32; /* unsigned 32bit */ -typedef unsigned long long UINT64; /* unsigned 64bit */ -typedef signed char INT8; /* signed 8bit */ -typedef signed short INT16; /* signed 16bit */ -typedef signed int INT32; /* signed 32bit */ -typedef signed long long INT64; /* signed 64bit */ - -typedef INT32 stream_sample_t; -typedef UINT32 offs_t; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void * device_start_daccontrol(UINT32 samplerate, void *context); -void device_stop_daccontrol(void * chip); - -void daccontrol_update(void * chip, UINT32 base_clock, UINT32 samples); -void device_reset_daccontrol(void * chip); -void daccontrol_setup_chip(void * chip, UINT8 ChType, UINT8 ChNum, UINT16 Command); -void daccontrol_set_data(void *chip, const UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase); -void daccontrol_set_frequency(void *chip, UINT32 Frequency); -void daccontrol_start(void *chip, UINT32 DataPos, UINT8 LenMode, UINT32 Length); -void daccontrol_stop(void *chip); - -#define DCTRL_LMODE_IGNORE 0x00 -#define DCTRL_LMODE_CMDS 0x01 -#define DCTRL_LMODE_MSEC 0x02 -#define DCTRL_LMODE_TOEND 0x03 -#define DCTRL_LMODE_BYTES 0x0F - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Frameworks/GME/gme/dbopl.cpp b/Frameworks/GME/gme/dbopl.cpp deleted file mode 100644 index 3dbe669b2..000000000 --- a/Frameworks/GME/gme/dbopl.cpp +++ /dev/null @@ -1,1526 +0,0 @@ -/* - * Copyright (C) 2002-2009 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. - Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 - Except for the table generation it's all integer math - Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms - The generation was based on the MAME implementation but tried to have it use less memory and be faster in general - MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times - - //TODO Don't delay first operator 1 sample in opl3 mode - //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter - //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? - //TODO Check if having the same accuracy in all frequency multipliers sounds better or not - - //DUNNO Keyon in 4op, switch to 2op without keyoff. -*/ - -/* $Id$ */ - - -#include -#include -#include -//#include "dosbox.h" -#include "dbopl.h" - - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -namespace DBOPL { - -#define OPLRATE ((double)(14318180.0 / 288.0)) -#define TREMOLO_TABLE 52 - -//Try to use most precision for frequencies -//Else try to keep different waves in synch -//#define WAVE_PRECISION 1 -#ifndef WAVE_PRECISION -//Wave bits available in the top of the 32bit range -//Original adlib uses 10.10, we use 10.22 -#define WAVE_BITS 10 -#else -//Need some extra bits at the top to have room for octaves and frequency multiplier -//We support to 8 times lower rate -//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits -#define WAVE_BITS 14 -#endif -#define WAVE_SH ( 32 - WAVE_BITS ) -#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) - -//Use the same accuracy as the waves -#define LFO_SH ( WAVE_SH - 10 ) -//LFO is controlled by our tremolo 256 sample limit -#define LFO_MAX ( 256 << ( LFO_SH ) ) - - -//Maximum amount of attenuation bits -//Envelope goes to 511, 9 bits -#if (DBOPL_WAVE == WAVE_TABLEMUL ) -//Uses the value directly -#define ENV_BITS ( 9 ) -#else -//Add 3 bits here for more accuracy and would have to be shifted up either way -#define ENV_BITS ( 9 ) -#endif -//Limits of the envelope with those bits and when the envelope goes silent -#define ENV_MIN 0 -#define ENV_EXTRA ( ENV_BITS - 9 ) -#define ENV_MAX ( 511 << ENV_EXTRA ) -#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) -#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) - -//Attack/decay/release rate counter shift -#define RATE_SH 24 -#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) -//Has to fit within 16bit lookuptable -#define MUL_SH 16 - -//Check some ranges -#if ENV_EXTRA > 3 -#error Too many envelope bits -#endif - - -//How much to substract from the base value for the final attenuation -static const Bit8u KslCreateTable[16] = { - //0 will always be be lower than 7 * 8 - 64, 32, 24, 19, - 16, 12, 11, 10, - 8, 6, 5, 4, - 3, 2, 1, 0, -}; - -#define M(_X_) ((Bit8u)( (_X_) * 2)) -static const Bit8u FreqCreateTable[16] = { - M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), - M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) -}; -#undef M - -//We're not including the highest attack rate, that gets a special value -static const Bit8u AttackSamplesTable[13] = { - 69, 55, 46, 40, - 35, 29, 23, 20, - 19, 15, 11, 10, - 9 -}; -//On a real opl these values take 8 samples to reach and are based upon larger tables -static const Bit8u EnvelopeIncreaseTable[13] = { - 4, 5, 6, 7, - 8, 10, 12, 14, - 16, 20, 24, 28, - 32, -}; - -#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) -static Bit16u ExpTable[ 256 ]; -#endif - -#if ( DBOPL_WAVE == WAVE_HANDLER ) -//PI table used by WAVEHANDLER -static Bit16u SinTable[ 512 ]; -#endif - -#if ( DBOPL_WAVE > WAVE_HANDLER ) -//Layout of the waveform table in 512 entry intervals -//With overlapping waves we reduce the table to half it's size - -// | |//\\|____|WAV7|//__|/\ |____|/\/\| -// |\\//| | |WAV7| | \/| | | -// |06 |0126|17 |7 |3 |4 |4 5 |5 | - -//6 is just 0 shifted and masked - -static Bit16s WaveTable[ 8 * 512 ]; -//Distance into WaveTable the wave starts -static const Bit16u WaveBaseTable[8] = { - 0x000, 0x200, 0x200, 0x800, - 0xa00, 0xc00, 0x100, 0x400, - -}; -//Mask the counter with this -static const Bit16u WaveMaskTable[8] = { - 1023, 1023, 511, 511, - 1023, 1023, 512, 1023, -}; - -//Where to start the counter on at keyon -static const Bit16u WaveStartTable[8] = { - 512, 0, 0, 0, - 0, 512, 512, 256, -}; -#endif - -#if ( DBOPL_WAVE == WAVE_TABLEMUL ) -static Bit16u MulTable[ 384 ]; -#endif - -static Bit8u KslTable[ 8 * 16 ]; -static Bit8u TremoloTable[ TREMOLO_TABLE ]; -//Start of a channel behind the chip struct start -static Bit16u ChanOffsetTable[32]; -//Start of an operator behind the chip struct start -static Bit16u OpOffsetTable[64]; - -//The lower bits are the shift of the operator vibrato value -//The highest bit is right shifted to generate -1 or 0 for negation -//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 -static const Bit8s VibratoTable[ 8 ] = { - 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, - 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 -}; - -//Shift strength for the ksl value determined by ksl strength -static const Bit8u KslShiftTable[4] = { - 31,1,2,0 -}; - -//Generate a table index and table shift value using input value from a selected rate -static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) { - if ( val < 13 * 4 ) { //Rate 0 - 12 - shift = 12 - ( val >> 2 ); - index = val & 3; - } else if ( val < 15 * 4 ) { //rate 13 - 14 - shift = 0; - index = val - 12 * 4; - } else { //rate 15 and up - shift = 0; - index = 12; - } -} - -#if ( DBOPL_WAVE == WAVE_HANDLER ) -/* - Generate the different waveforms out of the sine/exponetial table using handlers -*/ -static inline Bits MakeVolume( Bitu wave, Bitu volume ) { - Bitu total = wave + volume; - Bitu index = total & 0xff; - Bitu sig = ExpTable[ index ]; - Bitu exp = total >> 8; -#if 0 - //Check if we overflow the 31 shift limit - if ( exp >= 32 ) { - LOG_MSG( "WTF %d %d", total, exp ); - } -#endif - return (sig >> exp); -}; - -static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { - Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 - Bitu wave = SinTable[i & 511]; - return (MakeVolume( wave, volume ) ^ neg) - neg; -} -static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { - Bit32u wave = SinTable[i & 511]; - wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { - Bitu wave = SinTable[i & 511]; - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { - Bitu wave = SinTable[i & 255]; - wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { - //Twice as fast - i <<= 1; - Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 - Bitu wave = SinTable[i & 511]; - wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); - return (MakeVolume( wave, volume ) ^ neg) - neg; -} -static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { - //Twice as fast - i <<= 1; - Bitu wave = SinTable[i & 511]; - wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); - return MakeVolume( wave, volume ); -} -static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { - Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 - return (MakeVolume( 0, volume ) ^ neg) - neg; -} -static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { - //Negative is reversed here - Bits neg = (( i >> 9) & 1) - 1; - Bitu wave = (i << 3); - //When negative the volume also runs backwards - wave = ((wave ^ neg) - neg) & 4095; - return (MakeVolume( wave, volume ) ^ neg) - neg; -} - -static const WaveHandler WaveHandlerTable[8] = { - WaveForm0, WaveForm1, WaveForm2, WaveForm3, - WaveForm4, WaveForm5, WaveForm6, WaveForm7 -}; - -#endif - -/* - Operator -*/ - -//We zero out when rate == 0 -inline void Operator::UpdateAttack( const Chip* chip ) { - Bit8u rate = reg60 >> 4; - if ( rate ) { - Bit8u val = (rate << 2) + ksr; - attackAdd = chip->attackRates[ val ]; - rateZero &= ~(1 << ATTACK); - } else { - attackAdd = 0; - rateZero |= (1 << ATTACK); - } -} -inline void Operator::UpdateDecay( const Chip* chip ) { - Bit8u rate = reg60 & 0xf; - if ( rate ) { - Bit8u val = (rate << 2) + ksr; - decayAdd = chip->linearRates[ val ]; - rateZero &= ~(1 << DECAY); - } else { - decayAdd = 0; - rateZero |= (1 << DECAY); - } -} -inline void Operator::UpdateRelease( const Chip* chip ) { - Bit8u rate = reg80 & 0xf; - if ( rate ) { - Bit8u val = (rate << 2) + ksr; - releaseAdd = chip->linearRates[ val ]; - rateZero &= ~(1 << RELEASE); - if ( !(reg20 & MASK_SUSTAIN ) ) { - rateZero &= ~( 1 << SUSTAIN ); - } - } else { - rateZero |= (1 << RELEASE); - releaseAdd = 0; - if ( !(reg20 & MASK_SUSTAIN ) ) { - rateZero |= ( 1 << SUSTAIN ); - } - } -} - -inline void Operator::UpdateAttenuation( ) { - Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff); - Bit32u tl = reg40 & 0x3f; - Bit8u kslShift = KslShiftTable[ reg40 >> 6 ]; - //Make sure the attenuation goes to the right bits - totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max - totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; -} - -void Operator::UpdateFrequency( ) { - Bit32u freq = chanData & (( 1 << 10 ) - 1); - Bit32u block = (chanData >> 10) & 0xff; - -#ifdef WAVE_PRECISION - block = 7 - block; - waveAdd = ( freq * freqMul ) >> block; -#else - waveAdd = (freq << block) * freqMul; -#endif - if ( reg20 & MASK_VIBRATO ) { - vibStrength = (Bit8u)(freq >> 7); -#ifdef WAVE_PRECISION - vibrato = ( vibStrength * freqMul ) >> block; -#else - vibrato = ( vibStrength << block ) * freqMul; -#endif - } else { - vibStrength = 0; - vibrato = 0; - } -} - -void Operator::UpdateRates( const Chip* chip ) { - //Mame seems to reverse this where enabling ksr actually lowers - //the rate, but pdf manuals says otherwise? - Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff); - if ( !( reg20 & MASK_KSR ) ) { - newKsr >>= 2; - } - if ( ksr == newKsr ) - return; - ksr = newKsr; - UpdateAttack( chip ); - UpdateDecay( chip ); - UpdateRelease( chip ); -} - -INLINE Bit32s Operator::RateForward( Bit32u add ) { - rateIndex += add; - Bit32s ret = rateIndex >> RATE_SH; - rateIndex = rateIndex & RATE_MASK; - return ret; -} - -template< Operator::State yes> -Bits Operator::TemplateVolume( ) { - Bit32s vol = volume; - Bit32s change; - switch ( yes ) { - case OFF: - return ENV_MAX; - case ATTACK: - change = RateForward( attackAdd ); - if ( !change ) - return vol; - vol += ( (~vol) * change ) >> 3; - if ( vol < ENV_MIN ) { - volume = ENV_MIN; - rateIndex = 0; - SetState( DECAY ); - return ENV_MIN; - } - break; - case DECAY: - vol += RateForward( decayAdd ); - if ( vol >= sustainLevel ) { - //Check if we didn't overshoot max attenuation, then just go off - if ( vol >= ENV_MAX ) { - volume = ENV_MAX; - SetState( OFF ); - return ENV_MAX; - } - //Continue as sustain - rateIndex = 0; - SetState( SUSTAIN ); - } - break; - case SUSTAIN: - if ( reg20 & MASK_SUSTAIN ) { - return vol; - } - //In sustain phase, but not sustaining, do regular release - case RELEASE: - vol += RateForward( releaseAdd );; - if ( vol >= ENV_MAX ) { - volume = ENV_MAX; - SetState( OFF ); - return ENV_MAX; - } - break; - } - volume = vol; - return vol; -} - -static const VolumeHandler VolumeHandlerTable[5] = { - &Operator::TemplateVolume< Operator::OFF >, - &Operator::TemplateVolume< Operator::RELEASE >, - &Operator::TemplateVolume< Operator::SUSTAIN >, - &Operator::TemplateVolume< Operator::DECAY >, - &Operator::TemplateVolume< Operator::ATTACK > -}; - -INLINE Bitu Operator::ForwardVolume() { - return currentLevel + (this->*volHandler)(); -} - - -INLINE Bitu Operator::ForwardWave() { - waveIndex += waveCurrent; - return waveIndex >> WAVE_SH; -} - -void Operator::Write20( const Chip* chip, Bit8u val ) { - Bit8u change = (reg20 ^ val ); - if ( !change ) - return; - reg20 = val; - //Shift the tremolo bit over the entire register, saved a branch, YES! - tremoloMask = (Bit8s)(val) >> 7; - tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); - //Update specific features based on changes - if ( change & MASK_KSR ) { - UpdateRates( chip ); - } - //With sustain enable the volume doesn't change - if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) { - rateZero |= ( 1 << SUSTAIN ); - } else { - rateZero &= ~( 1 << SUSTAIN ); - } - //Frequency multiplier or vibrato changed - if ( change & (0xf | MASK_VIBRATO) ) { - freqMul = chip->freqMul[ val & 0xf ]; - UpdateFrequency(); - } -} - -void Operator::Write40( const Chip* /*chip*/, Bit8u val ) { - if (!(reg40 ^ val )) - return; - reg40 = val; - UpdateAttenuation( ); -} - -void Operator::Write60( const Chip* chip, Bit8u val ) { - Bit8u change = reg60 ^ val; - reg60 = val; - if ( change & 0x0f ) { - UpdateDecay( chip ); - } - if ( change & 0xf0 ) { - UpdateAttack( chip ); - } -} - -void Operator::Write80( const Chip* chip, Bit8u val ) { - Bit8u change = (reg80 ^ val ); - if ( !change ) - return; - reg80 = val; - Bit8u sustain = val >> 4; - //Turn 0xf into 0x1f - sustain |= ( sustain + 1) & 0x10; - sustainLevel = sustain << ( ENV_BITS - 5 ); - if ( change & 0x0f ) { - UpdateRelease( chip ); - } -} - -void Operator::WriteE0( const Chip* chip, Bit8u val ) { - if ( !(regE0 ^ val) ) - return; - //in opl3 mode you can always selet 7 waveforms regardless of waveformselect - Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); - regE0 = val; -#if ( DBOPL_WAVE == WAVE_HANDLER ) - waveHandler = WaveHandlerTable[ waveForm ]; -#else - waveBase = WaveTable + WaveBaseTable[ waveForm ]; - waveStart = WaveStartTable[ waveForm ] << WAVE_SH; - waveMask = WaveMaskTable[ waveForm ]; -#endif -} - -INLINE void Operator::SetState( Bit8u s ) { - state = s; - volHandler = VolumeHandlerTable[ s ]; -} - -INLINE bool Operator::Silent() const { - if ( !ENV_SILENT( totalLevel + volume ) ) - return false; - if ( !(rateZero & ( 1 << state ) ) ) - return false; - return true; -} - - INLINE void Operator::Prepare( const Chip* chip ) { - currentLevel = totalLevel + (chip->tremoloValue & tremoloMask); - waveCurrent = waveAdd; - if ( vibStrength >> chip->vibratoShift ) { - Bit32s add = vibrato >> chip->vibratoShift; - //Sign extend over the shift value - Bit32s neg = chip->vibratoSign; - //Negate the add with -1 or 0 - add = ( add ^ neg ) - neg; - waveCurrent += add; - } -} - -void Operator::KeyOn( Bit8u mask ) { - if ( !keyOn ) { - //Restart the frequency generator -#if ( DBOPL_WAVE > WAVE_HANDLER ) - waveIndex = waveStart; -#else - waveIndex = 0; -#endif - rateIndex = 0; - SetState( ATTACK ); - } - keyOn |= mask; -} - -void Operator::KeyOff( Bit8u mask ) { - keyOn &= ~mask; - if ( !keyOn ) { - if ( state != OFF ) { - SetState( RELEASE ); - } - } -} - -INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) { -#if ( DBOPL_WAVE == WAVE_HANDLER ) - return waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); -#elif ( DBOPL_WAVE == WAVE_TABLEMUL ) - return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; -#elif ( DBOPL_WAVE == WAVE_TABLELOG ) - Bit32s wave = waveBase[ index & waveMask ]; - Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); - Bit32s sig = ExpTable[ total & 0xff ]; - Bit32u exp = total >> 8; - Bit32s neg = wave >> 16; - return ((sig ^ neg) - neg) >> exp; -#else -#error "No valid wave routine" -#endif -} - -Bits INLINE Operator::GetSample( Bits modulation ) { - Bitu vol = ForwardVolume(); - if ( ENV_SILENT( vol ) ) { - //Simply forward the wave - waveIndex += waveCurrent; - return 0; - } else { - Bitu index = ForwardWave(); - index += modulation; - return GetWave( index, vol ); - } -} - -Operator::Operator() { - chanData = 0; - freqMul = 0; - waveIndex = 0; - waveAdd = 0; - waveCurrent = 0; - keyOn = 0; - ksr = 0; - reg20 = 0; - reg40 = 0; - reg60 = 0; - reg80 = 0; - regE0 = 0; - SetState( OFF ); - rateZero = (1 << OFF); - sustainLevel = ENV_MAX; - currentLevel = ENV_MAX; - totalLevel = ENV_MAX; - volume = ENV_MAX; -} - -/* - Channel -*/ - -Channel::Channel() { - old[0] = old[1] = 0; - chanData = 0; - regB0 = 0; - regC0 = 0; - maskLeft = -1; - maskRight = -1; - feedback = 31; - fourMask = 0; - synthHandler = &Channel::BlockTemplate< sm2FM >; -} - -void Channel::SetChanData( const Chip* chip, Bit32u data ) { - Bit32u change = chanData ^ data; - chanData = data; - Op( 0 )->chanData = data; - Op( 1 )->chanData = data; - //Since a frequency update triggered this, always update frequency - Op( 0 )->UpdateFrequency(); - Op( 1 )->UpdateFrequency(); - if ( change & ( 0xff << SHIFT_KSLBASE ) ) { - Op( 0 )->UpdateAttenuation(); - Op( 1 )->UpdateAttenuation(); - } - if ( change & ( 0xff << SHIFT_KEYCODE ) ) { - Op( 0 )->UpdateRates( chip ); - Op( 1 )->UpdateRates( chip ); - } -} - -void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) { - //Extrace the frequency bits - Bit32u data = chanData & 0xffff; - Bit32u kslBase = KslTable[ data >> 6 ]; - Bit32u keyCode = ( data & 0x1c00) >> 9; - if ( chip->reg08 & 0x40 ) { - keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ - } else { - keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ - } - //Add the keycode and ksl into the highest bits of chanData - data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); - ( this + 0 )->SetChanData( chip, data ); - if ( fourOp & 0x3f ) { - ( this + 1 )->SetChanData( chip, data ); - } -} - -void Channel::WriteA0( const Chip* chip, Bit8u val ) { - Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; - //Don't handle writes to silent fourop channels - if ( fourOp > 0x80 ) - return; - Bit32u change = (chanData ^ val ) & 0xff; - if ( change ) { - chanData ^= change; - UpdateFrequency( chip, fourOp ); - } -} - -void Channel::WriteB0( const Chip* chip, Bit8u val ) { - Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask; - //Don't handle writes to silent fourop channels - if ( fourOp > 0x80 ) - return; - Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00; - if ( change ) { - chanData ^= change; - UpdateFrequency( chip, fourOp ); - } - //Check for a change in the keyon/off state - if ( !(( val ^ regB0) & 0x20)) - return; - regB0 = val; - if ( val & 0x20 ) { - Op(0)->KeyOn( 0x1 ); - Op(1)->KeyOn( 0x1 ); - if ( fourOp & 0x3f ) { - ( this + 1 )->Op(0)->KeyOn( 1 ); - ( this + 1 )->Op(1)->KeyOn( 1 ); - } - } else { - Op(0)->KeyOff( 0x1 ); - Op(1)->KeyOff( 0x1 ); - if ( fourOp & 0x3f ) { - ( this + 1 )->Op(0)->KeyOff( 1 ); - ( this + 1 )->Op(1)->KeyOff( 1 ); - } - } -} - -void Channel::WriteC0( const Chip* chip, Bit8u val ) { - Bit8u change = val ^ regC0; - if ( !change ) - return; - regC0 = val; - feedback = ( val >> 1 ) & 7; - if ( feedback ) { - //We shift the input to the right 10 bit wave index value - feedback = 9 - feedback; - } else { - feedback = 31; - } - //Select the new synth mode - if ( chip->opl3Active ) { - //4-op mode enabled for this channel - if ( (chip->reg104 & fourMask) & 0x3f ) { - Channel* chan0, *chan1; - //Check if it's the 2nd channel in a 4-op - if ( !(fourMask & 0x80 ) ) { - chan0 = this; - chan1 = this + 1; - } else { - chan0 = this - 1; - chan1 = this; - } - - Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); - switch ( synth ) { - case 0: - chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; - break; - case 1: - chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; - break; - case 2: - chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; - break; - case 3: - chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; - break; - } - //Disable updating percussion channels - } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { - - //Regular dual op, am or fm - } else if ( val & 1 ) { - synthHandler = &Channel::BlockTemplate< sm3AM >; - } else { - synthHandler = &Channel::BlockTemplate< sm3FM >; - } - maskLeft = ( val & 0x10 ) ? -1 : 0; - maskRight = ( val & 0x20 ) ? -1 : 0; - //opl2 active - } else { - //Disable updating percussion channels - if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { - - //Regular dual op, am or fm - } else if ( val & 1 ) { - synthHandler = &Channel::BlockTemplate< sm2AM >; - } else { - synthHandler = &Channel::BlockTemplate< sm2FM >; - } - } -} - -void Channel::ResetC0( const Chip* chip ) { - Bit8u val = regC0; - regC0 ^= 0xff; - WriteC0( chip, val ); -} - -template< bool opl3Mode> -void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) { - Channel* chan = this; - - //BassDrum - Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; - old[0] = old[1]; - old[1] = Op(0)->GetSample( mod ); - - //When bassdrum is in AM mode first operator is ignoed - if ( chan->regC0 & 1 ) { - mod = 0; - } else { - mod = old[0]; - } - Bit32s sample = Op(1)->GetSample( mod ); - - - //Precalculate stuff used by other outputs - Bit32u noiseBit = chip->ForwardNoise() & 0x1; - Bit32u c2 = Op(2)->ForwardWave(); - Bit32u c5 = Op(5)->ForwardWave(); - Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; - - //Hi-Hat - Bit32u hhVol = Op(2)->ForwardVolume(); - if ( !ENV_SILENT( hhVol ) ) { - Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); - sample += Op(2)->GetWave( hhIndex, hhVol ); - } - //Snare Drum - Bit32u sdVol = Op(3)->ForwardVolume(); - if ( !ENV_SILENT( sdVol ) ) { - Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); - sample += Op(3)->GetWave( sdIndex, sdVol ); - } - //Tom-tom - sample += Op(4)->GetSample( 0 ); - - //Top-Cymbal - Bit32u tcVol = Op(5)->ForwardVolume(); - if ( !ENV_SILENT( tcVol ) ) { - Bit32u tcIndex = (1 + phaseBit) << 8; - sample += Op(5)->GetWave( tcIndex, tcVol ); - } - sample <<= 1; - if ( opl3Mode ) { - output[0] += sample; - output[1] += sample; - } else { - output[0] += sample; - } -} - -template -Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { - switch( mode ) { - case sm2AM: - case sm3AM: - if ( Op(0)->Silent() && Op(1)->Silent() ) { - old[0] = old[1] = 0; - return (this + 1); - } - break; - case sm2FM: - case sm3FM: - if ( Op(1)->Silent() ) { - old[0] = old[1] = 0; - return (this + 1); - } - break; - case sm3FMFM: - if ( Op(3)->Silent() ) { - old[0] = old[1] = 0; - return (this + 2); - } - break; - case sm3AMFM: - if ( Op(0)->Silent() && Op(3)->Silent() ) { - old[0] = old[1] = 0; - return (this + 2); - } - break; - case sm3FMAM: - if ( Op(1)->Silent() && Op(3)->Silent() ) { - old[0] = old[1] = 0; - return (this + 2); - } - break; - case sm3AMAM: - if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) { - old[0] = old[1] = 0; - return (this + 2); - } - break; - default: - break; - } - //Init the operators with the the current vibrato and tremolo values - Op( 0 )->Prepare( chip ); - Op( 1 )->Prepare( chip ); - if ( mode > sm4Start ) { - Op( 2 )->Prepare( chip ); - Op( 3 )->Prepare( chip ); - } - if ( mode > sm6Start ) { - Op( 4 )->Prepare( chip ); - Op( 5 )->Prepare( chip ); - } - for ( Bitu i = 0; i < samples; i++ ) { - //Early out for percussion handlers - if ( mode == sm2Percussion ) { - GeneratePercussion( chip, output + i ); - continue; //Prevent some unitialized value bitching - } else if ( mode == sm3Percussion ) { - GeneratePercussion( chip, output + i * 2 ); - continue; //Prevent some unitialized value bitching - } - - //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise - Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback; - old[0] = old[1]; - old[1] = Op(0)->GetSample( mod ); - Bit32s sample; - Bit32s out0 = old[0]; - if ( mode == sm2AM || mode == sm3AM ) { - sample = out0 + Op(1)->GetSample( 0 ); - } else if ( mode == sm2FM || mode == sm3FM ) { - sample = Op(1)->GetSample( out0 ); - } else if ( mode == sm3FMFM ) { - Bits next = Op(1)->GetSample( out0 ); - next = Op(2)->GetSample( next ); - sample = Op(3)->GetSample( next ); - } else if ( mode == sm3AMFM ) { - sample = out0; - Bits next = Op(1)->GetSample( 0 ); - next = Op(2)->GetSample( next ); - sample += Op(3)->GetSample( next ); - } else if ( mode == sm3FMAM ) { - sample = Op(1)->GetSample( out0 ); - Bits next = Op(2)->GetSample( 0 ); - sample += Op(3)->GetSample( next ); - } else if ( mode == sm3AMAM ) { - sample = out0; - Bits next = Op(1)->GetSample( 0 ); - sample += Op(2)->GetSample( next ); - sample += Op(3)->GetSample( 0 ); - } - switch( mode ) { - case sm2AM: - case sm2FM: - output[ i ] += sample; - break; - case sm3AM: - case sm3FM: - case sm3FMFM: - case sm3AMFM: - case sm3FMAM: - case sm3AMAM: - output[ i * 2 + 0 ] += sample & maskLeft; - output[ i * 2 + 1 ] += sample & maskRight; - break; - default: - break; - } - } - switch( mode ) { - case sm2AM: - case sm2FM: - case sm3AM: - case sm3FM: - return ( this + 1 ); - case sm3FMFM: - case sm3AMFM: - case sm3FMAM: - case sm3AMAM: - return( this + 2 ); - case sm2Percussion: - case sm3Percussion: - return( this + 3 ); - } - return 0; -} - -/* - Chip -*/ - -Chip::Chip() { - reg08 = 0; - reg04 = 0; - regBD = 0; - reg104 = 0; - opl3Active = 0; -} - -INLINE Bit32u Chip::ForwardNoise() { - noiseCounter += noiseAdd; - Bitu count = noiseCounter >> LFO_SH; - noiseCounter &= WAVE_MASK; - for ( ; count > 0; --count ) { - //Noise calculation from mame - noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); - noiseValue >>= 1; - } - return noiseValue; -} - -Bit32u Chip::ForwardLFO( Bit32u samples ) { - //Current vibrato value, runs 4x slower than tremolo - vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; - vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; - tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; - - //Check hom many samples there can be done before the value changes - Bit32u todo = LFO_MAX - lfoCounter; - Bit32u count = (todo + lfoAdd - 1) / lfoAdd; - if ( count > samples ) { - count = samples; - lfoCounter += count * lfoAdd; - } else { - lfoCounter += count * lfoAdd; - lfoCounter &= (LFO_MAX - 1); - //Maximum of 7 vibrato value * 4 - vibratoIndex = ( vibratoIndex + 1 ) & 31; - //Clip tremolo to the the table size - if ( tremoloIndex + 1 < TREMOLO_TABLE ) - ++tremoloIndex; - else - tremoloIndex = 0; - } - return count; -} - -void Chip::WriteBD( Bit8u val ) { - Bit8u change = regBD ^ val; - if ( !change ) - return; - regBD = val; - //TODO could do this with shift and xor? - vibratoStrength = (val & 0x40) ? 0x00 : 0x01; - tremoloStrength = (val & 0x80) ? 0x00 : 0x02; - if ( val & 0x20 ) { - //Drum was just enabled, make sure channel 6 has the right synth - if ( change & 0x20 ) { - if ( opl3Active ) { - chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; - } else { - chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; - } - } - //Bass Drum - if ( val & 0x10 ) { - chan[6].op[0].KeyOn( 0x2 ); - chan[6].op[1].KeyOn( 0x2 ); - } else { - chan[6].op[0].KeyOff( 0x2 ); - chan[6].op[1].KeyOff( 0x2 ); - } - //Hi-Hat - if ( val & 0x1 ) { - chan[7].op[0].KeyOn( 0x2 ); - } else { - chan[7].op[0].KeyOff( 0x2 ); - } - //Snare - if ( val & 0x8 ) { - chan[7].op[1].KeyOn( 0x2 ); - } else { - chan[7].op[1].KeyOff( 0x2 ); - } - //Tom-Tom - if ( val & 0x4 ) { - chan[8].op[0].KeyOn( 0x2 ); - } else { - chan[8].op[0].KeyOff( 0x2 ); - } - //Top Cymbal - if ( val & 0x2 ) { - chan[8].op[1].KeyOn( 0x2 ); - } else { - chan[8].op[1].KeyOff( 0x2 ); - } - //Toggle keyoffs when we turn off the percussion - } else if ( change & 0x20 ) { - //Trigger a reset to setup the original synth handler - chan[6].ResetC0( this ); - chan[6].op[0].KeyOff( 0x2 ); - chan[6].op[1].KeyOff( 0x2 ); - chan[7].op[0].KeyOff( 0x2 ); - chan[7].op[1].KeyOff( 0x2 ); - chan[8].op[0].KeyOff( 0x2 ); - chan[8].op[1].KeyOff( 0x2 ); - } -} - - -#define REGOP( _FUNC_ ) \ - index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ - if ( OpOffsetTable[ index ] ) { \ - Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \ - regOp->_FUNC_( this, val ); \ - } - -#define REGCHAN( _FUNC_ ) \ - index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ - if ( ChanOffsetTable[ index ] ) { \ - Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \ - regChan->_FUNC_( this, val ); \ - } - -void Chip::WriteReg( Bit32u reg, Bit8u val ) { - Bitu index; - switch ( (reg & 0xf0) >> 4 ) { - case 0x00 >> 4: - if ( reg == 0x01 ) { - waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0; - } else if ( reg == 0x104 ) { - //Only detect changes in lowest 6 bits - if ( !((reg104 ^ val) & 0x3f) ) - return; - //Always keep the highest bit enabled, for checking > 0x80 - reg104 = 0x80 | ( val & 0x3f ); - } else if ( reg == 0x105 ) { - //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register - if ( !((opl3Active ^ val) & 1 ) ) - return; - opl3Active = ( val & 1 ) ? 0xff : 0; - //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers - for ( int i = 0; i < 18;i++ ) { - chan[i].ResetC0( this ); - } - } else if ( reg == 0x08 ) { - reg08 = val; - } - case 0x10 >> 4: - break; - case 0x20 >> 4: - case 0x30 >> 4: - REGOP( Write20 ); - break; - case 0x40 >> 4: - case 0x50 >> 4: - REGOP( Write40 ); - break; - case 0x60 >> 4: - case 0x70 >> 4: - REGOP( Write60 ); - break; - case 0x80 >> 4: - case 0x90 >> 4: - REGOP( Write80 ); - break; - case 0xa0 >> 4: - REGCHAN( WriteA0 ); - break; - case 0xb0 >> 4: - if ( reg == 0xbd ) { - WriteBD( val ); - } else { - REGCHAN( WriteB0 ); - } - break; - case 0xc0 >> 4: - REGCHAN( WriteC0 ); - case 0xd0 >> 4: - break; - case 0xe0 >> 4: - case 0xf0 >> 4: - REGOP( WriteE0 ); - break; - } -} - - -Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) { - switch ( port & 3 ) { - case 0: - return val; - case 2: - if ( opl3Active || (val == 0x05) ) - return 0x100 | val; - else - return val; - } - return 0; -} - -void Chip::GenerateBlock2( Bitu total, Bit32s* output ) { - while ( total > 0 ) { - Bit32u samples = ForwardLFO( total ); - for ( Bitu i = 0; i < samples; i++ ) { - output[i] = 0; - } - int count = 0; - for( Channel* ch = chan; ch < chan + 9; ) { - count++; - ch = (ch->*(ch->synthHandler))( this, samples, output ); - } - total -= samples; - output += samples; - } -} - -void Chip::GenerateBlock3( Bitu total, Bit32s* output ) { - while ( total > 0 ) { - Bit32u samples = ForwardLFO( total ); - for ( Bitu i = 0; i < samples; i++ ) { - output[i * 2 + 0 ] = 0; - output[i * 2 + 1 ] = 0; - } - int count = 0; - for( Channel* ch = chan; ch < chan + 18; ) { - count++; - ch = (ch->*(ch->synthHandler))( this, samples, output ); - } - total -= samples; - output += samples * 2; - } -} - -void Chip::Setup( Bit32u clock, Bit32u rate ) { - double original = (double)clock / 288.0; - double scale = original / (double)rate; - if (fabs(scale - 1.0) < 0.00001) - scale = 1.0; - - //Noise counter is run at the same precision as general waves - noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); - noiseCounter = 0; - noiseValue = 1; //Make sure it triggers the noise xor the first time - //The low frequency oscillation counter - //Every time his overflows vibrato and tremoloindex are increased - lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); - lfoCounter = 0; - vibratoIndex = 0; - tremoloIndex = 0; - - //With higher octave this gets shifted up - //-1 since the freqCreateTable = *2 -#ifdef WAVE_PRECISION - double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); - for ( int i = 0; i < 16; i++ ) { - //Use rounding with 0.5 - freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] ); - } -#else - Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); - for ( int i = 0; i < 16; i++ ) { - freqMul[i] = freqScale * FreqCreateTable[ i ]; - } -#endif - - //-3 since the real envelope takes 8 steps to reach the single value we supply - for ( Bit8u i = 0; i < 76; i++ ) { - Bit8u index, shift; - EnvelopeSelect( i, index, shift ); - linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); - } - //Generate the best matching attack rate - for ( Bit8u i = 0; i < 62; i++ ) { - Bit8u index, shift; - EnvelopeSelect( i, index, shift ); - //Original amount of samples the attack would take - Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale); - - Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 ))); - Bit32s bestAdd = guessAdd; - Bit32u bestDiff = 1 << 30; - for( Bit32u passes = 0; passes < 16; passes ++ ) { - Bit32s volume = ENV_MAX; - Bit32s samples = 0; - Bit32u count = 0; - while ( volume > 0 && samples < original * 2 ) { - count += guessAdd; - Bit32s change = count >> RATE_SH; - count &= RATE_MASK; - if ( change ) { - volume += ( ~volume * change ) >> 3; - } - samples++; - - } - Bit32s diff = original - samples; - Bit32u lDiff = labs( diff ); - //Init last on first pass - if ( lDiff < bestDiff ) { - bestDiff = lDiff; - bestAdd = guessAdd; - if ( !bestDiff ) - break; - } - //Below our target - if ( diff < 0 ) { - //Better than the last time - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = ((guessAdd * mul) >> 12); - guessAdd++; - } else if ( diff > 0 ) { - Bit32s mul = ((original - diff) << 12) / original; - guessAdd = (guessAdd * mul) >> 12; - guessAdd--; - } - } - attackRates[i] = bestAdd; - } - for ( Bit8u i = 62; i < 76; i++ ) { - //This should provide instant volume maximizing - attackRates[i] = 8 << RATE_SH; - } - //Setup the channels with the correct four op flags - //Channels are accessed through a table so they appear linear here - chan[ 0].fourMask = 0x00 | ( 1 << 0 ); - chan[ 1].fourMask = 0x80 | ( 1 << 0 ); - chan[ 2].fourMask = 0x00 | ( 1 << 1 ); - chan[ 3].fourMask = 0x80 | ( 1 << 1 ); - chan[ 4].fourMask = 0x00 | ( 1 << 2 ); - chan[ 5].fourMask = 0x80 | ( 1 << 2 ); - - chan[ 9].fourMask = 0x00 | ( 1 << 3 ); - chan[10].fourMask = 0x80 | ( 1 << 3 ); - chan[11].fourMask = 0x00 | ( 1 << 4 ); - chan[12].fourMask = 0x80 | ( 1 << 4 ); - chan[13].fourMask = 0x00 | ( 1 << 5 ); - chan[14].fourMask = 0x80 | ( 1 << 5 ); - - //mark the percussion channels - chan[ 6].fourMask = 0x40; - chan[ 7].fourMask = 0x40; - chan[ 8].fourMask = 0x40; - - //Clear Everything in opl3 mode - WriteReg( 0x105, 0x1 ); - for ( int i = 0; i < 512; i++ ) { - if ( i == 0x105 ) - continue; - WriteReg( i, 0xff ); - WriteReg( i, 0x0 ); - } - WriteReg( 0x105, 0x0 ); - //Clear everything in opl2 mode - for ( int i = 0; i < 256; i++ ) { - WriteReg( i, 0xff ); - WriteReg( i, 0x0 ); - } -} - -static bool doneTables = false; -void InitTables( void ) { - if ( doneTables ) - return; - doneTables = true; -#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) - //Exponential volume table, same as the real adlib - for ( int i = 0; i < 256; i++ ) { - //Save them in reverse - ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); - ExpTable[i] += 1024; //or remove the -1 oh well :) - //Preshift to the left once so the final volume can shift to the right - ExpTable[i] *= 2; - } -#endif -#if ( DBOPL_WAVE == WAVE_HANDLER ) - //Add 0.5 for the trunc rounding of the integer cast - //Do a PI sinetable instead of the original 0.5 PI - for ( int i = 0; i < 512; i++ ) { - SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); - } -#endif -#if ( DBOPL_WAVE == WAVE_TABLEMUL ) - //Multiplication based tables - for ( int i = 0; i < 384; i++ ) { - int s = i * 8; - //TODO maybe keep some of the precision errors of the original table? - double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); - MulTable[i] = (Bit16u)(val); - } - - //Sine Wave Base - for ( int i = 0; i < 512; i++ ) { - WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); - WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; - } - //Exponential wave - for ( int i = 0; i < 256; i++ ) { - WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); - WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; - } -#endif -#if ( DBOPL_WAVE == WAVE_TABLELOG ) - //Sine Wave Base - for ( int i = 0; i < 512; i++ ) { - WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); - WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i]; - } - //Exponential wave - for ( int i = 0; i < 256; i++ ) { - WaveTable[ 0x700 + i ] = i * 8; - WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8; - } -#endif - - // | |//\\|____|WAV7|//__|/\ |____|/\/\| - // |\\//| | |WAV7| | \/| | | - // |06 |0126|27 |7 |3 |4 |4 5 |5 | - -#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) - for ( int i = 0; i < 256; i++ ) { - //Fill silence gaps - WaveTable[ 0x400 + i ] = WaveTable[0]; - WaveTable[ 0x500 + i ] = WaveTable[0]; - WaveTable[ 0x900 + i ] = WaveTable[0]; - WaveTable[ 0xc00 + i ] = WaveTable[0]; - WaveTable[ 0xd00 + i ] = WaveTable[0]; - //Replicate sines in other pieces - WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; - //double speed sines - WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; - WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; - WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; - WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; - } -#endif - - //Create the ksl table - for ( int oct = 0; oct < 8; oct++ ) { - int base = oct * 8; - for ( int i = 0; i < 16; i++ ) { - int val = base - KslCreateTable[i]; - if ( val < 0 ) - val = 0; - //*4 for the final range to match attenuation range - KslTable[ oct * 16 + i ] = val * 4; - } - } - //Create the Tremolo table, just increase and decrease a triangle wave - for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) { - Bit8u val = i << ENV_EXTRA; - TremoloTable[i] = val; - TremoloTable[TREMOLO_TABLE - 1 - i] = val; - } - //Create a table with offsets of the channels from the start of the chip - DBOPL::Chip* chip = 0; - for ( Bitu i = 0; i < 32; i++ ) { - Bitu index = i & 0xf; - if ( index >= 9 ) { - ChanOffsetTable[i] = 0; - continue; - } - //Make sure the four op channels follow eachother - if ( index < 6 ) { - index = (index % 3) * 2 + ( index / 3 ); - } - //Add back the bits for highest ones - if ( i >= 16 ) - index += 9; - Bitu blah = static_cast( reinterpret_cast( &(chip->chan[ index ]) ) ); - ChanOffsetTable[i] = blah; - } - //Same for operators - for ( Bitu i = 0; i < 64; i++ ) { - if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { - OpOffsetTable[i] = 0; - continue; - } - Bitu chNum = (i / 8) * 3 + (i % 8) % 3; - //Make sure we use 16 and up for the 2nd range to match the chanoffset gap - if ( chNum >= 12 ) - chNum += 16 - 12; - Bitu opNum = ( i % 8 ) / 3; - DBOPL::Channel* chan = 0; - Bitu blah = static_cast( reinterpret_cast ( &(chan->op[opNum]) ) ); - OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah; - } -#if 0 - //Stupid checks if table's are correct - for ( Bitu i = 0; i < 18; i++ ) { - Bit32u find = (Bit16u)( &(chip->chan[ i ]) ); - for ( Bitu c = 0; c < 32; c++ ) { - if ( ChanOffsetTable[c] == find ) { - find = 0; - break; - } - } - if ( find ) { - find = find; - } - } - for ( Bitu i = 0; i < 36; i++ ) { - Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) ); - for ( Bitu c = 0; c < 64; c++ ) { - if ( OpOffsetTable[c] == find ) { - find = 0; - break; - } - } - if ( find ) { - find = find; - } - } -#endif -} - -/*Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) { - return chip.WriteAddr( port, val ); - -} -void Handler::WriteReg( Bit32u addr, Bit8u val ) { - chip.WriteReg( addr, val ); -} - -void Handler::Generate( MixerChannel* chan, Bitu samples ) { - Bit32s buffer[ 512 * 2 ]; - if ( samples > 512 ) - samples = 512; - if ( !chip.opl3Active ) { - chip.GenerateBlock2( samples, buffer ); - chan->AddSamples_m32( samples, buffer ); - } else { - chip.GenerateBlock3( samples, buffer ); - chan->AddSamples_s32( samples, buffer ); - } -} - -void Handler::Init( Bitu rate ) { - InitTables(); - chip.Setup( rate ); -}*/ - -static class init { -public: - init() { InitTables(); } -} init_stuff; - - -} //Namespace DBOPL diff --git a/Frameworks/GME/gme/dbopl.h b/Frameworks/GME/gme/dbopl.h deleted file mode 100644 index 7e24cc10e..000000000 --- a/Frameworks/GME/gme/dbopl.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2002-2009 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifdef _WIN32 -#define INLINE __forceinline -#define DB_FASTCALL __fastcall -#else -#define INLINE inline -#define DB_FASTCALL __attribute__((fastcall)) -#endif - -typedef double Real64; -/* The internal types */ - -#ifdef HAVE_STDINT_H -#include - -typedef uint8_t Bit8u; -typedef int8_t Bit8s; -typedef uint16_t Bit16u; -typedef int16_t Bit16s; -typedef uint32_t Bit32u; -typedef int32_t Bit32s; -typedef uint64_t Bit64u; -typedef int64_t Bit64s; -#else -typedef unsigned char Bit8u; -typedef signed char Bit8s; -typedef unsigned short Bit16u; -typedef signed short Bit16s; -typedef unsigned long Bit32u; -typedef signed long Bit32s; -typedef unsigned __int64 Bit64u; -typedef signed __int64 Bit64s; -#endif - -typedef unsigned int Bitu; -typedef signed int Bits; - -#include "adlib.h" -//#include "dosbox.h" - -//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume -#define WAVE_HANDLER 10 -//Use a logarithmic wavetable with an exponential table for volume -#define WAVE_TABLELOG 11 -//Use a linear wavetable with a multiply table for volume -#define WAVE_TABLEMUL 12 - -//Select the type of wave generator routine -#define DBOPL_WAVE WAVE_TABLEMUL - -namespace DBOPL { - -struct Chip; -struct Operator; -struct Channel; - -#if (DBOPL_WAVE == WAVE_HANDLER) -typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); -#endif - -typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); -typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output ); - -//Different synth modes that can generate blocks of data -typedef enum { - sm2AM, - sm2FM, - sm3AM, - sm3FM, - sm4Start, - sm3FMFM, - sm3AMFM, - sm3FMAM, - sm3AMAM, - sm6Start, - sm2Percussion, - sm3Percussion -} SynthMode; - -//Shifts for the values contained in chandata variable -enum { - SHIFT_KSLBASE = 16, - SHIFT_KEYCODE = 24 -}; - -struct Operator { -public: - //Masks for operator 20 values - enum { - MASK_KSR = 0x10, - MASK_SUSTAIN = 0x20, - MASK_VIBRATO = 0x40, - MASK_TREMOLO = 0x80 - }; - - typedef enum { - OFF, - RELEASE, - SUSTAIN, - DECAY, - ATTACK - } State; - - VolumeHandler volHandler; - -#if (DBOPL_WAVE == WAVE_HANDLER) - WaveHandler waveHandler; //Routine that generate a wave -#else - Bit16s* waveBase; - Bit32u waveMask; - Bit32u waveStart; -#endif - Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index - Bit32u waveAdd; //The base frequency without vibrato - Bit32u waveCurrent; //waveAdd + vibratao - - Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this - Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove? - Bit32u vibrato; //Scaled up vibrato strength - Bit32s sustainLevel; //When stopping at sustain level stop here - Bit32s totalLevel; //totalLevel is added to every generated volume - Bit32u currentLevel; //totalLevel + tremolo - Bit32s volume; //The currently active volume - - Bit32u attackAdd; //Timers for the different states of the envelope - Bit32u decayAdd; - Bit32u releaseAdd; - Bit32u rateIndex; //Current position of the evenlope - - Bit8u rateZero; //Bits for the different states of the envelope having no changes - Bit8u keyOn; //Bitmask of different values that can generate keyon - //Registers, also used to check for changes - Bit8u reg20, reg40, reg60, reg80, regE0; - //Active part of the envelope we're in - Bit8u state; - //0xff when tremolo is enabled - Bit8u tremoloMask; - //Strength of the vibrato - Bit8u vibStrength; - //Keep track of the calculated KSR so we can check for changes - Bit8u ksr; -private: - void SetState( Bit8u s ); - void UpdateAttack( const Chip* chip ); - void UpdateRelease( const Chip* chip ); - void UpdateDecay( const Chip* chip ); -public: - void UpdateAttenuation(); - void UpdateRates( const Chip* chip ); - void UpdateFrequency( ); - - void Write20( const Chip* chip, Bit8u val ); - void Write40( const Chip* chip, Bit8u val ); - void Write60( const Chip* chip, Bit8u val ); - void Write80( const Chip* chip, Bit8u val ); - void WriteE0( const Chip* chip, Bit8u val ); - - bool Silent() const; - void Prepare( const Chip* chip ); - - void KeyOn( Bit8u mask); - void KeyOff( Bit8u mask); - - template< State state> - Bits TemplateVolume( ); - - Bit32s RateForward( Bit32u add ); - Bitu ForwardWave(); - Bitu ForwardVolume(); - - Bits GetSample( Bits modulation ); - Bits GetWave( Bitu index, Bitu vol ); -public: - Operator(); -}; - -struct Channel { - Operator op[2]; - inline Operator* Op( Bitu index ) { - return &( ( this + (index >> 1) )->op[ index & 1 ]); - } - SynthHandler synthHandler; - Bit32u chanData; //Frequency/octave and derived values - Bit32s old[2]; //Old data for feedback - - Bit8u feedback; //Feedback shift - Bit8u regB0; //Register values to check for changes - Bit8u regC0; - //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel - Bit8u fourMask; - Bit8s maskLeft; //Sign extended values for both channel's panning - Bit8s maskRight; - - //Forward the channel data to the operators of the channel - void SetChanData( const Chip* chip, Bit32u data ); - //Change in the chandata, check for new values and if we have to forward to operators - void UpdateFrequency( const Chip* chip, Bit8u fourOp ); - void WriteA0( const Chip* chip, Bit8u val ); - void WriteB0( const Chip* chip, Bit8u val ); - void WriteC0( const Chip* chip, Bit8u val ); - void ResetC0( const Chip* chip ); - - //call this for the first channel - template< bool opl3Mode > - void GeneratePercussion( Chip* chip, Bit32s* output ); - - //Generate blocks of data in specific modes - template - Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ); - Channel(); -}; - -struct Chip { - //This is used as the base counter for vibrato and tremolo - Bit32u lfoCounter; - Bit32u lfoAdd; - - - Bit32u noiseCounter; - Bit32u noiseAdd; - Bit32u noiseValue; - - //Frequency scales for the different multiplications - Bit32u freqMul[16]; - //Rates for decay and release for rate of this chip - Bit32u linearRates[76]; - //Best match attack rates for the rate of this chip - Bit32u attackRates[76]; - - //18 channels with 2 operators each - Channel chan[18]; - - Bit8u reg104; - Bit8u reg08; - Bit8u reg04; - Bit8u regBD; - Bit8u vibratoIndex; - Bit8u tremoloIndex; - Bit8s vibratoSign; - Bit8u vibratoShift; - Bit8u tremoloValue; - Bit8u vibratoStrength; - Bit8u tremoloStrength; - //Mask for allowed wave forms - Bit8u waveFormMask; - //0 or -1 when enabled - Bit8s opl3Active; - - //Return the maximum amount of samples before and LFO change - Bit32u ForwardLFO( Bit32u samples ); - Bit32u ForwardNoise(); - - void WriteBD( Bit8u val ); - void WriteReg(Bit32u reg, Bit8u val ); - - Bit32u WriteAddr( Bit32u port, Bit8u val ); - - void GenerateBlock2( Bitu samples, Bit32s* output ); - void GenerateBlock3( Bitu samples, Bit32s* output ); - - void Setup( Bit32u c, Bit32u r ); - - Chip(); -}; - -/*struct Handler : public Adlib::Handler { - DBOPL::Chip chip; - virtual Bit32u WriteAddr( Bit32u port, Bit8u val ); - virtual void WriteReg( Bit32u addr, Bit8u val ); - virtual void Generate( MixerChannel* chan, Bitu samples ); - virtual void Init( Bitu rate ); -};*/ - - -} //Namespace diff --git a/Frameworks/GME/gme/divfix.h b/Frameworks/GME/gme/divfix.h deleted file mode 100644 index 21212df3f..000000000 --- a/Frameworks/GME/gme/divfix.h +++ /dev/null @@ -1,18 +0,0 @@ - -static Uint32 DivFix(Uint32 p1, Uint32 p2, Uint32 fix) -{ - Uint32 ret; - ret = p1 / p2; - p1 = p1 % p2;/* p1 = p1 - p2 * ret; */ - while (fix--) - { - p1 += p1; - ret += ret; - if (p1 >= p2) - { - p1 -= p2; - ret++; - } - } - return ret; -} diff --git a/Frameworks/GME/gme/emuconfig.h b/Frameworks/GME/gme/emuconfig.h deleted file mode 100644 index 9250e1cf9..000000000 --- a/Frameworks/GME/gme/emuconfig.h +++ /dev/null @@ -1,78 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// Configuration for emulation libraries -// -///////////////////////////////////////////////////////////////////////////// - -#ifndef __EMUCONFIG_H__ -#define __EMUCONFIG_H__ - -///////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include - -///////////////////////////////////////////////////////////////////////////// -// -// WIN32 native project definitions -// -///////////////////////////////////////////////////////////////////////////// -#if defined(WIN32) && !defined(__GNUC__) - -#define EMU_CALL __fastcall -#define EMU_CALL_ __cdecl -#define EMU_INLINE __inline - -#define uint8 unsigned char -#define uint16 unsigned short -#define uint32 unsigned int -#define uint64 unsigned __int64 -#define sint8 signed char -#define sint16 signed short -#define sint32 signed int -#define sint64 signed __int64 - -///////////////////////////////////////////////////////////////////////////// -// -// LINUX / other platform definitions -// -///////////////////////////////////////////////////////////////////////////// -#else - -//#if defined(__GNUC__) && defined(__i386__) -//#define EMU_CALL __attribute__((__regparm__(2))) -//#else -#define EMU_CALL -//#endif - -#define EMU_CALL_ -#define EMU_INLINE __inline - -#ifdef HAVE_STDINT_H -#include -#define uint8 uint8_t -#define uint16 uint16_t -#define uint32 uint32_t -#define uint64 uint64_t -#define sint8 int8_t -#define sint16 int16_t -#define sint32 int32_t -#define sint64 int64_t -#else -#define uint8 unsigned char -#define uint16 unsigned short -#define uint32 unsigned int -#define uint64 unsigned long long -#define sint8 signed char -#define sint16 signed short -#define sint32 signed int -#define sint64 signed long long -#endif - -#endif - -///////////////////////////////////////////////////////////////////////////// - -#endif diff --git a/Frameworks/GME/gme/i_fmpac.h b/Frameworks/GME/gme/i_fmpac.h deleted file mode 100644 index fecba35d8..000000000 --- a/Frameworks/GME/gme/i_fmpac.h +++ /dev/null @@ -1,38 +0,0 @@ - 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x33, 0x21, 0x09, 0x0E, 0x94, 0x90, 0x48, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x41, 0x0F, 0x0D, 0xCE, 0xD3, 0x43, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x12, 0x1B, 0x06, 0xFF, 0xD2, 0x00, 0x32, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x20, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x10, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x21, 0x20, 0x1F, 0xC0, 0x71, 0x07, 0x47, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x21, 0x28, 0x07, 0xFF, 0x83, 0x02, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x97, 0x81, 0x25, 0x07, 0xCF, 0xC8, 0x02, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x21, 0x54, 0x0F, 0x80, 0x7F, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x56, 0x03, 0xD3, 0xB2, 0x43, 0x58, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x21, 0x0C, 0x03, 0x82, 0xC0, 0x40, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x01, 0x0C, 0x03, 0xD4, 0xD3, 0x40, 0x84, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x22, 0x00, 0x00, 0xA8, 0xF8, 0xF8, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x18, 0x00, 0x00, 0xF8, 0xA9, 0xF8, 0x55, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/Frameworks/GME/gme/i_fmunit.h b/Frameworks/GME/gme/i_fmunit.h deleted file mode 100644 index 3030810e5..000000000 --- a/Frameworks/GME/gme/i_fmunit.h +++ /dev/null @@ -1,38 +0,0 @@ - 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x61, 0x1E, 0x07, 0xF0, 0x7E, 0x07, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x41, 0x0F, 0x1D, 0xCE, 0xD2, 0x43, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x01, 0x99, 0x04, 0xFF, 0xC3, 0x03, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x40, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x61, 0x1D, 0x07, 0x32, 0x81, 0x10, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x21, 0x2D, 0x16, 0xC0, 0x70, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x21, 0x1B, 0x06, 0x64, 0x65, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x61, 0x0C, 0x18, 0x85, 0xA0, 0x79, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x21, 0x87, 0x11, 0xF0, 0xA4, 0x00, 0xF7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x97, 0xE1, 0x28, 0x07, 0xFF, 0xF3, 0x02, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x10, 0x0C, 0x05, 0xF2, 0xC4, 0x40, 0xC8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x56, 0x03, 0xB4, 0xB2, 0x23, 0x58, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x41, 0x89, 0x03, 0xF1, 0xF4, 0xF0, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0F, 0x02, 0x00, 0x00, 0xA7, 0xF7, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x1F, 0x00, 0x00, 0xF8, 0xA9, 0x08, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/Frameworks/GME/gme/i_vrc7.h b/Frameworks/GME/gme/i_vrc7.h deleted file mode 100644 index 56bbfa9d4..000000000 --- a/Frameworks/GME/gme/i_vrc7.h +++ /dev/null @@ -1,38 +0,0 @@ - 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x61, 0x1E, 0x07, 0xF0, 0x7E, 0x07, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x41, 0x0F, 0x1D, 0xCE, 0xD2, 0x43, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x01, 0x99, 0x04, 0xFF, 0xC3, 0x03, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x40, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x61, 0x1D, 0x07, 0x32, 0x81, 0x10, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x21, 0x2D, 0x16, 0xC0, 0x70, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x21, 0x1B, 0x06, 0x64, 0x65, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x61, 0x0C, 0x18, 0x85, 0xA0, 0x79, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x21, 0x87, 0x11, 0xF0, 0xA4, 0x00, 0xF7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x97, 0xE1, 0x28, 0x07, 0xFF, 0xF3, 0x02, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x10, 0x0C, 0x05, 0xF2, 0xC4, 0x40, 0xC8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x56, 0x03, 0xB4, 0xB2, 0x23, 0x58, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x41, 0x89, 0x03, 0xF1, 0xF4, 0xF0, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x21, 0x00, 0x00, 0xA8, 0xF8, 0xF8, 0xF8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x18, 0x00, 0x00, 0xF8, 0xA9, 0xF8, 0x55, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/Frameworks/GME/gme/k051649.c b/Frameworks/GME/gme/k051649.c deleted file mode 100644 index 599d9ecd2..000000000 --- a/Frameworks/GME/gme/k051649.c +++ /dev/null @@ -1,298 +0,0 @@ -/*************************************************************************** - - Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber - - This file is pieced together by Bryan McPhail from a combination of - Namco Sound, Amuse by Cab, Haunted Castle schematics and whoever first - figured out SCC! - - The 051649 is a 5 channel sound generator, each channel gets it's - waveform from RAM (32 bytes per waveform, 8 bit signed data). - - This sound chip is the same as the sound chip in some Konami - megaROM cartridges for the MSX. It is actually well researched - and documented: - - http://www.msxnet.org/tech/scc - - Thanks to Sean Young (sean@mess.org) for some bugfixes. - - K052539 is equivalent to this chip except channel 5 does not share - waveforms with channel 4. - -***************************************************************************/ - -#include "mamedef.h" -#include -#include -//#include "emu.h" -//#include "streams.h" -#include "k051649.h" - -#define FREQBASEBITS 16 - -/* this structure defines the parameters for a channel */ -typedef struct -{ - unsigned long counter; - int frequency; - int volume; - int key; - signed char waveform[32]; /* 19991207.CAB */ - UINT8 Muted; -} k051649_sound_channel; - -typedef struct _k051649_state k051649_state; -struct _k051649_state -{ - k051649_sound_channel channel_list[5]; - - /* global sound parameters */ - //sound_stream * stream; - int mclock,rate; - - /* mixer tables and internal buffers */ - INT16 *mixer_table; - INT16 *mixer_lookup; - short *mixer_buffer; - - int f[10]; - int cur_reg; -}; - -/*INLINE k051649_state *get_safe_token(running_device *device) -{ - assert(device != NULL); - assert(device->type() == K051649); - return (k051649_state *)downcast(device)->token(); -}*/ - -/* build a table to divide by the number of voices */ -static void make_mixer_table(/*running_machine *machine,*/ k051649_state *info, int voices) -{ - int count = voices * 256; - int i; - int gain = 8; - - /* allocate memory */ - //info->mixer_table = auto_alloc_array(machine, INT16, 512 * voices); - info->mixer_table = (INT16*)malloc(sizeof(INT16) * 512 * voices); - - /* find the middle of the table */ - info->mixer_lookup = info->mixer_table + (256 * voices); - - /* fill in the table - 16 bit case */ - for (i = 0; i < count; i++) - { - int val = i * gain * 16 / voices; - if (val > 32767) val = 32767; - info->mixer_lookup[ i] = val; - info->mixer_lookup[-i] = -val; - } -} - - -/* generate sound to the mix buffer */ -//static STREAM_UPDATE( k051649_update ) -void k051649_update(void *chip, stream_sample_t **outputs, int samples) -{ - k051649_state *info = (k051649_state *) chip; - k051649_sound_channel *voice=info->channel_list; - stream_sample_t *buffer = outputs[0]; - stream_sample_t *buffer2 = outputs[1]; - short *mix; - int i,v,f,j,k; - - /* zap the contents of the mixer buffer */ - memset(info->mixer_buffer, 0, samples * sizeof(short)); - - for (j=0; j<5; j++) { - v=voice[j].volume; - f=voice[j].frequency; - k=voice[j].key; - /* SY 20040109: the SCC produces no sound for freq < 9 */ - if (v && f > 8 && k && ! voice[j].Muted) - { - const signed char *w = voice[j].waveform; /* 19991207.CAB */ - int c=voice[j].counter; - - mix = info->mixer_buffer; - - /* add our contribution */ - for (i = 0; i < samples; i++) - { - int offs; - - /* Amuse source: Cab suggests this method gives greater resolution */ - /* Sean Young 20010417: the formula is really: f = clock/(16*(f+1))*/ - c+=(long)((((float)info->mclock / (float)((f+1) * 16))*(float)(1<rate / 32)); - offs = (c >> 16) & 0x1f; - *mix++ += (w[offs] * v)>>3; - } - - /* update the counter for this voice */ - voice[j].counter = c; - } - } - - /* mix it down */ - mix = info->mixer_buffer; - for (i = 0; i < samples; i++) - *buffer++ = *buffer2++ = info->mixer_lookup[*mix++]; -} - -//static DEVICE_START( k051649 ) -void * device_start_k051649(int clock) -{ - //k051649_state *info = get_safe_token(device); - k051649_state *info; - UINT8 CurChn; - - info = (k051649_state *) calloc(1, sizeof(k051649_state)); - /* get stream channels */ - //info->rate = device->clock()/16; - info->rate = clock/16; - //info->stream = stream_create(device, 0, 1, info->rate, info, k051649_update); - //info->mclock = device->clock(); - info->mclock = clock; - - /* allocate a buffer to mix into - 1 second's worth should be more than enough */ - //info->mixer_buffer = auto_alloc_array(device->machine, short, 2 * info->rate); - info->mixer_buffer = (short*)malloc(sizeof(short) * info->rate); - - /* build the mixer table */ - //make_mixer_table(device->machine, info, 5); - make_mixer_table(info, 5); - - for (CurChn = 0; CurChn < 5; CurChn ++) - info->channel_list[CurChn].Muted = 0x00; - - return info; //return info->rate; -} - -void device_stop_k051649(void *chip) -{ - k051649_state *info = (k051649_state *) chip; - - free(info->mixer_buffer); - free(info->mixer_table); - free(info); -} - -//static DEVICE_RESET( k051649 ) -void device_reset_k051649(void *chip) -{ - k051649_state *info = (k051649_state *) chip; - k051649_sound_channel *voice = info->channel_list; - int i; - - /* reset all the voices */ - for (i = 0; i < 5; i++) { - voice[i].frequency = 0; - voice[i].volume = 0; - voice[i].counter = 0; - } -} - -/********************************************************************************/ - -//WRITE8_DEVICE_HANDLER( k051649_waveform_w ) -void k051649_waveform_w(void *chip, offs_t offset, UINT8 data) -{ - k051649_state *info = (k051649_state *) chip; - //stream_update(info->stream); - info->channel_list[offset>>5].waveform[offset&0x1f]=data; - /* SY 20001114: Channel 5 shares the waveform with channel 4 */ - if (offset >= 0x60) - info->channel_list[4].waveform[offset&0x1f]=data; -} - -//READ8_DEVICE_HANDLER ( k051649_waveform_r ) -UINT8 k051649_waveform_r(void *chip, offs_t offset) -{ - k051649_state *info = (k051649_state *) chip; - return info->channel_list[offset>>5].waveform[offset&0x1f]; -} - -/* SY 20001114: Channel 5 doesn't share the waveform with channel 4 on this chip */ -//WRITE8_DEVICE_HANDLER( k052539_waveform_w ) -void k052539_waveform_w(void *chip, offs_t offset, UINT8 data) -{ - k051649_state *info = (k051649_state *) chip; - //stream_update(info->stream); - info->channel_list[offset>>5].waveform[offset&0x1f]=data; -} - -//WRITE8_DEVICE_HANDLER( k051649_volume_w ) -void k051649_volume_w(void *chip, offs_t offset, UINT8 data) -{ - k051649_state *info = (k051649_state *) chip; - //stream_update(info->stream); - info->channel_list[offset&0x7].volume=data&0xf; -} - -//WRITE8_DEVICE_HANDLER( k051649_frequency_w ) -void k051649_frequency_w(void *chip, offs_t offset, UINT8 data) -{ - k051649_state *info = (k051649_state *) chip; - info->f[offset]=data; - - //stream_update(info->stream); - info->channel_list[offset>>1].frequency=(info->f[offset&0xe] + (info->f[offset|1]<<8))&0xfff; -} - -//WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ) -void k051649_keyonoff_w(void *chip, offs_t offset, UINT8 data) -{ - k051649_state *info = (k051649_state *) chip; - //stream_update(info->stream); - info->channel_list[0].key=data&1; - info->channel_list[1].key=data&2; - info->channel_list[2].key=data&4; - info->channel_list[3].key=data&8; - info->channel_list[4].key=data&16; -} - -void k051649_w(void *chip, offs_t offset, UINT8 data) -{ - k051649_state *info = (k051649_state *) chip; - - switch(offset & 1) - { - case 0x00: - info->cur_reg = data; - break; - case 0x01: - switch(offset >> 1) - { - case 0x00: - k051649_waveform_w(info, info->cur_reg, data); - break; - case 0x01: - k051649_frequency_w(info, info->cur_reg, data); - break; - case 0x02: - k051649_volume_w(info, info->cur_reg, data); - break; - case 0x03: - k051649_keyonoff_w(info, info->cur_reg, data); - break; - case 0x04: - k052539_waveform_w(info, info->cur_reg, data); - break; - } - break; - } - - return; -} - - -void k051649_set_mute_mask(void *chip, UINT32 MuteMask) -{ - k051649_state *info = (k051649_state *) chip; - UINT8 CurChn; - - for (CurChn = 0; CurChn < 5; CurChn ++) - info->channel_list[CurChn].Muted = (MuteMask >> CurChn) & 0x01; -} diff --git a/Frameworks/GME/gme/k051649.h b/Frameworks/GME/gme/k051649.h deleted file mode 100644 index 584bb7ff7..000000000 --- a/Frameworks/GME/gme/k051649.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "mamedef.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//#ifndef __K051649_H__ -//#define __K051649_H__ - -//#include "devlegcy.h" - -/*WRITE8_DEVICE_HANDLER( k051649_waveform_w ); -READ8_DEVICE_HANDLER( k051649_waveform_r ); -WRITE8_DEVICE_HANDLER( k051649_volume_w ); -WRITE8_DEVICE_HANDLER( k051649_frequency_w ); -WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ); - -WRITE8_DEVICE_HANDLER( k052539_waveform_w ); - -DECLARE_LEGACY_SOUND_DEVICE(K051649, k051649);*/ - -void k051649_update(void *, stream_sample_t **outputs, int samples); -void * device_start_k051649(int clock); -void device_stop_k051649(void *); -void device_reset_k051649(void *); - -void k051649_waveform_w(void *, offs_t offset, UINT8 data); -UINT8 k051649_waveform_r(void *, offs_t offset); -void k051649_volume_w(void *, offs_t offset, UINT8 data); -void k051649_frequency_w(void *, offs_t offset, UINT8 data); -void k051649_keyonoff_w(void *, offs_t offset, UINT8 data); - -void k052539_waveform_w(void *, offs_t offset, UINT8 data); - -void k051649_w(void *, offs_t offset, UINT8 data); - -void k051649_set_mute_mask(void *, UINT32 MuteMask); - -//#endif /* __K051649_H__ */ - -#ifdef __cplusplus -}; -#endif diff --git a/Frameworks/GME/gme/kmsnddev.h b/Frameworks/GME/gme/kmsnddev.h deleted file mode 100644 index ec7552b1b..000000000 --- a/Frameworks/GME/gme/kmsnddev.h +++ /dev/null @@ -1,31 +0,0 @@ -/* libnezp by Mamiya */ - -#ifndef KMSNDDEV_H__ -#define KMSNDDEV_H__ -#ifdef __cplusplus -extern "C" { -#endif - -#include "nestypes.h" - -typedef struct KMIF_SOUND_DEVICE { - void *ctx; - void (*release)(void *ctx); - void (*reset)(void *ctx, Uint32 clock, Uint32 freq); - int (*synth)(void *ctx); - void (*volume)(void *ctx, Int32 v); - void (*write)(void *ctx, Uint32 a, Uint32 v); - Uint32 (*read)(void *ctx, Uint32 a); - void (*setinst)(void *ctx, Uint32 n, void *p, Uint32 l); -#if 0 - void (*setrate)(void *ctx, Uint32 clock, Uint32 freq); - void (*getinfo)(void *ctx, KMCH_INFO *cip, ); - void (*volume2)(void *ctx, Uint8 *volp, Uint32 numch); - /* 0x00(mute),0x70(x1/2),0x80(x1),0x90(x2) */ -#endif -} KMIF_SOUND_DEVICE; - -#ifdef __cplusplus -} -#endif -#endif /* KMSNDDEV_H__ */ diff --git a/Frameworks/GME/gme/mathdefs.h b/Frameworks/GME/gme/mathdefs.h deleted file mode 100644 index b12dfd0c7..000000000 --- a/Frameworks/GME/gme/mathdefs.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MATHDEFS_H -#define MATHDEFS_H - - -#ifndef _USE_MATH_DEFINES -#define _USE_MATH_DEFINES -#endif -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - - -#endif diff --git a/Frameworks/GME/gme/nestypes.h b/Frameworks/GME/gme/nestypes.h deleted file mode 100644 index 1fd3582b0..000000000 --- a/Frameworks/GME/gme/nestypes.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef NESTYPES_H__ -#define NESTYPES_H__ - -#if defined(_MSC_VER) -#define NEVER_REACH __assume(0); -#define inline __inline -#elif defined(__BORLANDC__) -#define __fastcall __msfastcall -#elif defined(__GNUC__) -#define __inline __inline__ -#define __fastcall -#else -#define __inline -#define __fastcall -#endif -#ifndef NEVER_REACH -#define NEVER_REACH -#endif - -typedef int Int; -typedef unsigned int Uint; -typedef signed int Int32; -typedef unsigned int Uint32; -typedef signed short Int16; -typedef unsigned short Uint16; -typedef signed char Int8; -typedef unsigned char Uint8; -typedef char Char; - -#include - -#define XSLEEP(n) ((void)0) -#define XMALLOC(s) malloc(s) -#define XREALLOC(p,s) realloc(p,s) -#define XFREE(p) free(p) -#define XMEMCPY(d,s,n) memcpy(d,s,n) -#define XMEMSET(d,c,n) memset(d,c,n) - -#endif /* NESTYPES_H__ */ diff --git a/Frameworks/GME/gme/qmix.c b/Frameworks/GME/gme/qmix.c deleted file mode 100644 index 7a855eec3..000000000 --- a/Frameworks/GME/gme/qmix.c +++ /dev/null @@ -1,462 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// qmix - QSound mixer -// -///////////////////////////////////////////////////////////////////////////// - -#include "qmix.h" - -///////////////////////////////////////////////////////////////////////////// - -#define ANTICLICK_TIME (64) -#define ANTICLICK_THRESHHOLD (32) - -///////////////////////////////////////////////////////////////////////////// - -#define RENDERMAX (200) - -///////////////////////////////////////////////////////////////////////////// - -static const sint32 gauss_shuffled_reverse_table[1024] = { - 366,1305, 374, 0, 362,1304, 378, 0, 358,1304, 381, 0, 354,1304, 385, 0, 351,1304, 389, 0, 347,1304, 393, 0, 343,1303, 397, 0, 339,1303, 401, 0, - 336,1303, 405, 0, 332,1302, 410, 0, 328,1302, 414, 0, 325,1301, 418, 0, 321,1300, 422, 0, 318,1300, 426, 0, 314,1299, 430, 0, 311,1298, 434, 0, - 307,1297, 439, 1, 304,1297, 443, 1, 300,1296, 447, 1, 297,1295, 451, 1, 293,1294, 456, 1, 290,1293, 460, 1, 286,1292, 464, 1, 283,1291, 469, 1, - 280,1290, 473, 1, 276,1288, 477, 1, 273,1287, 482, 1, 270,1286, 486, 2, 267,1284, 491, 2, 263,1283, 495, 2, 260,1282, 499, 2, 257,1280, 504, 2, - 254,1279, 508, 2, 251,1277, 513, 2, 248,1275, 517, 3, 245,1274, 522, 3, 242,1272, 527, 3, 239,1270, 531, 3, 236,1269, 536, 3, 233,1267, 540, 4, - 230,1265, 545, 4, 227,1263, 550, 4, 224,1261, 554, 4, 221,1259, 559, 4, 218,1257, 563, 5, 215,1255, 568, 5, 212,1253, 573, 5, 210,1251, 577, 5, - 207,1248, 582, 6, 204,1246, 587, 6, 201,1244, 592, 6, 199,1241, 596, 6, 196,1239, 601, 7, 193,1237, 606, 7, 191,1234, 611, 7, 188,1232, 615, 8, - 186,1229, 620, 8, 183,1227, 625, 8, 180,1224, 630, 9, 178,1221, 635, 9, 175,1219, 640, 9, 173,1216, 644, 10, 171,1213, 649, 10, 168,1210, 654, 10, - 166,1207, 659, 11, 163,1205, 664, 11, 161,1202, 669, 11, 159,1199, 674, 12, 156,1196, 678, 12, 154,1193, 683, 13, 152,1190, 688, 13, 150,1186, 693, 14, - 147,1183, 698, 14, 145,1180, 703, 15, 143,1177, 708, 15, 141,1174, 713, 15, 139,1170, 718, 16, 137,1167, 723, 16, 134,1164, 728, 17, 132,1160, 732, 17, - 130,1157, 737, 18, 128,1153, 742, 19, 126,1150, 747, 19, 124,1146, 752, 20, 122,1143, 757, 20, 120,1139, 762, 21, 118,1136, 767, 21, 117,1132, 772, 22, - 115,1128, 777, 23, 113,1125, 782, 23, 111,1121, 787, 24, 109,1117, 792, 24, 107,1113, 797, 25, 106,1109, 802, 26, 104,1106, 806, 27, 102,1102, 811, 27, - 100,1098, 816, 28, 99,1094, 821, 29, 97,1090, 826, 29, 95,1086, 831, 30, 94,1082, 836, 31, 92,1078, 841, 32, 90,1074, 846, 32, 89,1070, 851, 33, - 87,1066, 855, 34, 86,1061, 860, 35, 84,1057, 865, 36, 83,1053, 870, 36, 81,1049, 875, 37, 80,1045, 880, 38, 78,1040, 884, 39, 77,1036, 889, 40, - 76,1032, 894, 41, 74,1027, 899, 42, 73,1023, 904, 43, 71,1019, 908, 44, 70,1014, 913, 45, 69,1010, 918, 46, 67,1005, 923, 47, 66,1001, 927, 48, - 65, 997, 932, 49, 64, 992, 937, 50, 62, 988, 941, 51, 61, 983, 946, 52, 60, 978, 951, 53, 59, 974, 955, 54, 58, 969, 960, 55, 56, 965, 965, 56, - 55, 960, 969, 58, 54, 955, 974, 59, 53, 951, 978, 60, 52, 946, 983, 61, 51, 941, 988, 62, 50, 937, 992, 64, 49, 932, 997, 65, 48, 927,1001, 66, - 47, 923,1005, 67, 46, 918,1010, 69, 45, 913,1014, 70, 44, 908,1019, 71, 43, 904,1023, 73, 42, 899,1027, 74, 41, 894,1032, 76, 40, 889,1036, 77, - 39, 884,1040, 78, 38, 880,1045, 80, 37, 875,1049, 81, 36, 870,1053, 83, 36, 865,1057, 84, 35, 860,1061, 86, 34, 855,1066, 87, 33, 851,1070, 89, - 32, 846,1074, 90, 32, 841,1078, 92, 31, 836,1082, 94, 30, 831,1086, 95, 29, 826,1090, 97, 29, 821,1094, 99, 28, 816,1098, 100, 27, 811,1102, 102, - 27, 806,1106, 104, 26, 802,1109, 106, 25, 797,1113, 107, 24, 792,1117, 109, 24, 787,1121, 111, 23, 782,1125, 113, 23, 777,1128, 115, 22, 772,1132, 117, - 21, 767,1136, 118, 21, 762,1139, 120, 20, 757,1143, 122, 20, 752,1146, 124, 19, 747,1150, 126, 19, 742,1153, 128, 18, 737,1157, 130, 17, 732,1160, 132, - 17, 728,1164, 134, 16, 723,1167, 137, 16, 718,1170, 139, 15, 713,1174, 141, 15, 708,1177, 143, 15, 703,1180, 145, 14, 698,1183, 147, 14, 693,1186, 150, - 13, 688,1190, 152, 13, 683,1193, 154, 12, 678,1196, 156, 12, 674,1199, 159, 11, 669,1202, 161, 11, 664,1205, 163, 11, 659,1207, 166, 10, 654,1210, 168, - 10, 649,1213, 171, 10, 644,1216, 173, 9, 640,1219, 175, 9, 635,1221, 178, 9, 630,1224, 180, 8, 625,1227, 183, 8, 620,1229, 186, 8, 615,1232, 188, - 7, 611,1234, 191, 7, 606,1237, 193, 7, 601,1239, 196, 6, 596,1241, 199, 6, 592,1244, 201, 6, 587,1246, 204, 6, 582,1248, 207, 5, 577,1251, 210, - 5, 573,1253, 212, 5, 568,1255, 215, 5, 563,1257, 218, 4, 559,1259, 221, 4, 554,1261, 224, 4, 550,1263, 227, 4, 545,1265, 230, 4, 540,1267, 233, - 3, 536,1269, 236, 3, 531,1270, 239, 3, 527,1272, 242, 3, 522,1274, 245, 3, 517,1275, 248, 2, 513,1277, 251, 2, 508,1279, 254, 2, 504,1280, 257, - 2, 499,1282, 260, 2, 495,1283, 263, 2, 491,1284, 267, 2, 486,1286, 270, 1, 482,1287, 273, 1, 477,1288, 276, 1, 473,1290, 280, 1, 469,1291, 283, - 1, 464,1292, 286, 1, 460,1293, 290, 1, 456,1294, 293, 1, 451,1295, 297, 1, 447,1296, 300, 1, 443,1297, 304, 1, 439,1297, 307, 0, 434,1298, 311, - 0, 430,1299, 314, 0, 426,1300, 318, 0, 422,1300, 321, 0, 418,1301, 325, 0, 414,1302, 328, 0, 410,1302, 332, 0, 405,1303, 336, 0, 401,1303, 339, - 0, 397,1303, 343, 0, 393,1304, 347, 0, 389,1304, 351, 0, 385,1304, 354, 0, 381,1304, 358, 0, 378,1304, 362, 0, 374,1305, 366, 0, 370,1305, 370, -}; - -static const sint32 pan_table[33] = { - 0, 724,1024,1254,1448,1619,1774,1916, -2048,2172,2290,2401,2508,2611,2709,2804, -2896,2985,3072,3156,3238,3318,3396,3473, -3547,3620,3692,3762,3831,3899,3966,4031, -4096}; - -///////////////////////////////////////////////////////////////////////////// -// -// Static information -// -sint32 EMU_CALL _qmix_init(void) { return 0; } - -///////////////////////////////////////////////////////////////////////////// -// -// State information -// -#define QMIXSTATE ((struct QMIX_STATE*)(state)) - -struct QMIX_CHAN { - uint32 on; - uint32 startbank; - uint32 startaddr; - uint32 curbank; - uint32 curaddr; - uint32 startloop; - uint32 startend; - uint32 curloop; - uint32 curend; - uint32 phase; - uint32 pitch; - uint32 vol; - uint32 pan; - sint32 current_mix_l; - sint32 current_mix_r; - sint32 sample[4]; - sint32 sample_last_l; - sint32 sample_last_r; - sint32 sample_anticlick_l; - sint32 sample_anticlick_r; - sint32 sample_anticlick_remaining_l; - sint32 sample_anticlick_remaining_r; -}; - -///////////////////////////////////////////////////////////////////////////// - -static EMU_INLINE void get_anticlicked_samples( - struct QMIX_CHAN *chan, sint32 *l, sint32 *r -) { - sint32 out, remain; - remain = chan->sample_anticlick_remaining_l; - if(remain) { - sint32 diff = chan->sample_last_l - chan->sample_anticlick_l; - if(diff < 0) { diff = -diff; } - if(diff < ANTICLICK_THRESHHOLD) { - out = chan->sample_last_l; - chan->sample_anticlick_remaining_l = 0; - } else { - out = ( - chan->sample_last_l * (ANTICLICK_TIME-remain) + - chan->sample_anticlick_l * (remain) - ) / ANTICLICK_TIME; - chan->sample_anticlick_remaining_l--; - } - } else { - out = chan->sample_last_l; - } - *l = out; - - remain = chan->sample_anticlick_remaining_r; - if(remain) { - sint32 diff = chan->sample_last_r - chan->sample_anticlick_r; - if(diff < 0) { diff = -diff; } - if(diff < ANTICLICK_THRESHHOLD) { - out = chan->sample_last_r; - chan->sample_anticlick_remaining_r = 0; - } else { - out = ( - chan->sample_last_r * (ANTICLICK_TIME-remain) + - chan->sample_anticlick_r * (remain) - ) / ANTICLICK_TIME; - chan->sample_anticlick_remaining_r--; - } - } else { - out = chan->sample_last_r; - } - *r = out; - -} - -///////////////////////////////////////////////////////////////////////////// - -static EMU_INLINE void anticlick(struct QMIX_CHAN *chan) { - sint32 l, r; - get_anticlicked_samples(chan, &l, &r); - chan->sample_anticlick_l = l; - chan->sample_anticlick_r = r; - chan->sample_anticlick_remaining_l = ANTICLICK_TIME; - chan->sample_anticlick_remaining_r = ANTICLICK_TIME; -} - -///////////////////////////////////////////////////////////////////////////// - -struct QMIX_STATE { - uint8 *sample_rom; - uint32 sample_rom_size; - uint32 pitchscaler; - struct QMIX_CHAN chan[16]; - sint32 last_in_l; - sint32 last_in_r; - sint32 last_out_l; - sint32 last_out_r; - sint32 acc_l; - sint32 acc_r; -}; - -uint32 EMU_CALL _qmix_get_state_size(void) { - return sizeof(struct QMIX_STATE); -} - -void EMU_CALL _qmix_clear_state(void *state) { - memset(state, 0, sizeof(struct QMIX_STATE)); - - -} - -void EMU_CALL _qmix_set_sample_rom(void *state, void *rom, uint32 size) { - QMIXSTATE->sample_rom = rom; - QMIXSTATE->sample_rom_size = size; -} - -///////////////////////////////////////////////////////////////////////////// - -static EMU_INLINE void chan_advance( - struct QMIX_STATE *state, - struct QMIX_CHAN *chan -) { - uint32 rom_addr = chan->curbank + chan->curaddr; - if(rom_addr >= state->sample_rom_size) rom_addr = 0; - chan->sample[0] = chan->sample[1]; - chan->sample[1] = chan->sample[2]; - chan->sample[2] = chan->sample[3]; - chan->sample[3] = (sint32)((sint8)(state->sample_rom[rom_addr])); - chan->curaddr++; -// FIXME: MAME thinks this is >=, but is it > ? - if(chan->curaddr >= chan->curend) { -// if(!chan->curloop) { -// chan->on = 0; -// chan->curaddr--; -// } else { - chan->curaddr = chan->curend - chan->curloop; -// chan->curaddr -= 1 + chan->curloop; -// } - } - chan->curaddr &= 0xFFFF; -} - -///////////////////////////////////////////////////////////////////////////// - -static EMU_INLINE sint32 chan_get_resampled( - struct QMIX_STATE *state, - struct QMIX_CHAN *chan -) { - sint32 sum; - sint32 phase = chan->phase & 0xFFF; - -// sum = chan->sample[2]; -// sum <<= 8; - - const sint32 *gauss = (sint32*) - (((sint8*)gauss_shuffled_reverse_table) + (phase & 0x0FF0)); - sum = chan->sample[0] * gauss[0]; - sum += chan->sample[1] * gauss[1]; - sum += chan->sample[2] * gauss[2]; - sum += chan->sample[3] * gauss[3]; - sum /= 8; - -// sum = chan->sample[1] * (0x1000-phase); -// sum += chan->sample[2] * ( phase); -// sum >>= 4; - - chan->phase += chan->pitch; - while(chan->phase >= 0x1000) { - chan_advance(state, chan); - chan->phase -= 0x1000; - } - - return sum; -} - -static EMU_INLINE void chan_get_stereo_anticlicked( - struct QMIX_STATE *state, - struct QMIX_CHAN *chan, - sint32 *l, - sint32 *r -) { - if(!chan->on) { - chan->sample_last_l = 0; - chan->sample_last_r = 0; - } else { - sint32 out = chan_get_resampled(state, chan); - chan->sample_last_l = (out * chan->current_mix_l) / 0x8000; - chan->sample_last_r = (out * chan->current_mix_r) / 0x8000; - // if we suddenly keyed off, perform an anticlick here - if(!chan->on) { anticlick(chan); } - } - get_anticlicked_samples(chan, l, r); -} - -///////////////////////////////////////////////////////////////////////////// - -static void recalc_mix(struct QMIX_CHAN *chan) { - sint32 realpan = (chan->pan & 0x3F) - 0x10; - sint32 realvol = chan->vol & 0xFFFF; - if(realpan < 0x00) realpan = 0x00; - if(realpan > 0x20) realpan = 0x20; - -// chan->current_mix_l = realvol << 3; -// chan->current_mix_r = realvol << 3; -// if(realpan < 0x10) { -// chan->current_mix_r *= realpan; -// chan->current_mix_r >>= 4; -// } -// if(realpan > 0x10) { -// chan->current_mix_l *= (0x20-realpan); -// chan->current_mix_l >>= 4; -// } - -// chan->current_mix_l = ((0x20-realpan) * realvol) >> 1; -// chan->current_mix_r = (( realpan) * realvol) >> 1; - - chan->current_mix_l = (realvol * pan_table[0x20-realpan]) / 0x2000; - chan->current_mix_r = (realvol * pan_table[ realpan]) / 0x2000; - - // perform anticlick - //anticlick(chan); -} - -///////////////////////////////////////////////////////////////////////////// -// -// Command handling -// -//#include - -void EMU_CALL _qmix_command(void *state, uint8 cmd, uint16 data) { - struct QMIX_CHAN *chan; - uint32 ch = 0; - uint32 reg = 99; -//printf("qmix command 0x%02X:0x%04X\n",cmd,data); - if(cmd < 0x80) { - reg = cmd & 7; - ch = cmd >> 3; - } else if(cmd < 0x90) { - reg = 8; - ch = cmd - 0x80; - } else if(cmd >= 0xBA && cmd < 0xCA) { - reg = 9; - ch = cmd - 0xBA; - } else { - reg = 99; - ch = 0; - } - chan = QMIXSTATE->chan + ch; - switch(reg) { - case 0: // bank - ch = (ch+1) & 0xF; chan = QMIXSTATE->chan + ch; - //printf("qmix: bank ch%X = %04X\n",ch,data); - chan->startbank = (((uint32)data) & 0x7F) << 16; - break; - case 1: // start - //printf("qmix: start ch%X = %04X\n",ch,data); - chan->startaddr = ((uint32)data) & 0xFFFF; - break; - case 2: // pitch - //printf("qmix: pitch ch%X = %04X\n",ch,data); - chan->pitch = (((uint32)(data & 0xFFFF)) * QMIXSTATE->pitchscaler) / 0x10000; - if (chan->pitch == 0) { - chan->on = 0; - anticlick(chan); - } - break; - case 3: // unknown - break; - case 4: // loop start - //printf("qmix: loop ch%X = %04X\n",ch,data); - chan->startloop = data; - break; - case 5: // end - //printf("qmix: end ch%X = %04X\n",ch,data); - chan->startend = data; - break; - case 6: // volume - //printf("qmix: vol ch%X = %04X\n",ch,data); -//printf("volume=%04X\n",data); -// if(!data) { -// chan->on = 0; -// } else { -// chan->on = 1; -// chan->address = chan->start; -// chan->phase = 0; -// } - //printf("qmix: unknown reg3 ch%X = %04X\n",ch,data); - if(data == 0) { - chan->on = 0; - anticlick(chan); - } else if (chan->on == 0) { - chan->on = 1; - chan->curbank = chan->startbank; - chan->curaddr = chan->startaddr; - chan->curloop = chan->startloop; - chan->curend = chan->startend; - chan->phase = 0; - chan->sample[0] = 0; - chan->sample[1] = 0; - chan->sample[2] = 0; - chan->sample[3] = 0; - anticlick(chan); - } - - chan->vol = data; - recalc_mix(chan); - break; - case 7: // unknown - //printf("qmix: unknown reg7 ch%X = %04X\n",ch,data); - break; - case 8: // pan (0x110-0x130) - //printf("qmix: pan ch%X = %04X\n",ch,data); -//printf("pan=%04X\n",data); - chan->pan = data; - recalc_mix(chan); - break; - case 9: // ADSR? - //printf("qmix: unknown reg9 ch%X = %04X\n",ch,data); - break; - default: - //printf("qmix: unknown reg %02X = %04X\n",cmd,data); - break; - } -} - -///////////////////////////////////////////////////////////////////////////// -// -// Rendering -// -static void render( - struct QMIX_STATE *state, - sint16 *buf, - uint32 samples -) { - sint32 buf_l[RENDERMAX]; - sint32 buf_r[RENDERMAX]; - sint32 l, r; - uint32 s; - int ch; - memset(buf_l, 0, 4 * samples); - memset(buf_r, 0, 4 * samples); - for(ch = 0; ch < 16; ch++) { - struct QMIX_CHAN *chan = state->chan + ch; - for(s = 0; s < samples; s++) { - chan_get_stereo_anticlicked(state, chan, &l, &r); - buf_l[s] += l; - buf_r[s] += r; - } - } - if(!buf) return; - for(s = 0; s < samples; s++) { - sint32 diff_l = buf_l[s] - state->last_in_l; - sint32 diff_r = buf_r[s] - state->last_in_r; - state->last_in_l = buf_l[s]; - state->last_in_r = buf_r[s]; - l = ((state->last_out_l * 255) / 256) + diff_l; - r = ((state->last_out_r * 255) / 256) + diff_r; - state->last_out_l = l; - state->last_out_r = r; -// l /= 2; -// r /= 2; - l *= 8; - r *= 8; - if(l > ( 32767)) l = ( 32767); - if(l < (-32768)) l = (-32768); - if(r > ( 32767)) r = ( 32767); - if(r < (-32768)) r = (-32768); - *buf++ = l; - *buf++ = r; - } -} - -///////////////////////////////////////////////////////////////////////////// - -void EMU_CALL _qmix_render(void *state, sint16 *buf, uint32 samples) { -//printf("qmix render %u samples\n",samples); - for(; samples >= RENDERMAX; samples -= RENDERMAX) { - render(QMIXSTATE, buf, RENDERMAX); - if(buf) buf += 2 * RENDERMAX; - } - if(samples) { - render(QMIXSTATE, buf, samples); - } -} - -///////////////////////////////////////////////////////////////////////////// - -void EMU_CALL _qmix_set_sample_rate(void *state, uint32 rate) { - if(rate < 1) rate = 1; - QMIXSTATE->pitchscaler = (65536 * 24000) / rate; -} - -///////////////////////////////////////////////////////////////////////////// diff --git a/Frameworks/GME/gme/qmix.h b/Frameworks/GME/gme/qmix.h deleted file mode 100644 index 2ce3ebf8d..000000000 --- a/Frameworks/GME/gme/qmix.h +++ /dev/null @@ -1,29 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// qmix - QSound mixer -// -///////////////////////////////////////////////////////////////////////////// - -#ifndef __Q_QMIX_H__ -#define __Q_QMIX_H__ - -#include "emuconfig.h" - -#ifdef __cplusplus -extern "C" { -#endif - -sint32 EMU_CALL _qmix_init(void); -uint32 EMU_CALL _qmix_get_state_size(void); -void EMU_CALL _qmix_clear_state(void *state); - -void EMU_CALL _qmix_set_sample_rate(void *state, uint32 rate); -void EMU_CALL _qmix_set_sample_rom(void *state, void *rom, uint32 size); -void EMU_CALL _qmix_command(void *state, uint8 cmd, uint16 data); -void EMU_CALL _qmix_render(void *state, sint16 *buf, uint32 samples); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Frameworks/GME/gme/s_deltat.c b/Frameworks/GME/gme/s_deltat.c deleted file mode 100644 index 8684005b3..000000000 --- a/Frameworks/GME/gme/s_deltat.c +++ /dev/null @@ -1,281 +0,0 @@ -#include "kmsnddev.h" -#include "divfix.h" -#include "s_logtbl.h" -#include "s_deltat.h" -#include - -#define CPS_SHIFT 16 -#define PHASE_SHIFT 16 /* 16(fix) */ - -typedef struct { - KMIF_SOUND_DEVICE kmif; - KMIF_LOGTABLE *logtbl; - struct YMDELTATPCMSOUND_COMMON_TAG { - Int32 mastervolume; - Int32 step; - Int32 output; - Uint32 cnt; - Uint32 cps; - Uint32 phase; - Uint32 deltan; - Uint32 scale; - Uint32 mem; - Uint32 play; - Uint32 start; - Uint32 stop; - Int32 level32; - Uint8 key; - Uint8 level; - Uint8 granuality; - Uint8 pad4_3; - Uint8 regs[0x10]; - } common; - Uint8 *romrambuf; - Uint32 romrammask; - Uint8 *rambuf; - Uint32 rammask; - Uint8 *rombuf; - Uint32 rommask; - Uint8 ymdeltatpcm_type; - Uint8 memshift; -} YMDELTATPCMSOUND; - -static Uint8 const table_step[8] = -{ - 1, 3, 5, 7, 9, 11, 13, 15, -}; -static Uint8 const table_scale[16] = -{ - 57, 57, 57, 57, 77, 102, 128, 153, - 57, 57, 57, 57, 77, 102, 128, 153, -}; - -__inline static void writeram(YMDELTATPCMSOUND *sndp, Uint32 v) -{ - sndp->rambuf[(sndp->common.mem >> 1) & sndp->rammask] = v; - sndp->common.mem += 1 << 1; -} - -__inline static Uint32 readram(YMDELTATPCMSOUND *sndp) -{ - Uint32 v; - v = sndp->romrambuf[(sndp->common.play >> 1) & sndp->romrammask]; - if (sndp->common.play & 1) - v &= 0x0F; - else - v >>= 4; - sndp->common.play += 1; - if (sndp->common.play >= sndp->common.stop) - { - if (sndp->common.regs[0] & 0x10) - { - sndp->common.play = sndp->common.start; - sndp->common.step = 0; - sndp->common.scale = 127; - } - else - { - sndp->common.key = 0; - } - } - return v; -} - -__inline static void DelrtatStep(YMDELTATPCMSOUND *sndp, Uint32 data) -{ - if (data & 8) - sndp->common.step -= (table_step[data & 7] * sndp->common.scale) >> 3; - else - sndp->common.step += (table_step[data & 7] * sndp->common.scale) >> 3; - if (sndp->common.step > ((1 << 15) - 1)) sndp->common.step = ((1 << 15) - 1); - if (sndp->common.step < -(1 << 15)) sndp->common.step = -(1 << 15); - sndp->common.scale = (sndp->common.scale * table_scale[data]) >> 6; - if (sndp->common.scale > 24576) sndp->common.scale = 24576; - if (sndp->common.scale < 127) sndp->common.scale = 127; -} - -#if (((-1) >> 1) == -1) -#define SSR(x, y) (((Int32)x) >> (y)) -#else -#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1)) -#endif - -static int sndsynth(YMDELTATPCMSOUND *sndp ) -{ - if (!sndp->common.key) - return 0; - { - Uint32 step; - sndp->common.cnt += sndp->common.cps; - step = sndp->common.cnt >> CPS_SHIFT; - sndp->common.cnt &= (1 << CPS_SHIFT) - 1; - sndp->common.phase += step * sndp->common.deltan; - step = sndp->common.phase >> PHASE_SHIFT; - sndp->common.phase &= (1 << PHASE_SHIFT) - 1; - if (step) - { - do - { - DelrtatStep(sndp, readram(sndp)); - } while (--step); - sndp->common.output = sndp->common.step * sndp->common.level32; - sndp->common.output = SSR(sndp->common.output, 8 + 2); - } - } - return sndp->common.output; -} - - - -static void sndwrite(YMDELTATPCMSOUND *sndp, Uint32 a, Uint32 v) -{ - sndp->common.regs[a] = v; - switch (a) - { - /* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ - case 0x00: /* Control Register 1 */ - if ((v & 0x80) && !sndp->common.key) - { - sndp->common.key = 1; - sndp->common.play = sndp->common.start; - sndp->common.step = 0; - sndp->common.scale = 127; - } - if (v & 1) sndp->common.key = 0; - break; - /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ - case 0x01: /* Control Register 2 */ - sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf; - sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask; - break; - case 0x02: /* Start Address L */ - case 0x03: /* Start Address H */ - sndp->common.granuality = (v & 2) ? 1 : 4; - sndp->common.start = ((sndp->common.regs[3] << 8) + sndp->common.regs[2]) << (sndp->memshift + 1); - sndp->common.mem = sndp->common.start; - break; - case 0x04: /* Stop Address L */ - case 0x05: /* Stop Address H */ - sndp->common.stop = ((sndp->common.regs[5] << 8) + sndp->common.regs[4]) << (sndp->memshift + 1); - break; - case 0x06: /* Prescale L */ - case 0x07: /* Prescale H */ - break; - case 0x08: /* Data */ - if ((sndp->common.regs[0] & 0x60) == 0x60) writeram(sndp, v); - break; - case 0x09: /* Delta-N L */ - case 0x0A: /* Delta-N H */ - sndp->common.deltan = (sndp->common.regs[0xA] << 8) + sndp->common.regs[0x9]; - if (sndp->common.deltan < 0x100) sndp->common.deltan = 0x100; - break; - case 0x0B: /* Level Control */ - sndp->common.level = v; - sndp->common.level32 = ((Int32)(sndp->common.level * LogToLin(sndp->logtbl, sndp->common.mastervolume, LOG_LIN_BITS - 15))) >> 7; - sndp->common.output = sndp->common.step * sndp->common.level32; - sndp->common.output = SSR(sndp->common.output, 8 + 2); - break; - } -} - -static Uint32 sndread(YMDELTATPCMSOUND *sndp, Uint32 a) -{ - return 0; -} - -static void sndreset(YMDELTATPCMSOUND *sndp, Uint32 clock, Uint32 freq) -{ - XMEMSET(&sndp->common, 0, sizeof(sndp->common)); - sndp->common.cps = DivFix(clock, 72 * freq, CPS_SHIFT); - sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf; - sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask; - sndp->common.granuality = 4; -} - -static void sndvolume(YMDELTATPCMSOUND *sndp, Int32 volume) -{ - volume = (volume << (LOG_BITS - 8)) << 1; - sndp->common.mastervolume = volume; - sndp->common.level32 = ((Int32)(sndp->common.level * LogToLin(sndp->logtbl, sndp->common.mastervolume, LOG_LIN_BITS - 15))) >> 7; - sndp->common.output = sndp->common.step * sndp->common.level32; - sndp->common.output = SSR(sndp->common.output, 8 + 2); -} - -static void sndrelease(YMDELTATPCMSOUND *sndp) -{ - if (sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx); - XFREE(sndp); -} - -static void setinst(YMDELTATPCMSOUND *sndp, Uint32 n, void *p, Uint32 l) -{ - if (n) return; - if (p) - { - sndp->rombuf = (Uint8*) p; - sndp->rommask = l - 1; - sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf; - sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask; - } - else - { - sndp->rombuf = 0; - sndp->rommask = 0; - } - -} - -KMIF_SOUND_DEVICE *YMDELTATPCMSoundAlloc(Uint32 ymdeltatpcm_type) -{ - Uint32 ram_size; - YMDELTATPCMSOUND *sndp; - switch (ymdeltatpcm_type) - { - case YMDELTATPCM_TYPE_Y8950: - ram_size = 32 * 1024; - break; - case YMDELTATPCM_TYPE_YM2608: - ram_size = 256 * 1024; - break; - default: - ram_size = 0; - break; - } - sndp = (YMDELTATPCMSOUND*) XMALLOC(sizeof(YMDELTATPCMSOUND) + ram_size); - if (!sndp) return 0; - sndp->ymdeltatpcm_type = ymdeltatpcm_type; - switch (ymdeltatpcm_type) - { - case YMDELTATPCM_TYPE_Y8950: - sndp->memshift = 2; - break; - case YMDELTATPCM_TYPE_YM2608: - /* OPNA */ - sndp->memshift = 6; - break; - case YMDELTATPCM_TYPE_YM2610: - sndp->memshift = 9; - break; - } - sndp->kmif.ctx = sndp; - sndp->kmif.release = (void (*)( void* )) sndrelease; - sndp->kmif.synth = (int (*)( void* )) sndsynth; - sndp->kmif.volume = (void (*)( void*, int )) sndvolume; - sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset; - sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) sndwrite; - sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) sndread; - sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) setinst; - /* RAM */ - sndp->rambuf = ram_size ? (Uint8 *)(sndp + 1) : 0; - sndp->rammask = ram_size ? (ram_size - 1) : 0; - /* ROM */ - sndp->rombuf = 0; - sndp->rommask = 0; - sndp->logtbl = LogTableAddRef(); - if (!sndp->logtbl) - { - sndrelease(sndp); - return 0; - } - return &sndp->kmif; -} diff --git a/Frameworks/GME/gme/s_deltat.h b/Frameworks/GME/gme/s_deltat.h deleted file mode 100644 index 89c9242c8..000000000 --- a/Frameworks/GME/gme/s_deltat.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef S_DELTAT_H__ -#define S_DELTAT_H__ - -#include "kmsnddev.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - /* MSX-AUDIO */ YMDELTATPCM_TYPE_Y8950, - /* OPNA ADPCM */ YMDELTATPCM_TYPE_YM2608, - /* OPNB ADPCMB */ YMDELTATPCM_TYPE_YM2610 -}; - -KMIF_SOUND_DEVICE *YMDELTATPCMSoundAlloc(Uint32 ymdeltatpcm_type); - -#ifdef __cplusplus -} -#endif - -#endif /* S_DELTAT_H__ */ - diff --git a/Frameworks/GME/gme/s_logtbl.c b/Frameworks/GME/gme/s_logtbl.c deleted file mode 100644 index 0f455f2b7..000000000 --- a/Frameworks/GME/gme/s_logtbl.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "nestypes.h" -#include "s_logtbl.h" - -#if STATIC_TABLES - -static void LogTableRelease(void *ctx) -{ -} - -static KMIF_LOGTABLE log_static_tables = { - &log_static_tables; - LogTableRelease, -#include "s_logt.h" -}; - - -KMIF_LOGTABLE *LogTableAddRef(void) -{ - log_static_tables.release = LogTableRelease; - return &log_static_tables; -} - -#else - -#include - -static volatile Uint32 log_tables_mutex = 0; -static Uint32 log_tables_refcount = 0; -static KMIF_LOGTABLE *log_tables = 0; - -static void LogTableRelease(void *ctx) -{ - ++log_tables_mutex; - while (log_tables_mutex != 1) - { - XSLEEP(0); - } - log_tables_refcount--; - if (!log_tables_refcount) - { - XFREE(ctx); - log_tables = 0; - } - --log_tables_mutex; -} - -static void LogTableCalc(KMIF_LOGTABLE *kmif_lt) -{ - Uint32 i; - double a; - for (i = 0; i < (1 << LOG_BITS); i++) - { - a = (1 << LOG_LIN_BITS) / pow(2, i / (double)(1 << LOG_BITS)); - kmif_lt->logtbl[i] = (Uint32)a; - } - kmif_lt->lineartbl[0] = LOG_LIN_BITS << LOG_BITS; - for (i = 1; i < (1 << LIN_BITS) + 1; i++) - { - Uint32 ua; - a = i << (LOG_LIN_BITS - LIN_BITS); - ua = (Uint32)((LOG_LIN_BITS - (log(a) / log(2))) * (1 << LOG_BITS)); - kmif_lt->lineartbl[i] = ua << 1; - } -} - -KMIF_LOGTABLE *LogTableAddRef(void) -{ - ++log_tables_mutex; - while (log_tables_mutex != 1) - { - XSLEEP(0); - } - if (!log_tables_refcount) - { - log_tables = (KMIF_LOGTABLE*) XMALLOC(sizeof(KMIF_LOGTABLE)); - if (log_tables) - { - log_tables->ctx = log_tables; - log_tables->release = LogTableRelease; - LogTableCalc(log_tables); - } - } - if (log_tables) log_tables_refcount++; - --log_tables_mutex; - return log_tables; -} - -#endif diff --git a/Frameworks/GME/gme/s_logtbl.h b/Frameworks/GME/gme/s_logtbl.h deleted file mode 100644 index 3abc79c11..000000000 --- a/Frameworks/GME/gme/s_logtbl.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef S_LOGTBL_H__ -#define S_LOGTBL_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#define LOG_BITS 12 -#define LIN_BITS 7 -#define LOG_LIN_BITS 30 - -typedef struct -{ - void *ctx; - void (*release)(void *ctx); - Uint32 lineartbl[(1 << LIN_BITS) + 1]; - Uint32 logtbl[1 << LOG_BITS]; -} KMIF_LOGTABLE; - -KMIF_LOGTABLE *LogTableAddRef(void); - -__inline static Uint32 LinToLog(KMIF_LOGTABLE *kmif_lt, Int32 l) -{ - return (l < 0) ? (kmif_lt->lineartbl[-l] + 1) : kmif_lt->lineartbl[l]; -} - -__inline static Int32 LogToLin(KMIF_LOGTABLE *kmif_lt, Int32 l, Uint32 sft) -{ - Int32 ret; - Uint32 ofs; - ofs = l + (sft << (LOG_BITS + 1)); - sft = ofs >> (LOG_BITS + 1); - if (sft >= LOG_LIN_BITS) return 0; - ofs = (ofs >> 1) & ((1 << LOG_BITS) - 1); - ret = kmif_lt->logtbl[ofs] >> sft; - return (l & 1) ? -ret : ret; -} - -#ifdef __cplusplus -} -#endif - -#endif /* S_LOGTBL_H__ */ diff --git a/Frameworks/GME/gme/s_opl.c b/Frameworks/GME/gme/s_opl.c deleted file mode 100644 index 83b33e6e8..000000000 --- a/Frameworks/GME/gme/s_opl.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* - s_opl.c -- YM2413/Y8950/YM3526/YM3812 emulator by Mamiya, 2001. - - References: - fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). - fmopl.c(fixed) -- 2000 modified by mamiya (NEZplug development). - fmgen.cpp -- 1999-2001 written by cisc. - emu2413.c -- a YM2413 emulator : written by Mitsutaka Okazaki 2001 - fmpac.ill -- 2000 created by sama. - fmpac.ill -- 2000 created by NARUTO. - fmpac.ill -- 2001 created by Okazaki. - YM2413 application manual -*/ - -#include "kmsnddev.h" -#include "divfix.h" -#include "s_logtbl.h" -#include "s_opltbl.h" -#include "s_opl.h" -#include "s_deltat.h" -#include - -#define PG_SHIFT 10 /* fix */ -#define CPS_SHIFTE 20 -#define CPS_SHIFTP 14 -#define LFO_SHIFT 16 - -#define OPLL_INST_WORK 0x40 -#define OPLL_INST_WORK2 (OPLL_INST_WORK + 8 * 0x13) - -#define AR_BITS 6 /* fix */ -#define AR_SHIFT 14 /* fix */ -#define EG_SHIFT 15 /* fix */ - -#define AR_PHASEMAX (((1 << AR_BITS) - 1) << AR_SHIFT) -#define EG_PHASEMAX (127 << EG_SHIFT) -#define EG_KEYOFF (128 << EG_SHIFT) -#define LOG_KEYOFF (31 << (LOG_BITS + 1)) - -#if 0 - 0-48dB - AR_BITS=6 - AR_SHIFT=14 - x = FC000h(20.7618(sec) * 3579545 / 72)cycles 63 * 4000h - x / 3 * 4 1730.15(ms) - x / 3 * 256 27.03(ms) - - EG_BITS=7 - EG_SHIFT=15 - x = 3F8000h(83.7064(sec) * 3579545 / 72)cycles 127 * 8000h - x / 4 = 20926.6(ms) -#endif - - -#define TESTING_OPTIMIZE_AME 1 -#define USE_FBBUF 1 - -typedef struct -{ - Uint32 phase; - Uint32 spd; - Uint32 rng; -} OPL_PG; - -enum { - EG_MODE_ATTACK, - EG_MODE_DECAY, - EG_MODE_SUSTINE, - EG_MODE_RELEASE, - EG_MODE_NUM, - EG_MODE_SUSHOLD, - EG_MODE_OFF = EG_MODE_NUM -}; - -typedef struct -{ - Uint32 phasear; - Uint32 phase; - Uint32 spd [EG_MODE_NUM]; - Uint32 dr_phasemax; - Uint8 mode; - Uint8 pad4_1; - Uint8 pad4_2; - Uint8 pad4_3; -} OPL_EG; - -enum -{ - FLAG_AME = (1 << 0), - FLAG_PME = (1 << 1), - FLAG_EGT = (1 << 2), - FLAG_KSR = (1 << 3) -}; - -typedef struct -{ - OPL_PG pg; - OPL_EG eg; - Int32 input; -#if USE_FBBUF - Int32 fbbuf; -#endif -#if TESTING_OPTIMIZE_AME - Uint32* amin; -#endif - Uint32 tl_ofs; - Uint32* sintable; - - Uint8 modcar; /* 1:m 0:c */ - Uint8 fb; - Uint8 lvl; - Uint8 nst; - - Uint8 tll; - Uint8 key; - Uint8 rkey; - Uint8 prevkey; - - Uint8 enable; - Uint8 __enablehold__; - Uint8 flag; - Uint8 ksr; - - Uint8 mul; - Uint8 ksl; - Uint8 ar; - Uint8 dr; - Uint8 sl; - Uint8 rr; - Uint8 tl; - Uint8 wf; -} OPL_OP; - -typedef struct -{ - OPL_OP op [2]; - Uint8 con; - Uint8 freql; - Uint8 freqh; - Uint8 blk; - Uint8 kcode; - Uint8 sus; - Uint8 ksb; - Uint8 pad4_3; -} OPL_CH; - -enum -{ - LFO_UNIT_AM, - LFO_UNIT_PM, - LFO_UNIT_NUM -}; - -typedef struct -{ - Uint32 output; - Uint32 cnt; - Uint32 sps; /* step per sample */ - Uint32 adr; - Uint32 adrmask; - Uint32* table; -} OPL_LFO; - -typedef struct -{ - KMIF_SOUND_DEVICE kmif; - KMIF_SOUND_DEVICE* deltatpcm; - KMIF_LOGTABLE* logtbl; - KMIF_OPLTABLE* opltbl; - OPL_CH ch [9]; - OPL_LFO lfo [LFO_UNIT_NUM]; - struct OPLSOUND_COMMON_TAG - { - Uint32 cpsp; - Uint32 cnt; - Uint32* ar_table; - Uint32* tll2logtbl; -#if TESTING_OPTIMIZE_AME - Uint32 amzero; -#endif - Int32 mastervolume; - Uint32 sintablemask; - Uint32 ratetbla [4]; - Uint32 ratetbl [4]; - Uint8 adr; - Uint8 wfe; - Uint8 rc; - Uint8 rmode; - Uint8 enable; - } common; - Uint8 regs [0x100]; - Uint8 opl_type; -} OPLSOUND; - -static Uint8 romtone [3] [16 * 19] = -{ - { -#include "i_fmpac.h" - }, - { -#include "i_fmunit.h" - }, - { -#include "i_vrc7.h" - }, -}; - -static void SetOpOff(OPL_OP* opp ) -{ - opp->eg.mode = EG_MODE_OFF; - opp->eg.phase = EG_KEYOFF; - opp->enable = 0; -} - -inline static void EgStep( OPLSOUND* sndp, OPL_OP* opp ) -{ - switch ( opp->eg.mode ) - { - default: - NEVER_REACH - - case EG_MODE_ATTACK: - opp->eg.phase = sndp->common.ar_table [opp->eg.phasear >> (AR_SHIFT + AR_BITS - ARTBL_BITS)] >> (ARTBL_SHIFT - EG_SHIFT); - opp->eg.phasear += opp->eg.spd [EG_MODE_ATTACK]; - if ( opp->eg.phasear >= AR_PHASEMAX ) - { - opp->eg.mode = EG_MODE_DECAY; - opp->eg.phase = 0; - } - break; - - case EG_MODE_DECAY: - opp->eg.phase += opp->eg.spd [EG_MODE_DECAY]; - if ( opp->eg.phase >= opp->eg.dr_phasemax ) - { - opp->eg.phase = opp->eg.dr_phasemax; - opp->eg.mode = (opp->flag & FLAG_EGT) ? EG_MODE_SUSHOLD : EG_MODE_SUSTINE; - } - break; - - case EG_MODE_SUSTINE: - case EG_MODE_RELEASE: - opp->eg.phase += opp->eg.spd [opp->eg.mode]; - if ( opp->eg.phase >= EG_PHASEMAX ) - SetOpOff(opp); - break; - - case EG_MODE_SUSHOLD: - case EG_MODE_OFF: - break; - } -} - -static void OpStep( OPLSOUND* sndp, OPL_OP* opp ) -{ - int step; - EgStep(sndp, opp); - step = opp->pg.spd; - if ( opp->flag & FLAG_PME ) - step = (step * sndp->lfo [LFO_UNIT_PM].output) >> PM_SHIFT; - opp->pg.phase += step; -} - -__inline static void OpStepNG( OPLSOUND* sndp, OPL_OP* opp ) -{ - Uint32 step; - EgStep(sndp, opp); - opp->pg.phase += opp->pg.spd; - step = opp->pg.phase >> opp->nst/*(PG_SHIFT + 5)*/; - opp->pg.phase &= (1 << opp->nst/*(PG_SHIFT + 5)*/) - 1; - while ( step-- ) - { - opp->pg.rng ^= ((opp->pg.rng & 1) << 16) + ((opp->pg.rng & 1) << 13); - opp->pg.rng >>= 1; - } -} - -#if -1 >> 1 == -1 -/* RIGHT SHIFT IS SIGNED */ -#define SSR(x, y) ((Int32)(x) >> (y)) -#else -/* RIGHT SHIFT IS UNSIGNED */ -#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1)) -#endif - - -inline static void OpSynthMod( OPLSOUND* sndp, OPL_OP* opp ) -{ - if ( opp->enable ) - { - Uint32 tll; - Int32 output; - OpStep(sndp, opp); - tll = opp->tll + (opp->eg.phase >> EG_SHIFT); - tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; - tll += opp->tl_ofs; -#if TESTING_OPTIMIZE_AME - tll += *opp->amin; -#else - if ( opp->flag & FLAG_AME ) - tll += sndp->lfo [LFO_UNIT_AM].output; -#endif - tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))]; - output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2))); - if ( opp->fb ) - { -#if USE_FBBUF - Int32 fbtmp; - fbtmp = opp->fbbuf + output; - opp->fbbuf = output; - opp->input = SSR(fbtmp, (9 - opp->fb)); -#else - opp->input = SSR(output, (8 - opp->fb)); -#endif - } - opp [1].input = output; - } -} - -inline static Int32 OpSynthCarFb( OPLSOUND* sndp, OPL_OP* opp ) -{ - if ( opp->enable ) - { - Uint32 tll; - OpStep(sndp, opp); - tll = opp->tll + (opp->eg.phase >> EG_SHIFT); - tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; - tll += opp->tl_ofs; -#if TESTING_OPTIMIZE_AME - tll +=* opp->amin; -#else - if ( opp->flag & FLAG_AME) tll += sndp->lfo [LFO_UNIT_AM].output; -#endif - tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))]; - if ( opp->fb ) - { -#if USE_FBBUF - Int32 output, fbtmp; - output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2))); - fbtmp = opp->fbbuf + output; - opp->fbbuf = output; - opp->input = SSR(fbtmp, (9 - opp->fb)); -#else - Int32 output; - output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2))); - opp->input = SSR(output, (8 - opp->fb)); -#endif - } - return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); - } - return 0; -} - -inline static Int32 OpSynthCar( OPLSOUND* sndp, OPL_OP* opp ) -{ - if ( opp->enable ) - { - Uint32 tll; - OpStep(sndp, opp); - tll = opp->tll + (opp->eg.phase >> EG_SHIFT); - tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; - tll += opp->tl_ofs; -#if TESTING_OPTIMIZE_AME - tll += *opp->amin; -#else - if ( opp->flag & FLAG_AME ) - tll += sndp->lfo [LFO_UNIT_AM].output; -#endif - tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))]; - return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); - } - return 0; -} - -inline static Int32 OpSynthTom( OPLSOUND* sndp, OPL_OP* opp ) -{ - if ( opp->enable ) - { - Uint32 tll; - OpStep(sndp, opp); - tll = opp->tll + (opp->eg.phase >> EG_SHIFT); - tll = (tll >= 128 - 16) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; - tll += opp->tl_ofs; - tll += opp->sintable [sndp->common.sintablemask & (opp->pg.phase >> PG_SHIFT)]; - return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); - } - return 0; -} - - -static Int32 OpSynthRym( OPLSOUND* sndp, OPL_OP* opp ) -{ - if ( opp->enable ) - { - Uint32 tll; - OpStepNG(sndp, opp); - tll = opp->tll + (opp->eg.phase >> EG_SHIFT) + 0x10/* +6dB */; - tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll]; - tll += opp->tl_ofs; - tll += (opp->pg.rng & 1); - return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl); - } - return 0; -} - -inline static void LfoStep(OPL_LFO* lfop ) -{ - lfop->cnt += lfop->sps; - lfop->adr += lfop->cnt >> LFO_SHIFT; - lfop->cnt &= (1 << LFO_SHIFT) - 1; - lfop->output = lfop->table [lfop->adr & lfop->adrmask]; -} - -static int sndsynth( OPLSOUND* sndp ) -{ - Int32 accum = 0; - if ( sndp->common.enable ) - { - Uint32 i, rch; - for ( i = 0; i < LFO_UNIT_NUM; i++ ) - LfoStep(&sndp->lfo [i]); - - rch = sndp->common.rmode ? 7 : 9; - for ( i = 0; i < rch; i++ ) - { - if ( sndp->ch [i].op [0].modcar ) - OpSynthMod(sndp, &sndp->ch [i].op [0]); - else - accum += OpSynthCarFb(sndp, &sndp->ch [i].op [0]); - accum += OpSynthCar(sndp, &sndp->ch [i].op [1]); - } - if ( sndp->common.rmode ) - { - accum += OpSynthRym(sndp, &sndp->ch [7].op [0]); - accum += OpSynthRym(sndp, &sndp->ch [7].op [1]); - accum += OpSynthTom(sndp, &sndp->ch [8].op [0]); - accum += OpSynthRym(sndp, &sndp->ch [8].op [1]); - } - } - if ( sndp->deltatpcm ) - { - accum += sndp->deltatpcm->synth(sndp->deltatpcm->ctx); - } -#if 0 - /* NISE DAC */ - if ( accum >= 0 ) - accum = (Int32)(((Uint32) accum) & (((1 << 8) - 1) << (23 - 8))); - else - accum = -(Int32)(((Uint32)-accum) & (((1 << 8) - 1) << (23 - 8))); -#endif - return accum; -} - -static void sndvolume( OPLSOUND* sndp, Int32 volume ) -{ - if ( sndp->deltatpcm) sndp->deltatpcm->volume(sndp->deltatpcm->ctx, volume); - volume = (volume << (LOG_BITS - 8)) << 1; - sndp->common.mastervolume = volume; -} - -static Uint8 const op_table [0x20]= -{ - 0, 2, 4, 1, 3, 5,0xFF,0xFF, - 6, 8, 10, 7, 9, 11,0xFF,0xFF, - 12, 14, 16, 13, 15, 17,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, -}; - -static Uint8 const mul_table [0x10]= -{ - 1+0, 2+0, 4+0, 2+4, 8+0, 8+2, 8+4,16-2, - 16+0,16+2,16+4,16+4,16+8,16+8,32-2,32-2, -}; - -#define DB2TLL(x) (x * 2 / 375 ) -static Uint8 const ksl_table [8] [16]= -{ - { - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - },{ - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - DB2TLL( 0), DB2TLL( 750), DB2TLL( 1125), DB2TLL( 1500), - DB2TLL( 1875), DB2TLL( 2250), DB2TLL( 2625), DB2TLL( 3000), - },{ - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), - DB2TLL( 0), DB2TLL( 1125), DB2TLL( 1875), DB2TLL( 2625), - DB2TLL( 3000), DB2TLL( 3750), DB2TLL( 4125), DB2TLL( 4500), - DB2TLL( 4875), DB2TLL( 5250), DB2TLL( 5625), DB2TLL( 6000), - },{ - DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 1875), - DB2TLL( 3000), DB2TLL( 4125), DB2TLL( 4875), DB2TLL( 5625), - DB2TLL( 6000), DB2TLL( 6750), DB2TLL( 7125), DB2TLL( 7500), - DB2TLL( 7875), DB2TLL( 8250), DB2TLL( 8625), DB2TLL( 9000), - },{ - DB2TLL( 0), DB2TLL( 0), DB2TLL( 3000), DB2TLL( 4875), - DB2TLL( 6000), DB2TLL( 7125), DB2TLL( 7875), DB2TLL( 8625), - DB2TLL( 9000), DB2TLL( 9750), DB2TLL(10125), DB2TLL(10500), - DB2TLL(10875), DB2TLL(11250), DB2TLL(11625), DB2TLL(12000), - },{ - DB2TLL( 0), DB2TLL( 3000), DB2TLL( 6000), DB2TLL( 7875), - DB2TLL( 9000), DB2TLL(10125), DB2TLL(10875), DB2TLL(11625), - DB2TLL(12000), DB2TLL(12750), DB2TLL(13125), DB2TLL(13500), - DB2TLL(13875), DB2TLL(14250), DB2TLL(14625), DB2TLL(15000), - },{ - DB2TLL( 0), DB2TLL( 6000), DB2TLL( 9000), DB2TLL(10875), - DB2TLL(12000), DB2TLL(13125), DB2TLL(13875), DB2TLL(14625), - DB2TLL(15000), DB2TLL(15750), DB2TLL(16125), DB2TLL(16500), - DB2TLL(16875), DB2TLL(17250), DB2TLL(17625), DB2TLL(18000), - },{ - DB2TLL( 0), DB2TLL( 9000), DB2TLL(12000), DB2TLL(13875), - DB2TLL(15000), DB2TLL(16125), DB2TLL(16875), DB2TLL(17625), - DB2TLL(18000), DB2TLL(18750), DB2TLL(19125), DB2TLL(19500), - DB2TLL(19875), DB2TLL(20250), DB2TLL(20625), DB2TLL(21000), - } -}; -#undef DB2TLL - -static Uint32 rateconvAR( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr ) -{ - if ( !rrr ) - return 0; - rrr = rrr + (ksr >> 2); - if ( rrr >= 15) - return AR_PHASEMAX; - return sndp->common.ratetbla [ksr & 3] >> (CPS_SHIFTE + 1 - rrr); -} - -static Uint32 rateconv( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr ) -{ - if ( !rrr ) - return 0; - rrr = rrr + (ksr >> 2); - if ( rrr > 15 ) - rrr = 15; - return sndp->common.ratetbl [ksr & 3] >> (CPS_SHIFTE + 1 - rrr); -} - -static void OpUpdateWF( OPLSOUND* sndp, OPL_OP* opp ) -{ - opp->sintable = sndp->opltbl->sin_table [opp->wf & sndp->common.wfe]; -} - -static void OpUpdatePG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp ) -{ - opp->pg.spd = (((chp->freqh << 8) + chp->freql) * opp->mul * sndp->common.cpsp) >> (CPS_SHIFTP - chp->blk); -} - -static void OpUpdateEG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp ) -{ - Uint32 sr, rr; - opp->ksr = chp->kcode >> ((opp->flag & FLAG_KSR) ? 0 : 2); - opp->eg.dr_phasemax = opp->sl << (1 + 2 + EG_SHIFT); /* 3dB->eg */ - opp->eg.spd [EG_MODE_ATTACK] = rateconvAR(sndp, opp->ar, opp->ksr); - opp->eg.spd [EG_MODE_DECAY] = rateconv(sndp, opp->dr, opp->ksr); - if ( opp->flag & FLAG_EGT ) - { - if ( opp->eg.mode == EG_MODE_SUSTINE ) - opp->eg.mode = EG_MODE_SUSHOLD; - sr = 0; - rr = opp->rr; - } - else - { - if ( opp->eg.mode == EG_MODE_SUSHOLD ) - opp->eg.mode = EG_MODE_SUSTINE; - sr = opp->rr; - rr = 7; - } - if ( chp->sus ) - { - rr = 5; - } - opp->eg.spd [EG_MODE_SUSTINE] = rateconv(sndp, sr, opp->ksr); - opp->eg.spd [EG_MODE_RELEASE] = rateconv(sndp, rr, opp->ksr); -} - -static void OpUpdateTLL( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp ) -{ - opp->tll = (opp->tl + (chp->ksb >> opp->ksl)) << 1; -} - - - -static void oplsetopmul( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) -{ - opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR); -#if TESTING_OPTIMIZE_AME - if ( v & 0x80 ) - opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero; -#else - if ( v & 0x80 ) - opp->flag |= FLAG_AME; -#endif - if ( v & 0x40 ) opp->flag |= FLAG_PME; - if ( v & 0x20 ) opp->flag |= FLAG_EGT; - if ( v & 0x10 ) opp->flag |= FLAG_KSR; - opp->mul = mul_table [v & 0x0F]; - OpUpdateEG(sndp, chp, opp); - OpUpdatePG(sndp, chp, opp); -} - -static void oplsetopkstl( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) -{ - opp->ksl = (v >> 6) ? (3 - (v >> 6)) : 15; /* 0 / 1.5 / 3 / 6 db/OCT */ - opp->tl = v & 0x3F; /* 0.75 db */ - OpUpdateTLL(sndp, chp, opp); -} - -static void oplsetopardr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) -{ - opp->ar = v >> 4; - opp->dr = v & 0xF; - OpUpdateEG(sndp, chp, opp); -} - -static void oplsetopslrr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) -{ - opp->sl = v >> 4; - opp->rr = v & 0xF; - OpUpdateEG(sndp, chp, opp); -} - -static void oplsetopwf( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) -{ - opp->wf = v & 0x3; - OpUpdateWF(sndp, opp); -} - -static void oplsetopkey( OPLSOUND* sndp, OPL_OP* opp ) -{ - Uint8 nextkey = ((sndp->common.rmode) && opp->rkey) || opp->key; - if ( opp->prevkey ^ nextkey ) - { - opp->prevkey = nextkey; - if ( nextkey ) - { - sndp->common.enable = 1; - opp->eg.mode = EG_MODE_ATTACK; - opp->eg.phase = EG_KEYOFF; - opp->enable = 1; - opp->eg.phasear = 0; - } - else if ( !opp->modcar && opp->eg.mode != EG_MODE_OFF ) - opp->eg.mode = EG_MODE_RELEASE; - } -} - -static void oplsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) -{ - chp->freql = v & 0xFF; - chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)]; - OpUpdatePG(sndp, chp, &chp->op [0]); - OpUpdatePG(sndp, chp, &chp->op [1]); - OpUpdateTLL(sndp, chp, &chp->op [0]); - OpUpdateTLL(sndp, chp, &chp->op [1]); -} - -static void oplsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) -{ - Uint32 key = v & 0x20; - chp->kcode = (v >> 1) & 15; - chp->freqh = v & 3; - chp->blk = (v >> 2) & 7; - chp->op [0].key = chp->op [1].key = key; - oplsetopkey(sndp, &chp->op [0]); - oplsetopkey(sndp, &chp->op [1]); - chp->sus = 0; - chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)]; - OpUpdateEG(sndp, chp, &chp->op [0]); - OpUpdateEG(sndp, chp, &chp->op [1]); - OpUpdatePG(sndp, chp, &chp->op [0]); - OpUpdatePG(sndp, chp, &chp->op [1]); - OpUpdateTLL(sndp, chp, &chp->op [0]); - OpUpdateTLL(sndp, chp, &chp->op [1]); -} - -static void oplsetchfbcon( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) -{ - chp->op [0].fb = (v >> 1) & 7; -#if USE_FBBUF - chp->op [0].fbbuf = 0; -#endif - chp->con = v & 1; - chp->op [0].modcar = (chp->con) ? 0 : 1; - OpUpdateEG(sndp, chp, &chp->op [0]); - chp->op [1].input = 0; -} - -static void opllsetopvolume( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v ) -{ - opp->tl = v; - OpUpdateTLL(sndp, chp, opp); -} - -static void opllsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) -{ - chp->freql = v & 0xFF; - chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)]; - OpUpdatePG(sndp, chp, &chp->op [0]); - OpUpdatePG(sndp, chp, &chp->op [1]); - OpUpdateTLL(sndp, chp, &chp->op [0]); - OpUpdateTLL(sndp, chp, &chp->op [1]); -} - -static void opllsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v ) -{ - Uint32 key = v & 0x10; - chp->kcode = v & 15; - chp->freqh = v & 1; - chp->blk = (v >> 1) & 7; - chp->op [0].key = chp->op [1].key = key; - oplsetopkey(sndp, &chp->op [0]); - oplsetopkey(sndp, &chp->op [1]); - chp->sus = v & 0x20; - chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)]; - OpUpdateEG(sndp, chp, &chp->op [0]); - OpUpdateEG(sndp, chp, &chp->op [1]); - OpUpdatePG(sndp, chp, &chp->op [0]); - OpUpdatePG(sndp, chp, &chp->op [1]); - OpUpdateTLL(sndp, chp, &chp->op [0]); - OpUpdateTLL(sndp, chp, &chp->op [1]); -} - -static void SetOpTone( OPLSOUND* sndp, OPL_OP* opp, Uint8* tonep ) -{ - opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR); -#if TESTING_OPTIMIZE_AME - if ( tonep [0] & 0x80 ) opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero; -#else - if ( tonep [0] & 0x80 ) opp->flag |= FLAG_AME; -#endif - if ( tonep [0] & 0x40 ) opp->flag |= FLAG_PME; - if ( tonep [0] & 0x20 ) opp->flag |= FLAG_EGT; - if ( tonep [0] & 0x10 ) opp->flag |= FLAG_KSR; - opp->mul = mul_table [tonep [0] & 0x0F] << 1; - opp->ksl = (tonep [2] >> 6) ? (3 - (tonep [2] >> 6)) : 15; - opp->ar = tonep [4] >> 4; - opp->dr = tonep [4] & 0xF; - opp->sl = tonep [6] >> 4; - opp->rr = tonep [6] & 0xF; -} -static void SetChTone( OPLSOUND* sndp, OPL_CH* chp, Uint8* tonep, Uint8* tlofsp ) -{ - Uint32 op; - for ( op = 0; op < 2; op++ ) - SetOpTone(sndp, &chp->op [op], &tonep [op]); - chp->op [0].tl_ofs = (tlofsp [0] ^ 0x80) << (LOG_BITS - 4 + 1); - chp->op [1].tl_ofs = (tlofsp [1] ^ 0x80) << (LOG_BITS - 4 + 1); - chp->op [0].tl = tonep [2] & 0x3F; - chp->op [0].fb = tonep [3] & 0x7; - chp->op [0].wf = (tonep [3] >> 3) & 1; - chp->op [1].wf = (tonep [3] >> 4) & 1; -#if USE_FBBUF - chp->op [0].fbbuf = 0; -#endif - chp->op [1].input = 0; - OpUpdateWF(sndp, &chp->op [0]); - OpUpdateWF(sndp, &chp->op [1]); - OpUpdateEG(sndp, chp, &chp->op [0]); - OpUpdateEG(sndp, chp, &chp->op [1]); - OpUpdatePG(sndp, chp, &chp->op [0]); - OpUpdatePG(sndp, chp, &chp->op [1]); - OpUpdateTLL(sndp, chp, &chp->op [0]); - OpUpdateTLL(sndp, chp, &chp->op [1]); -} - -static void opllsetchtone( OPLSOUND* sndp, OPL_CH* chp, Uint32 tone ) -{ - SetChTone(sndp, chp, &sndp->regs [OPLL_INST_WORK + (tone << 3)], &sndp->regs [OPLL_INST_WORK2 + (tone << 1) + 0]); -} - -static void recovercon( OPLSOUND* sndp, OPL_CH* chp ) -{ - chp->op [0].modcar = (chp->con) ? 0 : 1; - chp->op [0].lvl = chp->con ? 1 : 0; - chp->op [1].lvl = 1; - OpUpdateEG(sndp, chp, &chp->op [0]); - chp->op [1].input = 0; -} - -static void initrc_common( OPLSOUND* sndp, Uint32 rmode ) -{ - if ( rmode ) - { - /* BD */ - sndp->ch [6].op [0].modcar = 1; - sndp->ch [6].op [0].lvl = 0; - OpUpdateEG(sndp, &sndp->ch [6], &sndp->ch [6].op [0]); - sndp->ch [6].op [1].input = 0; - sndp->ch [6].op [1].lvl = 2; - /* CYM */ - sndp->ch [7].op [0].modcar = 0; - sndp->ch [7].op [0].lvl = 1; - OpUpdateEG(sndp, &sndp->ch [7], &sndp->ch [7].op [0]); - /* SD */ - sndp->ch [7].op [1].input = 0; - sndp->ch [7].op [1].lvl = 2; - /* TOM */ - sndp->ch [8].op [0].modcar = 0; - sndp->ch [8].op [0].lvl = 2; - OpUpdateEG(sndp, &sndp->ch [8], &sndp->ch [8].op [0]); - /* HH */ - sndp->ch [8].op [1].input = 0; - sndp->ch [8].op [1].lvl = 1; - } - else - { - recovercon(sndp, &sndp->ch [6]); - if ( !sndp->ch [6].op [0].key ) SetOpOff(&sndp->ch [6].op [0]); - if ( !sndp->ch [6].op [1].key ) SetOpOff(&sndp->ch [6].op [1]); - recovercon(sndp, &sndp->ch [7]); - if ( !sndp->ch [7].op [0].key ) SetOpOff(&sndp->ch [7].op [0]); - if ( !sndp->ch [7].op [1].key ) SetOpOff(&sndp->ch [7].op [1]); - recovercon(sndp, &sndp->ch [8]); - if ( !sndp->ch [8].op [0].key ) SetOpOff(&sndp->ch [8].op [0]); - if ( !sndp->ch [8].op [1].key ) SetOpOff(&sndp->ch [8].op [1]); - } -} - -static void oplsetrc( OPLSOUND* sndp, Uint32 rc ) -{ - sndp->lfo [LFO_UNIT_AM].table = (rc & 0x80) ? sndp->opltbl->am_table1 : sndp->opltbl->am_table2; - sndp->lfo [LFO_UNIT_PM].table = (rc & 0x40) ? sndp->opltbl->pm_table1 : sndp->opltbl->pm_table2; - if ( (sndp->common.rmode ^ rc) & 0x20 ) - { - if ( rc & 0x20 ) - { -#if 0 - static Uint8 volini [2] = { 0, 0 }; - static Uint8 bdtone [8] = { 0x04, 0x20, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8 }; - SetChTone(sndp, &sndp->ch [6], bdtone, volini); - SetChTone(sndp, &sndp->ch [7], &romtone [0] [0x11 << 4], volini); - SetChTone(sndp, &sndp->ch [8], &romtone [0] [0x12 << 4], volini); -#endif - sndp->ch [7].op [0].nst = PG_SHIFT + 4; - sndp->ch [7].op [1].nst = PG_SHIFT + 6; - sndp->ch [8].op [1].nst = PG_SHIFT + 5; - } - initrc_common(sndp, rc & 0x20); - } - sndp->common.rmode = rc & 0x20; - sndp->common.rc = rc & 0x1F; - /* BD */ - sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10; - oplsetopkey(sndp, &sndp->ch [6].op [0]); - oplsetopkey(sndp, &sndp->ch [6].op [1]); - /* CYM */ - sndp->ch [7].op [0].rkey = rc & 0x01; - oplsetopkey(sndp, &sndp->ch [7].op [0]); - /* SD */ - sndp->ch [7].op [1].rkey = rc & 0x08; - oplsetopkey(sndp, &sndp->ch [7].op [1]); - /* TOM */ - sndp->ch [8].op [0].rkey = rc & 0x04; - oplsetopkey(sndp, &sndp->ch [8].op [0]); - /* HH */ - sndp->ch [8].op [1].rkey = rc & 0x02; - oplsetopkey(sndp, &sndp->ch [8].op [1]); -} - -static void opllsetrc( OPLSOUND* sndp, Uint32 rc ) -{ - if ( (sndp->common.rmode ^ rc) & 0x20 ) - { - if ( rc & 0x20 ) - { - opllsetchtone(sndp, &sndp->ch [6], 0x10); - opllsetchtone(sndp, &sndp->ch [7], 0x11); - opllsetchtone(sndp, &sndp->ch [8], 0x12); - opllsetopvolume(sndp, &sndp->ch [7], &sndp->ch [7].op [0], (sndp->regs [0x37] & 0xF0) >> 2); - opllsetopvolume(sndp, &sndp->ch [8], &sndp->ch [8].op [0], (sndp->regs [0x38] & 0xF0) >> 2); - sndp->ch [7].op [0].nst = PG_SHIFT + 5; - sndp->ch [7].op [1].nst = PG_SHIFT + 5; - sndp->ch [8].op [1].nst = PG_SHIFT + 5; - } - else - { - opllsetchtone(sndp, &sndp->ch [6], sndp->regs [0x36]>>4); - opllsetchtone(sndp, &sndp->ch [7], sndp->regs [0x37]>>4); - opllsetchtone(sndp, &sndp->ch [8], sndp->regs [0x38]>>4); - } - initrc_common(sndp, rc & 0x20); - } - sndp->common.rmode = rc & 0x20; - sndp->common.rc = rc & 0x1F; - /* BD */ - sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10; - oplsetopkey(sndp, &sndp->ch [6].op [0]); - oplsetopkey(sndp, &sndp->ch [6].op [1]); - /* CYM */ - sndp->ch [7].op [0].rkey = rc & 0x01; - oplsetopkey(sndp, &sndp->ch [7].op [0]); - /* SD */ - sndp->ch [7].op [1].rkey = rc & 0x08; - oplsetopkey(sndp, &sndp->ch [7].op [1]); - /* TOM */ - sndp->ch [8].op [0].rkey = rc & 0x04; - oplsetopkey(sndp, &sndp->ch [8].op [0]); - /* HH */ - sndp->ch [8].op [1].rkey = rc & 0x02; - oplsetopkey(sndp, &sndp->ch [8].op [1]); -} - -#define OPLSETOP(func) { \ - Uint32 op = op_table [a & 0x1F]; \ - if ( op != 0xFF) func(sndp, &sndp->ch [op >> 1], &sndp->ch [op >> 1].op [op & 1], v); \ -} - -__inline static void oplwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v ) -{ - switch ( a >> 5 ) - { - default: - NEVER_REACH - - case 0: - switch ( a & 0x1F ) - { - case 0x01: - if ( sndp->opl_type == OPL_TYPE_OPL2 ) - { - Uint32 i; - sndp->common.wfe = (v & 0x20) ? 3 : 0; - for ( i = 0; i < 9; i++ ) - { - OpUpdateWF(sndp, &sndp->ch [i].op [0]); - OpUpdateWF(sndp, &sndp->ch [i].op [1]); - } - } - break; - - case 0x08: - /* CSM mode */ - case 0x07: case 0x09: case 0x0A: case 0x0B: case 0x0C: - case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: - if ( sndp->deltatpcm ) - sndp->deltatpcm->write(sndp->deltatpcm->ctx, a - 0x07, v); - break; - } - break; - - case 1: OPLSETOP(oplsetopmul); break; - case 2: OPLSETOP(oplsetopkstl); break; - case 3: OPLSETOP(oplsetopardr); break; - case 4: OPLSETOP(oplsetopslrr); break; - case 7: OPLSETOP(oplsetopwf); break; - case 5: - if ( (a & 0x1F) == (0xBD & 0x1F) ) - oplsetrc(sndp, v); - else if ( (a & 0x1F) < 9 ) - oplsetchfreql(sndp, &sndp->ch [a & 0xF], v); - else if ( (a & 0xF) < 9 ) - oplsetchfreqh(sndp, &sndp->ch [a & 0xF], v); - break; - - case 6: - if ( (a & 0x1F) < 9) oplsetchfbcon(sndp, &sndp->ch [a & 0xF], v); - break; - } -} - -static void oplwrite( OPLSOUND* sndp, Uint32 a, Uint32 v ) -{ - if ( a & 1 ) - { - sndp->regs [sndp->common.adr] = v; - oplwritereg(sndp, sndp->common.adr, v); - } - else - sndp->common.adr = v; -} - -static Uint32 oplread( OPLSOUND* sndp, Uint32 a ) -{ - if ( a & 1 ) - return sndp->regs [sndp->common.adr]; - else - return 0x80; -} - -__inline static void opllwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v ) -{ - switch ( a >> 3 ) - { - default: - NEVER_REACH - case 0: - sndp->regs [OPLL_INST_WORK + (a & 7)] = v; - break; - - case 1: - if ( a == 0xE) opllsetrc(sndp, v & 0x3F); - break; - - case 2: - case 3: - a &= 0xF; - if ( a < 9) opllsetchfreql(sndp, &sndp->ch [a], v); - break; - - case 4: - case 5: - a &= 0xF; - if ( a < 9) opllsetchfreqh(sndp, &sndp->ch [a], v); - break; - - case 6: - case 7: - a &= 0xF; - if ( a < 9 ) - { - if ( (sndp->common.rmode) && (a >= 6) ) - { - if ( a != 6) opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [0], (v & 0xF0) >> 2); - } - else - { - opllsetchtone(sndp, &sndp->ch [a], (v & 0xF0) >> 4); - } - opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [1], (v & 0xF) << 2); - } - break; - } -} - -static void opllwrite( OPLSOUND* sndp, Uint32 a, Uint32 v ) -{ - if ( a & 1 ) - { - if ( sndp->common.adr < 0x40 ) - { - sndp->regs [sndp->common.adr] = v; - opllwritereg(sndp, sndp->common.adr, v); - } - } - else - sndp->common.adr = v; -} - -static Uint32 opllread( OPLSOUND* sndp, Uint32 a ) -{ - return 0xFF; -} - -static void opreset( OPLSOUND* sndp, OPL_OP* opp ) -{ - /* XMEMSET(opp, 0, sizeof(OPL_OP)); */ - SetOpOff(opp); - opp->tl_ofs = 0x80 << (LOG_BITS - 4 + 1); -#if TESTING_OPTIMIZE_AME - opp->amin = &sndp->common.amzero; -#endif - opp->pg.rng = 0xFFFF; -} - -static void chreset( OPLSOUND* sndp, OPL_CH* chp, Uint32 clock, Uint32 freq ) -{ - Uint32 op; - XMEMSET(chp, 0, sizeof(OPL_CH)); - for ( op = 0; op < 2; op++ ) - { - opreset(sndp, &chp->op [op]); - } - recovercon(sndp, chp); -} - -static void sndreset( OPLSOUND* sndp, Uint32 clock, Uint32 freq ) -{ - Uint32 i, cpse; - XMEMSET(&sndp->common, 0, sizeof(sndp->common)); - XMEMSET(&sndp->lfo [LFO_UNIT_AM], 0, sizeof(OPL_LFO)); - sndp->lfo [LFO_UNIT_AM].sps = DivFix(37 * (1 << AMTBL_BITS), freq * 10, LFO_SHIFT); - sndp->lfo [LFO_UNIT_AM].adrmask = (1 << AMTBL_BITS) - 1; - sndp->lfo [LFO_UNIT_AM].table = sndp->opltbl->am_table1; - XMEMSET(&sndp->lfo [LFO_UNIT_PM], 0, sizeof(OPL_LFO)); - sndp->lfo [LFO_UNIT_PM].sps = DivFix(64 * (1 << PMTBL_BITS), freq * 10, LFO_SHIFT); - sndp->lfo [LFO_UNIT_PM].adrmask = (1 << PMTBL_BITS) - 1; - sndp->lfo [LFO_UNIT_PM].table = sndp->opltbl->pm_table1; - sndp->common.cpsp = DivFix(clock, 72 * freq, CPS_SHIFTP); - cpse = DivFix(clock, 72 * freq, CPS_SHIFTE); - for ( i = 0; i < 4; i++ ) - { - sndp->common.ratetbl [i] = (i + 4) * cpse; - sndp->common.ratetbla [i] = 3 * sndp->common.ratetbl [i]; - } - sndp->common.tll2logtbl = sndp->opltbl->tll2log_table; - sndp->common.sintablemask = (1 << SINTBL_BITS) - 1; - - for ( i = 0; i < 9; i++ ) - chreset(sndp, &sndp->ch [i], clock, freq); - - if ( sndp->deltatpcm ) - sndp->deltatpcm->reset(sndp->deltatpcm->ctx, clock, freq); - - if ( sndp->opl_type & OPL_TYPE_OPL ) - { - XMEMSET(&sndp->regs, 0, 0x100); - sndp->common.ar_table = sndp->opltbl->ar_tablepow; - sndp->common.sintablemask -= (1 << (SINTBL_BITS - 11)) - 1; - for ( i = 0x0; i < 0x100; i++ ) - { - oplwrite(sndp, 0, i); - oplwrite(sndp, 1, 0x00); - } - for ( i = 0xA0; i < 0xA9; i++ ) - { - oplwrite(sndp, 0, 0xA0 + i); - oplwrite(sndp, 1, 0x40); - oplwrite(sndp, 0, 0xB0 + i); - oplwrite(sndp, 1, 0x0E); - } - } - else - { - static Uint8 const fmbios_initdata [9] = "\x30\x10\x20\x20\xfb\xb2\xf3\xf3"; - XMEMSET(&sndp->regs, 0, 0x40); - sndp->common.ar_table = sndp->opltbl->ar_tablelog; - sndp->common.wfe = 1; - sndp->common.sintablemask -= (1 << (SINTBL_BITS - 8)) - 1; - for ( i = 0; i < sizeof(fmbios_initdata)-1; i++ ) - { - opllwrite(sndp, 0, i); - opllwrite(sndp, 1, fmbios_initdata [i]); - } - opllwrite(sndp, 0, 0x0E); - opllwrite(sndp, 1, 0x00); - opllwrite(sndp, 0, 0x0F); - opllwrite(sndp, 1, 0x00); - for ( i = 0; i < 9; i++ ) - { - opllwrite(sndp, 0, 0x10 + i); - opllwrite(sndp, 1, 0x20); - opllwrite(sndp, 0, 0x20 + i); - opllwrite(sndp, 1, 0x07); - opllwrite(sndp, 0, 0x30 + i); - opllwrite(sndp, 1, 0xB3); - } - } -} - -static void oplsetinst( OPLSOUND* sndp, Uint32 n, void* p, Uint32 l ) -{ - if ( sndp->deltatpcm) sndp->deltatpcm->setinst(sndp->deltatpcm->ctx, n, p, l); -} - -__inline static Uint32 GetDwordLE(Uint8* p ) -{ - return p [0] | (p [1] << 8) | (p [2] << 16) | (p [3] << 24); -} -#define GetDwordLEM(p) (Uint32)((((Uint8* )p) [0] | (((Uint8* )p) [1] << 8) | (((Uint8* )p) [2] << 16) | (((Uint8* )p) [3] << 24)) ) - -static void opllsetinst( OPLSOUND* sndp, Uint32 n, Uint8* p, Uint32 l ) -{ - Int32 i, j, sb = 9; - if ( n ) - return; - if ( (GetDwordLE(p) & 0xF0FFFFFF) == GetDwordLEM("ILL0") ) - { - if ( 0 < p [4] && p [4] <= SINTBL_BITS) sb = p [4]; - for ( j = 1; j < 16 + 3; j++ ) - for ( i = 0; i < 8; i++ ) - sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [(j << 4) + i]; - for ( j = 0; j < 16 + 3; j++ ) - { - sndp->regs [OPLL_INST_WORK2 + (j << 1) + 0] = p [(j << 4) + 8]; - sndp->regs [OPLL_INST_WORK2 + (j << 1) + 1] = p [(j << 4) + 9]; - } - } - else - { - for ( j = 1; j < 16; j++ ) - for ( i = 0; i < 8; i++ ) - sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [((j - 1) << 3) + i]; - } - sndp->common.sintablemask = (1 << SINTBL_BITS) - 1; - sndp->common.sintablemask -= (1 << (SINTBL_BITS - sb)) - 1; -} - -static void sndrelease( OPLSOUND* sndp ) -{ - if ( sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx); - if ( sndp->opltbl) sndp->opltbl->release(sndp->opltbl->ctx); - if ( sndp->deltatpcm) sndp->deltatpcm->release(sndp->deltatpcm->ctx); - XFREE(sndp); -} - -KMIF_SOUND_DEVICE* OPLSoundAlloc(Uint32 opl_type ) -{ - OPLSOUND* sndp; - sndp = (OPLSOUND*) XMALLOC(sizeof(OPLSOUND)); - if ( !sndp) return 0; - sndp->opl_type = opl_type; - sndp->kmif.ctx = sndp; - sndp->kmif.release = (void (*)( void* )) sndrelease; - sndp->kmif.volume = (void (*)( void*, int )) sndvolume; - sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset; - sndp->kmif.synth = (int (*)( void* )) sndsynth; - if ( sndp->opl_type == OPL_TYPE_MSXAUDIO ) - { - sndp->deltatpcm = YMDELTATPCMSoundAlloc(YMDELTATPCM_TYPE_Y8950); - } - else - sndp->deltatpcm = 0; - if ( sndp->opl_type & OPL_TYPE_OPL ) - { - sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) oplwrite; - sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) oplread; - sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) oplsetinst; - } - else - { - sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) opllwrite; - sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) opllread; - sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) opllsetinst; - switch ( sndp->opl_type ) - { - case OPL_TYPE_OPLL: - case OPL_TYPE_MSXMUSIC: - opllsetinst(sndp, 0, romtone [0], 16 * 19); - break; - - case OPL_TYPE_SMSFMUNIT: - opllsetinst(sndp, 0, romtone [1], 16 * 19); - break; - - case OPL_TYPE_VRC7: - opllsetinst(sndp, 0, romtone [2], 16 * 19); - break; - } - } - sndp->logtbl = LogTableAddRef(); - sndp->opltbl = OplTableAddRef(); - if ( !sndp->logtbl || !sndp->opltbl ) - { - sndrelease(sndp); - return 0; - } - - return &sndp->kmif; -} diff --git a/Frameworks/GME/gme/s_opl.h b/Frameworks/GME/gme/s_opl.h deleted file mode 100644 index 227c03c3c..000000000 --- a/Frameworks/GME/gme/s_opl.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef S_OPL_H__ -#define S_OPL_H__ - -#include "kmsnddev.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - OPL_TYPE_OPLL = 0x10, /* YAMAHA YM2413 */ - OPL_TYPE_MSXMUSIC = 0x11, /* YAMAHA YM2413 */ - OPL_TYPE_SMSFMUNIT = 0x12, /* YAMAHA YM2413? */ - OPL_TYPE_VRC7 = 0x13, /* KONAMI 053982 VRV VII */ - OPL_TYPE_OPL = 0x20, /* YAMAHA YM3526 */ - OPL_TYPE_MSXAUDIO = 0x21, /* YAMAHA Y8950 */ - OPL_TYPE_OPL2 = 0x22 /* YAMAHA YM3812 */ -}; - -KMIF_SOUND_DEVICE *OPLSoundAlloc(Uint32 opl_type); - -#ifdef __cplusplus -} -#endif - -#endif /* S_OPL_H__ */ diff --git a/Frameworks/GME/gme/s_opltbl.c b/Frameworks/GME/gme/s_opltbl.c deleted file mode 100644 index 35e54edbd..000000000 --- a/Frameworks/GME/gme/s_opltbl.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "nestypes.h" -#include "s_logtbl.h" -#include "s_opltbl.h" - -#if STATIC_TABLES - -static void OplTableRelease(void *ctx) -{ -} - -static KMIF_OPLTABLE opl_static_tables = { - &opl_static_tables; - OplTableRelease, -#include "s_oplt.h" -}; - -KMIF_OPLTABLE *OplTableAddRef(void) -{ - opl_static_tables.release = OplTableRelease; - return &opl_static_tables; -} - -#else - -#include -#ifndef M_PI -#ifdef PI -#define M_PI PI -#else -#define M_PI 3.14159265358979323846 -#endif -#endif - -#define AM_DEPTH1 4.8 /* dB */ -#define AM_DEPTH2 1.0 /* dB */ -#define PM_DEPTH1 14.0 /* cent */ -#define PM_DEPTH2 7.0 /* cent */ -#define LOG_KEYOFF (15 << (LOG_BITS + 1)) - -#define DB0375_TO_LOG(x) ((Uint32)(0.375 * (1 << (LOG_BITS + x)) / 10)) - -#define AR_OFF (128 << ARTBL_SHIFT) -#define AR_MAX (127 << ARTBL_SHIFT) - - -static volatile Uint32 opl_tables_mutex = 0; -static Uint32 opl_tables_refcount = 0; -static KMIF_OPLTABLE *opl_tables = 0; - -static void OplTableRelease(void *ctx) -{ - ++opl_tables_mutex; - while (opl_tables_mutex != 1) - { - XSLEEP(0); - } - opl_tables_refcount--; - if (!opl_tables_refcount) - { - XFREE(ctx); - opl_tables = 0; - } - --opl_tables_mutex; -} - -static void OplTableCalc(KMIF_OPLTABLE *tbl) -{ - Uint32 u, u2, i; - tbl->sin_table[0][0] = tbl->sin_table[0][1 << (SINTBL_BITS - 1)] = LOG_KEYOFF; - for (i = 1 ;i < (1 << (SINTBL_BITS - 1)); i++) - { - double d; - d = (1 << LOG_BITS) * -(log(sin(2.0 * M_PI * ((double)i) / (1 << SINTBL_BITS))) / log(2)); - if (d > (LOG_KEYOFF >> 1)) d = (LOG_KEYOFF >> 1); - tbl->sin_table[0][i] = ((Uint32)d) << 1; - tbl->sin_table[0][i + (1 << (SINTBL_BITS - 1))] = (((Uint32)d) << 1) + 1; - } - for (i = 0 ;i < (1 << SINTBL_BITS); i++) - { - tbl->sin_table[1][i] = (tbl->sin_table[0][i] & 1) ? tbl->sin_table[0][0] : tbl->sin_table[0][i]; - tbl->sin_table[2][i] = tbl->sin_table[0][i] & ~1; - tbl->sin_table[3][i] = (i & (1 << (SINTBL_BITS - 2))) ? LOG_KEYOFF : tbl->sin_table[2][i]; - } - for (i = 0; i < (1 << TLLTBL_BITS); i++) - { - tbl->tll2log_table[i] = (i * DB0375_TO_LOG(0)) << 1; - } - for (i = 0; i < (1 << AMTBL_BITS); i++) - { - u = (Uint32)((1 + sin(2 * M_PI * ((double)i) / (1 << AMTBL_BITS))) * ((1 << LOG_BITS) * AM_DEPTH1 / 20.0)); - u2 = (Uint32)((1 + sin(2 * M_PI * ((double)i) / (1 << AMTBL_BITS))) * ((1 << LOG_BITS) * AM_DEPTH2 / 20.0)); - tbl->am_table1[i] = u << 1; - tbl->am_table2[i] = u2 << 1; - } - for (i = 0; i < (1 << PMTBL_BITS); i++) - { - u = (Uint32)((1 << PM_SHIFT) * pow(2, sin(2 * M_PI * ((double)i) / (1 << PMTBL_BITS)) * PM_DEPTH1 / 1200.0)); - u2 = (Uint32)((1 << PM_SHIFT) * pow(2, sin(2 * M_PI * ((double)i) / (1 << PMTBL_BITS)) * PM_DEPTH2 / 1200.0)); - tbl->pm_table1[i] = u; - tbl->pm_table2[i] = u2; - } - - for (i = 0; i < (1 << ARTBL_BITS); i++) - { - u = (Uint32)(((double)AR_MAX) * (1 - log(1 + i) / log(1 << ARTBL_BITS))); - tbl->ar_tablelog[i] = u; -#if 1 - u = (Uint32)(((double)AR_MAX) * (pow(1 - i / (double)(1 << ARTBL_BITS), 8))); - tbl->ar_tablepow[i] = u; -#endif - } -} - -KMIF_OPLTABLE *OplTableAddRef(void) -{ - ++opl_tables_mutex; - while (opl_tables_mutex != 1) - { - XSLEEP(0); - } - if (!opl_tables_refcount) - { - opl_tables = (KMIF_OPLTABLE*) XMALLOC(sizeof(KMIF_OPLTABLE)); - if (opl_tables) - { - opl_tables->ctx = opl_tables; - opl_tables->release = OplTableRelease; - OplTableCalc(opl_tables); - } - } - if (opl_tables) opl_tables_refcount++; - --opl_tables_mutex; - return opl_tables; -} - -#endif diff --git a/Frameworks/GME/gme/s_opltbl.h b/Frameworks/GME/gme/s_opltbl.h deleted file mode 100644 index 7a3f805f7..000000000 --- a/Frameworks/GME/gme/s_opltbl.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef S_OPLTBL_H__ -#define S_OPLTBL_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#define SINTBL_BITS 11 -#define AMTBL_BITS 8 -#define PMTBL_BITS 8 -#define PM_SHIFT 9 -#define ARTBL_BITS 7 -#define ARTBL_SHIFT 20 -#define TLLTBL_BITS 7 - -typedef struct -{ - void *ctx; - void (*release)(void *ctx); - Uint32 sin_table[4][1 << SINTBL_BITS]; - Uint32 tll2log_table[1 << TLLTBL_BITS]; - Uint32 ar_tablelog[1 << ARTBL_BITS]; - Uint32 am_table1[1 << AMTBL_BITS]; - Uint32 pm_table1[1 << PMTBL_BITS]; -#if 1 - Uint32 ar_tablepow[1 << ARTBL_BITS]; -#endif - Uint32 am_table2[1 << AMTBL_BITS]; - Uint32 pm_table2[1 << PMTBL_BITS]; -} KMIF_OPLTABLE; - -KMIF_OPLTABLE *OplTableAddRef(void); - -#ifdef __cplusplus -} -#endif - -#endif /* S_OPLTBL_H__ */ diff --git a/Frameworks/GME/gme/ym2413.h b/Frameworks/GME/gme/ym2413.h deleted file mode 100644 index ebd51495e..000000000 --- a/Frameworks/GME/gme/ym2413.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#ifndef __YM2413_H__ -#define __YM2413_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* select output bits size of output : 8 or 16 */ -#define SAMPLE_BITS 16 - -#include "mamedef.h" - -typedef stream_sample_t SAMP; -/* -#if (SAMPLE_BITS==16) -typedef INT16 SAMP; -#endif -#if (SAMPLE_BITS==8) -typedef INT8 SAMP; -#endif -*/ - - - -void *ym2413_init(int clock, int rate, int type); -void ym2413_shutdown(void *chip); -void ym2413_reset_chip(void *chip); -void ym2413_write(void *chip, int a, int v); -unsigned char ym2413_read(void *chip, int a); -void ym2413_update_one(void *chip, SAMP **buffers, int length); - -void ym2413_advance_lfo(void *chip); /* call this once */ -SAMP ym2413_calcch(void *chip, int ch); /* then call this for each channel */ -void ym2413_advance(void *chip); /* then call this */ - -void * ym2413_get_inst0(void *chip); - -void ym2413_set_mask(void *chip, UINT32 mask); - -typedef void (*OPLL_UPDATEHANDLER)(void *param,int min_interval_us); - -void ym2413_set_update_handler(void *chip, OPLL_UPDATEHANDLER UpdateHandler, void *param); - -#ifdef __cplusplus -} -#endif - -#endif /*__YM2413_H__*/ diff --git a/Frameworks/GME/vgmplay/.gitignore b/Frameworks/GME/vgmplay/.gitignore new file mode 100644 index 000000000..42a83897f --- /dev/null +++ b/Frameworks/GME/vgmplay/.gitignore @@ -0,0 +1,3 @@ +/obj +/vgm2pcm +/vgmplay diff --git a/Frameworks/GME/vgmplay/ChipMapper.c b/Frameworks/GME/vgmplay/ChipMapper.c new file mode 100644 index 000000000..0c02e8359 --- /dev/null +++ b/Frameworks/GME/vgmplay/ChipMapper.c @@ -0,0 +1,170 @@ +// ChipMapper.c - Handles Chip Write (including OPL Hardware Support) + +#include +#include +#include +#include +#include "stdbool.h" + +#include "chips/mamedef.h" + +#include "chips/ChipIncl.h" + +#include "VGMPlay.h" + +#include "ChipMapper.h" + +void chip_reg_write(void *param, UINT8 ChipType, UINT8 ChipID, + UINT8 Port, UINT8 Offset, UINT8 Data) +{ + VGM_PLAYER* p = (VGM_PLAYER *) param; + switch(ChipType) + { + case 0x00: // SN76496 + sn764xx_w(p->sn764xx[ChipID], Port, Data); + break; + case 0x01: // YM2413 + ym2413_w(p->ym2413[ChipID], 0x00, Offset); + ym2413_w(p->ym2413[ChipID], 0x01, Data); + break; + case 0x02: // YM2612 + ym2612_w(p->ym2612[ChipID], (Port << 1) | 0x00, Offset); + ym2612_w(p->ym2612[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x03: // YM2151 + ym2151_w(p->ym2151[ChipID], 0x00, Offset); + ym2151_w(p->ym2151[ChipID], 0x01, Data); + break; + case 0x04: // SegaPCM + break; + case 0x05: // RF5C68 + rf5c68_w(p->rf5c68, Offset, Data); + break; + case 0x06: // YM2203 + ym2203_w(p->ym2203[ChipID], 0x00, Offset); + ym2203_w(p->ym2203[ChipID], 0x01, Data); + break; + case 0x07: // YM2608 + ym2608_w(p->ym2608[ChipID], (Port << 1) | 0x00, Offset); + ym2608_w(p->ym2608[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x08: // YM2610/YM2610B + ym2610_w(p->ym2610[ChipID], (Port << 1) | 0x00, Offset); + ym2610_w(p->ym2610[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x09: // YM3812 + ym3812_w(p->ym3812[ChipID], 0x00, Offset); + ym3812_w(p->ym3812[ChipID], 0x01, Data); + break; + case 0x0A: // YM3526 + ym3526_w(p->ym3526[ChipID], 0x00, Offset); + ym3526_w(p->ym3526[ChipID], 0x01, Data); + break; + case 0x0B: // Y8950 + y8950_w(p->y8950[ChipID], 0x00, Offset); + y8950_w(p->y8950[ChipID], 0x01, Data); + break; + case 0x0C: // YMF262 + ymf262_w(p->ymf262[ChipID], (Port << 1) | 0x00, Offset); + ymf262_w(p->ymf262[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x0D: // YMF278B + ymf278b_w(p->ymf278b[ChipID], (Port << 1) | 0x00, Offset); + ymf278b_w(p->ymf278b[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x0E: // YMF271 + ymf271_w(p->ymf271[ChipID], (Port << 1) | 0x00, Offset); + ymf271_w(p->ymf271[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x0F: // YMZ280B + ymz280b_w(p->ymz280b[ChipID], 0x00, Offset); + ymz280b_w(p->ymz280b[ChipID], 0x01, Data); + break; + case 0x10: // RF5C164 + rf5c164_w(p->rf5c164, Offset, Data); + break; + case 0x11: // PWM + pwm_chn_w(p->pwm, Port, (Offset << 8) | (Data << 0)); + break; + case 0x12: // AY8910 + ayxx_w(p->ay8910[ChipID], 0x00, Offset); + ayxx_w(p->ay8910[ChipID], 0x01, Data); + break; + case 0x13: // GameBoy + gb_sound_w(p->gbdmg[ChipID], Offset, Data); + break; + case 0x14: // NES APU + nes_w(p->nesapu[ChipID], Offset, Data); + break; + case 0x15: // MultiPCM + multipcm_w(p->multipcm[ChipID], Offset, Data); + break; + case 0x16: // UPD7759 + upd7759_write(p->upd7759[ChipID], Offset, Data); + break; + case 0x17: // OKIM6258 + okim6258_write(p->okim6258[ChipID], Offset, Data); + break; + case 0x18: // OKIM6295 + okim6295_w(p->okim6295[ChipID], Offset, Data); + break; + case 0x19: // K051649 / SCC1 + k051649_w(p->k051649[ChipID], (Port << 1) | 0x00, Offset); + k051649_w(p->k051649[ChipID], (Port << 1) | 0x01, Data); + break; + case 0x1A: // K054539 + k054539_w(p->k054539[ChipID], (Port << 8) | (Offset << 0), Data); + break; + case 0x1B: // HuC6280 + c6280_w(p->huc6280[ChipID], Offset, Data); + break; + case 0x1C: // C140 + c140_w(p->c140[ChipID], (Port << 8) | (Offset << 0), Data); + break; + case 0x1D: // K053260 + k053260_w(p->k053260[ChipID], Offset, Data); + break; + case 0x1E: // Pokey + pokey_w(p->pokey[ChipID], Offset, Data); + break; + case 0x1F: // QSound + qsound_w(p->qsound[ChipID], 0x00, Port); // Data MSB + qsound_w(p->qsound[ChipID], 0x01, Offset); // Data LSB + qsound_w(p->qsound[ChipID], 0x02, Data); // Register + break; + case 0x20: // YMF292/SCSP + scsp_w(p->scsp[ChipID], (Port << 8) | (Offset << 0), Data); + break; + case 0x21: // WonderSwan + ws_audio_port_write(p->wswan[ChipID], 0x80 | Offset, Data); + break; + case 0x22: // VSU + VSU_Write(p->vsu[ChipID], (Port << 8) | (Offset << 0), Data); + break; + case 0x23: // SAA1099 + saa1099_control_w(p->saa1099[ChipID], 0, Offset); + saa1099_data_w(p->saa1099[ChipID], 0, Data); + break; + case 0x24: // ES5503 + es5503_w(p->es5503[ChipID], Offset, Data); + break; + case 0x25: // ES5506 + if (Port & 0x80) + es550x_w16(p->es550x[ChipID], Port & 0x7F, (Offset << 8) | (Data << 0)); + else + es550x_w(p->es550x[ChipID], Port, Data); + break; + case 0x26: // X1-010 + seta_sound_w(p->x1_010[ChipID], (Port << 8) | (Offset << 0), Data); + break; + case 0x27: // C352 + c352_w(p->c352[ChipID], Port, (Offset << 8) | (Data << 0)); + break; + case 0x28: // GA20 + irem_ga20_w(p->ga20[ChipID], Offset, Data); + break; +// case 0x##: // OKIM6376 +// break; + } + return; +} diff --git a/Frameworks/GME/vgmplay/ChipMapper.h b/Frameworks/GME/vgmplay/ChipMapper.h new file mode 100644 index 000000000..c61579f17 --- /dev/null +++ b/Frameworks/GME/vgmplay/ChipMapper.h @@ -0,0 +1 @@ +void chip_reg_write(void *, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data); diff --git a/Frameworks/GME/vgmplay/Makefile b/Frameworks/GME/vgmplay/Makefile new file mode 100644 index 000000000..0de15b510 --- /dev/null +++ b/Frameworks/GME/vgmplay/Makefile @@ -0,0 +1,202 @@ +######################## +# +# VGMPlay Makefile +# (for GNU Make 3.81) +# +######################## + +# TODO: Make this look less horrible. +# TODO: Make it not include pt_ioctl.c when DISABLE_HW_SUPPORT is active. + +# Uncomment if you build on Windows using MinGW. +#WINDOWS = 1 + +# Uncomment if you want to use libao instead of OSS for sound streaming under Linux +USE_LIBAO = 1 + +CC = gcc +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +# -- VGMPlay specific Compile Flags -- +MAINFLAGS := -DCONSOLE_MODE -DADDITIONAL_FORMATS -DSET_CONSOLE_TITLE +ifdef WINDOWS +# MinGW defines __WINDOWS__, Visual Studio defines WIN32 +MAINFLAGS += -DWIN32 +else +ifdef USE_LIBAO +MAINFLAGS += -DUSE_LIBAO +endif +endif +EMUFLAGS := -DENABLE_ALL_CORES + +#MAINFLAGS := -DVGM_BIG_ENDIAN +#EMUFLAGS := -DVGM_BIG_ENDIAN + +#MAINFLAGS := -DDISABLE_HW_SUPPORT + + +# -- General Compile Flags -- +CFLAGS := -O3 -g0 $(CFLAGS) +# libm (math library) and libz (zlib) +LDFLAGS := -lm -lz $(LDFLAGS) +ifdef WINDOWS +# for Windows, add kernel32 and winmm (Multimedia APIs) +LDFLAGS += -lkernel32 -lwinmm +else +# for Linux, add librt (clock stuff) and libpthread (threads) +LDFLAGS += -lrt -lpthread -pthread +MAINFLAGS += -pthread -DSHARE_PREFIX=\"$(PREFIX)\" +ifdef USE_LIBAO +LDFLAGS += -lao +endif +endif +# add Library Path, if defined +ifdef LD_LIBRARY_PATH +LDFLAGS += -L $(LD_LIBRARY_PATH) +endif + +SRC = . +OBJ = obj +EMUSRC = $(SRC)/chips +EMUOBJ = $(OBJ)/chips + +OBJDIRS = \ + $(OBJ) \ + $(EMUOBJ) +MAINOBJS = \ + $(OBJ)/VGMPlay.o \ + $(OBJ)/VGMPlay_AddFmts.o \ + $(OBJ)/Stream.o \ + $(OBJ)/ChipMapper.o +ifdef WINDOWS +MAINOBJS += $(OBJ)/pt_ioctl.o +endif +EMUOBJS = \ + $(EMUOBJ)/262intf.o \ + $(EMUOBJ)/2151intf.o \ + $(EMUOBJ)/2203intf.o \ + $(EMUOBJ)/2413intf.o \ + $(EMUOBJ)/2608intf.o \ + $(EMUOBJ)/2610intf.o \ + $(EMUOBJ)/2612intf.o \ + $(EMUOBJ)/3526intf.o \ + $(EMUOBJ)/3812intf.o \ + $(EMUOBJ)/8950intf.o \ + $(EMUOBJ)/adlibemu_opl2.o \ + $(EMUOBJ)/adlibemu_opl3.o \ + $(EMUOBJ)/ay8910.o \ + $(EMUOBJ)/ay_intf.o \ + $(EMUOBJ)/c140.o \ + $(EMUOBJ)/c352.o \ + $(EMUOBJ)/c6280.o \ + $(EMUOBJ)/c6280intf.o \ + $(EMUOBJ)/dac_control.o \ + $(EMUOBJ)/es5503.o \ + $(EMUOBJ)/es5506.o \ + $(EMUOBJ)/emu2149.o \ + $(EMUOBJ)/emu2413.o \ + $(EMUOBJ)/fm2612.o \ + $(EMUOBJ)/fm.o \ + $(EMUOBJ)/fmopl.o \ + $(EMUOBJ)/gb.o \ + $(EMUOBJ)/iremga20.o \ + $(EMUOBJ)/k051649.o \ + $(EMUOBJ)/k053260.o \ + $(EMUOBJ)/k054539.o \ + $(EMUOBJ)/multipcm.o \ + $(EMUOBJ)/nes_apu.o \ + $(EMUOBJ)/nes_intf.o \ + $(EMUOBJ)/np_nes_apu.o \ + $(EMUOBJ)/np_nes_dmc.o \ + $(EMUOBJ)/np_nes_fds.o \ + $(EMUOBJ)/okim6258.o \ + $(EMUOBJ)/okim6295.o \ + $(EMUOBJ)/Ootake_PSG.o \ + $(EMUOBJ)/panning.o \ + $(EMUOBJ)/pokey.o \ + $(EMUOBJ)/pwm.o \ + $(EMUOBJ)/qsound.o \ + $(EMUOBJ)/rf5c68.o \ + $(EMUOBJ)/saa1099.o \ + $(EMUOBJ)/segapcm.o \ + $(EMUOBJ)/scd_pcm.o \ + $(EMUOBJ)/scsp.o \ + $(EMUOBJ)/scspdsp.o \ + $(EMUOBJ)/sn76489.o \ + $(EMUOBJ)/sn76496.o \ + $(EMUOBJ)/sn764intf.o \ + $(EMUOBJ)/upd7759.o \ + $(EMUOBJ)/vsu.o \ + $(EMUOBJ)/ws_audio.o \ + $(EMUOBJ)/x1_010.o \ + $(EMUOBJ)/ym2151.o \ + $(EMUOBJ)/ym2413.o \ + $(EMUOBJ)/ym2612.o \ + $(EMUOBJ)/ymdeltat.o \ + $(EMUOBJ)/ymf262.o \ + $(EMUOBJ)/ymf271.o \ + $(EMUOBJ)/ymf278b.o \ + $(EMUOBJ)/ymz280b.o \ + $(EMUOBJ)/ay8910_opl.o \ + $(EMUOBJ)/sn76496_opl.o \ + $(EMUOBJ)/ym2413hd.o \ + $(EMUOBJ)/ym2413_opl.o +VGMPLAY_OBJS = \ + $(OBJ)/VGMPlayUI.o +VGM2PCM_OBJS = \ + $(OBJ)/vgm2pcm.o +EXTRA_OBJS = $(VGMPLAY_OBJS) $(VGM2PCM_OBJS) + + +all: vgmplay vgm2pcm + +vgmplay: $(EMUOBJS) $(MAINOBJS) $(VGMPLAY_OBJS) + @echo Linking vgmplay ... + @$(CC) $(VGMPLAY_OBJS) $(MAINOBJS) $(EMUOBJS) $(LDFLAGS) -o vgmplay + @echo Done. + +vgm2pcm: $(EMUOBJS) $(MAINOBJS) $(VGM2PCM_OBJS) + @echo Linking vgm2pcm ... + @$(CC) $(VGM2PCM_OBJS) $(MAINOBJS) $(EMUOBJS) $(LDFLAGS) -o vgm2pcm + @echo Done. + +# compile the chip-emulator c-files +$(EMUOBJ)/%.o: $(EMUSRC)/%.c + @echo Compiling $< ... + @mkdir -p $(@D) + @$(CC) $(CFLAGS) $(EMUFLAGS) -c $< -o $@ + +# compile the main c-files +$(OBJ)/%.o: $(SRC)/%.c + @echo Compiling $< ... + @mkdir -p $(@D) + @$(CC) $(CFLAGS) $(MAINFLAGS) -c $< -o $@ + +clean: + @echo Deleting object files ... + @rm -f $(MAINOBJS) $(EMUOBJS) $(EXTRA_OBJS) + @echo Deleting executable files ... + @rm -f vgmplay vgm2pcm + @echo Done. + +# Thanks to ZekeSulastin and nextvolume for the install and uninstall routines. +install: vgmplay + install -m 755 vgmplay $(DESTDIR)$(PREFIX)/bin/vgmplay + install -m 644 vgmplay.1 $(DESTDIR)$(MANPREFIX)/man1/vgmplay.1 + mkdir -m 755 -p $(DESTDIR)$(PREFIX)/share/vgmplay + install -m 644 VGMPlay.ini $(DESTDIR)$(PREFIX)/share/vgmplay/vgmplay.ini + -install -m 644 yrw801.rom $(DESTDIR)$(PREFIX)/share/vgmplay/yrw801.rom + + +# Install the "vgm-player" wrapper +play_inst: install + install -m 755 vgm-player $(DESTDIR)$(PREFIX)/bin/vgm-player + +uninstall: + rm $(DESTDIR)$(PREFIX)/bin/vgmplay + rm $(DESTDIR)$(PREFIX)/bin/vgm-player + rm $(DESTDIR)$(MANPREFIX)/man1/vgmplay.1 + rm -rf $(DESTDIR)$(PREFIX)/share/vgmplay + +.PHONY: all clean install uninstall diff --git a/Frameworks/GME/vgmplay/PortTalk_IOCTL.h b/Frameworks/GME/vgmplay/PortTalk_IOCTL.h new file mode 100644 index 000000000..d86c0930a --- /dev/null +++ b/Frameworks/GME/vgmplay/PortTalk_IOCTL.h @@ -0,0 +1,37 @@ +/******************************************************************************/ +/* */ +/* PortTalk Driver for Windows NT/2000/XP */ +/* Version 2.0, 12th January 2002 */ +/* http://www.beyondlogic.org */ +/* */ +/* Copyright © 2002 Craig Peacock. Craig.Peacock@beyondlogic.org */ +/* Any publication or distribution of this code in source form is prohibited */ +/* without prior written permission of the copyright holder. This source code */ +/* is provided "as is", without any guarantee made as to its suitability or */ +/* fitness for any particular use. Permission is herby granted to modify or */ +/* enhance this sample code to produce a derivative program which may only be */ +/* distributed in compiled object form only. */ +/******************************************************************************/ + +#define PORTTALK_TYPE 40000 /* 32768-65535 are reserved for customers */ + +// The IOCTL function codes from 0x800 to 0xFFF are for customer use. + +#define IOCTL_IOPM_RESTRICT_ALL_ACCESS \ + CTL_CODE(PORTTALK_TYPE, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_IOPM_ALLOW_EXCUSIVE_ACCESS \ + CTL_CODE(PORTTALK_TYPE, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SET_IOPM \ + CTL_CODE(PORTTALK_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_ENABLE_IOPM_ON_PROCESSID \ + CTL_CODE(PORTTALK_TYPE, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_READ_PORT_UCHAR \ + CTL_CODE(PORTTALK_TYPE, 0x904, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_WRITE_PORT_UCHAR \ + CTL_CODE(PORTTALK_TYPE, 0x905, METHOD_BUFFERED, FILE_ANY_ACCESS) + diff --git a/Frameworks/GME/vgmplay/SourceReadme.txt b/Frameworks/GME/vgmplay/SourceReadme.txt new file mode 100644 index 000000000..afc48e02d --- /dev/null +++ b/Frameworks/GME/vgmplay/SourceReadme.txt @@ -0,0 +1,31 @@ +VGMPlay Build Instructions +-------------------------- + +Compile VGMPlay under Windows +----------------------------- + +using MS Visual Studio 6.0: +1. Open VGMPlay.dsw. +2. Build the project. +3. Done. + +using later versions of MS Visual Studio: +1. Open VGMPlay.dsw. +2. It will ask you to convert the project to the current project format. Click "Yes". +3. Build the project. +4. Done. + +using MinGW/MSYS: +1. edit the Makefile and enable the line "WINDOWS = 1" (remove the #) +2. open MSYS and run the "make" command in VGMPlay's folder. +3. Done. + +Note: You can compile it without MSYS, but you need to manually create the OBJDIRS paths (or make them use the backslash '\'), because mkdir fails at paths with a forward slash. + + + +Compile VGMPlay under Linux +--------------------------- +1. [optional step] If you have libao installed, you can edit the Makefile to make VGMPlay use libao instead of OSS. +2. run "make" in VGMPlay's folder +3. Done. diff --git a/Frameworks/GME/vgmplay/VGMFile.h b/Frameworks/GME/vgmplay/VGMFile.h new file mode 100644 index 000000000..d7c5ae0c1 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMFile.h @@ -0,0 +1,147 @@ +// Header file for VGM file handling + +typedef struct _vgm_file_header +{ + UINT32 fccVGM; + UINT32 lngEOFOffset; + UINT32 lngVersion; + UINT32 lngHzPSG; + UINT32 lngHzYM2413; + UINT32 lngGD3Offset; + UINT32 lngTotalSamples; + UINT32 lngLoopOffset; + UINT32 lngLoopSamples; + UINT32 lngRate; + UINT16 shtPSG_Feedback; + UINT8 bytPSG_SRWidth; + UINT8 bytPSG_Flags; + UINT32 lngHzYM2612; + UINT32 lngHzYM2151; + UINT32 lngDataOffset; + UINT32 lngHzSPCM; + UINT32 lngSPCMIntf; + UINT32 lngHzRF5C68; + UINT32 lngHzYM2203; + UINT32 lngHzYM2608; + UINT32 lngHzYM2610; + UINT32 lngHzYM3812; + UINT32 lngHzYM3526; + UINT32 lngHzY8950; + UINT32 lngHzYMF262; + UINT32 lngHzYMF278B; + UINT32 lngHzYMF271; + UINT32 lngHzYMZ280B; + UINT32 lngHzRF5C164; + UINT32 lngHzPWM; + UINT32 lngHzAY8910; + UINT8 bytAYType; + UINT8 bytAYFlag; + UINT8 bytAYFlagYM2203; + UINT8 bytAYFlagYM2608; + UINT8 bytVolumeModifier; + UINT8 bytReserved2; + INT8 bytLoopBase; + UINT8 bytLoopModifier; + UINT32 lngHzGBDMG; + UINT32 lngHzNESAPU; + UINT32 lngHzMultiPCM; + UINT32 lngHzUPD7759; + UINT32 lngHzOKIM6258; + UINT8 bytOKI6258Flags; + UINT8 bytK054539Flags; + UINT8 bytC140Type; + UINT8 bytReservedFlags; + UINT32 lngHzOKIM6295; + UINT32 lngHzK051649; + UINT32 lngHzK054539; + UINT32 lngHzHuC6280; + UINT32 lngHzC140; + UINT32 lngHzK053260; + UINT32 lngHzPokey; + UINT32 lngHzQSound; + UINT32 lngHzSCSP; +// UINT32 lngHzOKIM6376; + //UINT8 bytReserved[0x04]; + UINT32 lngExtraOffset; + UINT32 lngHzWSwan; + UINT32 lngHzVSU; + UINT32 lngHzSAA1099; + UINT32 lngHzES5503; + UINT32 lngHzES5506; + UINT8 bytES5503Chns; + UINT8 bytES5506Chns; + UINT8 bytC352ClkDiv; + UINT8 bytESReserved; + UINT32 lngHzX1_010; + UINT32 lngHzC352; + UINT32 lngHzGA20; +} VGM_HEADER; +typedef struct _vgm_header_extra +{ + UINT32 DataSize; + UINT32 Chp2ClkOffset; + UINT32 ChpVolOffset; +} VGM_HDR_EXTRA; +typedef struct _vgm_extra_chip_data32 +{ + UINT8 Type; + UINT32 Data; +} VGMX_CHIP_DATA32; +typedef struct _vgm_extra_chip_data16 +{ + UINT8 Type; + UINT8 Flags; + UINT16 Data; +} VGMX_CHIP_DATA16; +typedef struct _vgm_extra_chip_extra32 +{ + UINT8 ChipCnt; + VGMX_CHIP_DATA32* CCData; +} VGMX_CHP_EXTRA32; +typedef struct _vgm_extra_chip_extra16 +{ + UINT8 ChipCnt; + VGMX_CHIP_DATA16* CCData; +} VGMX_CHP_EXTRA16; +typedef struct _vgm_header_extra_data +{ + VGMX_CHP_EXTRA32 Clocks; + VGMX_CHP_EXTRA16 Volumes; +} VGM_EXTRA; + +#define VOLUME_MODIF_WRAP 0xC0 +typedef struct _vgm_gd3_tag +{ + UINT32 fccGD3; + UINT32 lngVersion; + UINT32 lngTagLength; + wchar_t* strTrackNameE; + wchar_t* strTrackNameJ; + wchar_t* strGameNameE; + wchar_t* strGameNameJ; + wchar_t* strSystemNameE; + wchar_t* strSystemNameJ; + wchar_t* strAuthorNameE; + wchar_t* strAuthorNameJ; + wchar_t* strReleaseDate; + wchar_t* strCreator; + wchar_t* strNotes; +} GD3_TAG; +typedef struct _vgm_pcm_bank_data +{ + UINT32 DataSize; + UINT8* Data; + UINT32 DataStart; +} VGM_PCM_DATA; +typedef struct _vgm_pcm_bank +{ + UINT32 BankCount; + VGM_PCM_DATA* Bank; + UINT32 DataSize; + UINT8* Data; + UINT32 DataPos; + UINT32 BnkPos; +} VGM_PCM_BANK; + +#define FCC_VGM 0x206D6756 // 'Vgm ' +#define FCC_GD3 0x20336447 // 'Gd3 ' diff --git a/Frameworks/GME/vgmplay/VGMPlay.c b/Frameworks/GME/vgmplay/VGMPlay.c new file mode 100644 index 000000000..bdca3ee2e --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay.c @@ -0,0 +1,5150 @@ +// VGMPlay.c: C Source File of the Main Executable +// + +// Line Size: 96 Chars +// Tab Size: 4 Spaces + +/*3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 +0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999*/ +// TODO: Callback "ChangeSampleRate" to fix YM2203's AY8910 + +// Mixer Muting ON: +// Mixer's FM Volume is set to 0 or Mute -> absolutely muted +// (sometimes it can take some time to get the Mixer Control under Windows) +// Mixer Muting OFF: +// FM Volume is set to 0 through commands -> very very low volume level ~0.4% +// (faster way) +//#define MIXER_MUTING + +// These defines enable additional features. +// ADDITIONAL_FORMATS enables CMF and DRO support. +// CONSOLE_MODE switches between VGMPlay and in_vgm mode. +// in_vgm mode can also be used for custom players. +// +//#define ADDITIONAL_FORMATS +//#define CONSOLE_MODE +//#define VGM_BIG_ENDIAN + +#include +#include +#include +#include +#include "stdbool.h" +#include // for pow() + +#ifndef NO_ZLIB +#include +#endif + +#include "resampler.h" + +#include "chips/mamedef.h" + +// integer types for fast integer calculation +// the bit number is unused (it's an orientation) +#define FUINT8 unsigned int +#define FUINT16 unsigned int + +#include "VGMPlay.h" +//#include "VGMPlay_Intf.h" // Already included by VGMPlay.h now + +#include "chips/ChipIncl.h" + +unsigned char OpenPortTalk(void *); +void ClosePortTalk(void *); + +#include "ChipMapper.h" + + + +// Function Prototypes (prototypes in comments are defined in VGMPlay_Intf.h) +//void VGMPlay_Init(void); +//void VGMPlay_Init2(void); +//void VGMPlay_Deinit(void); + +INLINE UINT16 ReadLE16(const UINT8* Data); +INLINE UINT16 ReadBE16(const UINT8* Data); +INLINE UINT32 ReadLE24(const UINT8* Data); +INLINE UINT32 ReadLE32(const UINT8* Data); +INLINE int FILE_getLE16(VGM_FILE* hFile, UINT16* RetValue); +INLINE int FILE_getLE32(VGM_FILE* hFile, UINT32* RetValue); +static UINT32 gcd(UINT32 x, UINT32 y); +//void PlayVGM(void); +//void StopVGM(void); +//void RestartVGM(void); +//void PauseVGM(bool Pause); +//void SeekVGM(bool Relative, INT32 PlayBkSamples); +//void RefreshMuting(void); +//void RefreshPanning(void); +//void RefreshPlaybackOptions(void); + +//UINT32 GetGZFileLength(const char* FileName); +//UINT32 GetGZFileLengthW(const wchar_t* FileName); +static UINT32 GetGZFileLength_Internal(FILE* hFile); +//bool OpenVGMFile(const char* FileName); +static bool OpenVGMFile_Internal(VGM_PLAYER*, VGM_FILE* hFile, UINT32 FileSize); +static void ReadVGMHeader(VGM_FILE* hFile, VGM_HEADER* RetVGMHead); +static UINT8 ReadGD3Tag(VGM_FILE* hFile, UINT32 GD3Offset, GD3_TAG* RetGD3Tag); +static void ReadChipExtraData32(VGM_PLAYER*, UINT32 StartOffset, VGMX_CHP_EXTRA32* ChpExtra); +static void ReadChipExtraData16(VGM_PLAYER*, UINT32 StartOffset, VGMX_CHP_EXTRA16* ChpExtra); +//void CloseVGMFile(void); +//void FreeGD3Tag(GD3_TAG* TagData); +static wchar_t* MakeEmptyWStr(void); +static wchar_t* ReadWStrFromFile(VGM_FILE* hFile, UINT32* FilePos, UINT32 EOFPos); +//UINT32 GetVGMFileInfo(const char* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); +static UINT32 GetVGMFileInfo_Internal(VGM_FILE* hFile, UINT32 FileSize, + VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); +INLINE UINT32 MulDivRound(UINT64 Number, UINT64 Numerator, UINT64 Denominator); +//UINT32 CalcSampleMSec(VGM_PLAYER* p, UINT64 Value, UINT8 Mode); +//UINT32 CalcSampleMSecExt(VGM_PLAYER* p, UINT64 Value, UINT8 Mode, VGM_HEADER* FileHead); +//const char* GetChipName(UINT8 ChipID); +//const char* GetAccurateChipName(UINT8 ChipID, UINT8 SubType); +//UINT32 GetChipClock(void*, UINT8 ChipID, UINT8* RetSubType); +static UINT16 GetChipVolume(VGM_PLAYER*, UINT8 ChipID, UINT8 ChipNum, UINT8 ChipCnt); + +static void RestartPlaying(VGM_PLAYER*); +static void Chips_GeneralActions(VGM_PLAYER*, UINT8 Mode); + +INLINE INT32 SampleVGM2Pbk_I(VGM_PLAYER*, INT32 SampleVal); // inline functions +INLINE INT32 SamplePbk2VGM_I(VGM_PLAYER*, INT32 SampleVal); +//INT32 SampleVGM2Playback(void*, INT32 SampleVal); // non-inline functions +//INT32 SamplePlayback2VGM(void*, INT32 SampleVal); +static bool SetMuteControl(VGM_PLAYER*, bool mute); + +static void InterpretFile(VGM_PLAYER*, UINT32 SampleCount); +static void AddPCMData(VGM_PLAYER*, UINT8 Type, UINT32 DataSize, const UINT8* Data); +//INLINE FUINT16 ReadBits(UINT8* Data, UINT32* Pos, FUINT8* BitPos, FUINT8 BitsToRead); +static bool DecompressDataBlk(VGM_PLAYER* p, VGM_PCM_DATA* Bank, UINT32 DataSize, const UINT8* Data); +static UINT8 GetDACFromPCMBank(VGM_PLAYER*); +static UINT8* GetPointerFromPCMBank(VGM_PLAYER*, UINT8 Type, UINT32 DataPos); +static void ReadPCMTable(VGM_PLAYER*, UINT32 DataSize, const UINT8* Data); +static void InterpretVGM(VGM_PLAYER*, UINT32 SampleCount); +#ifdef ADDITIONAL_FORMATS +extern void InterpretOther(VGM_PLAYER*, UINT32 SampleCount); +#endif + +static void GeneralChipLists(VGM_PLAYER*); +static void SetupResampler(VGM_PLAYER*, CAUD_ATTR* CAA); +static void ChangeChipSampleRate(void* DataPtr, UINT32 NewSmplRate); + +INLINE INT16 Limit2Short(INT32 Value); +static void null_update(void *param, stream_sample_t **outputs, int samples); +struct dual_opl2_info +{ + void * chip; + int ChipID; +}; +static void dual_opl2_stereo(void *param, stream_sample_t **outputs, int samples); +static void ResampleChipStream(VGM_PLAYER*, CA_LIST* CLst, WAVE_32BS* RetSample, UINT32 Length); +static INT32 RecalcFadeVolume(VGM_PLAYER*); +//UINT32 FillBuffer(void *, WAVE_16BS* Buffer, UINT32 BufferSize) + +// Options and such moved to VGM_PLAYER structure + +void * VGMPlay_Init(void) +{ + UINT8 CurChip; + UINT8 CurCSet; + UINT8 CurChn; + CHIP_OPTS* TempCOpt; + CAUD_ATTR* TempCAud; + + VGM_PLAYER* p = (VGM_PLAYER*) calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->SampleRate = 44100; + p->FadeTime = 5000; + + p->FadeRAWLog = false; + p->VolumeLevel = 1.0f; + //p->FullBufFill = false; + p->SurroundSound = false; + p->VGMMaxLoop = 0x02; + p->VGMPbRate = 0; +#ifdef ADDITIONAL_FORMATS + p->CMFMaxLoop = 0x01; +#endif + p->ResampleMode = 0x00; + p->CHIP_SAMPLING_MODE = 0x00; + p->CHIP_SAMPLE_RATE = 0x00000000; + p->DoubleSSGVol = false; + + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + TempCAud = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCAud ++) + { + TempCOpt = (CHIP_OPTS*)&p->ChipOpts[CurCSet] + CurChip; + + TempCOpt->Disabled = false; + TempCOpt->EmuCore = 0x00; + TempCOpt->SpecialFlags = 0x00; + TempCOpt->ChnCnt = 0x00; + TempCOpt->ChnMute1 = 0x00; + TempCOpt->ChnMute2 = 0x00; + TempCOpt->ChnMute3 = 0x00; + TempCOpt->Panning = NULL; + + // Set up some important fields to prevent in_vgm from crashing + // when clicking on Muting checkboxes after init. + TempCAud->ChipType = 0xFF; + TempCAud->ChipID = CurCSet; + TempCAud->Paired = NULL; + } + p->ChipOpts[CurCSet].GameBoy.SpecialFlags = 0x0003; + // default options, 0x8000 skips the option write and keeps NSFPlay's default values + p->ChipOpts[CurCSet].NES.SpecialFlags = 0x8000 | + (0x00 << 12) | (0x3B << 4) | (0x01 << 2) | (0x03 << 0); + + TempCAud = p->CA_Paired[CurCSet]; + for (CurChip = 0x00; CurChip < 0x03; CurChip ++, TempCAud ++) + { + TempCAud->ChipType = 0xFF; + TempCAud->ChipID = CurCSet; + TempCAud->Paired = NULL; + } + + // currently the only chips with Panning support are + // SN76496 and YM2413, it should be not a problem that it's hardcoded. + TempCOpt = (CHIP_OPTS*)&p->ChipOpts[CurCSet].SN76496; + TempCOpt->ChnCnt = 0x04; + TempCOpt->Panning = (INT16*)malloc(sizeof(INT16) * TempCOpt->ChnCnt); + for (CurChn = 0x00; CurChn < TempCOpt->ChnCnt; CurChn ++) + TempCOpt->Panning[CurChn] = 0x00; + + TempCOpt = (CHIP_OPTS*)&p->ChipOpts[CurCSet].YM2413; + TempCOpt->ChnCnt = 0x0E; // 0x09 + 0x05 + TempCOpt->Panning = (INT16*)malloc(sizeof(INT16) * TempCOpt->ChnCnt); + for (CurChn = 0x00; CurChn < TempCOpt->ChnCnt; CurChn ++) + TempCOpt->Panning[CurChn] = 0x00; + } + + p->FileMode = 0xFF; + + return p; +} + +void VGMPlay_Init2(void *_p) +{ + VGM_PLAYER* p = (VGM_PLAYER*)_p; + // has to be called after the configuration is loaded + + p->StreamBufs[0x00] = (INT32*)malloc(SMPL_BUFSIZE * sizeof(INT32)); + p->StreamBufs[0x01] = (INT32*)malloc(SMPL_BUFSIZE * sizeof(INT32)); + + if (p->CHIP_SAMPLE_RATE <= 0) + p->CHIP_SAMPLE_RATE = p->SampleRate; + p->PlayingMode = 0xFF; + + return; +} + +void VGMPlay_Deinit(void *_p) +{ + UINT8 CurChip; + UINT8 CurCSet; + CHIP_OPTS* TempCOpt; + + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + free(p->StreamBufs[0x00]); p->StreamBufs[0x00] = NULL; + free(p->StreamBufs[0x01]); p->StreamBufs[0x01] = NULL; + + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) + { + TempCOpt = (CHIP_OPTS*)&p->ChipOpts[CurCSet] + CurChip; + + if (TempCOpt->Panning != NULL) + { + free(TempCOpt->Panning); TempCOpt->Panning = NULL; + } + } + } + + free(p); + + return; +} + +INLINE UINT16 ReadLE16(const UINT8* Data) +{ + // read 16-Bit Word (Little Endian/Intel Byte Order) +#ifndef VGM_BIG_ENDIAN + return *(UINT16*)Data; +#else + return (Data[0x01] << 8) | (Data[0x00] << 0); +#endif +} + +INLINE UINT16 ReadBE16(const UINT8* Data) +{ + // read 16-Bit Word (Big Endian/Motorola Byte Order) +#ifndef VGM_BIG_ENDIAN + return (Data[0x00] << 8) | (Data[0x01] << 0); +#else + return *(UINT16*)Data; +#endif +} + +INLINE UINT32 ReadLE24(const UINT8* Data) +{ + // read 24-Bit Word (Little Endian/Intel Byte Order) +#ifndef VGM_BIG_ENDIAN + return (*(UINT32*)Data) & 0x00FFFFFF; +#else + return (Data[0x02] << 16) | (Data[0x01] << 8) | (Data[0x00] << 0); +#endif +} + +INLINE UINT32 ReadLE32(const UINT8* Data) +{ + // read 32-Bit Word (Little Endian/Intel Byte Order) +#ifndef VGM_BIG_ENDIAN + return *(UINT32*)Data; +#else + return (Data[0x03] << 24) | (Data[0x02] << 16) | + (Data[0x01] << 8) | (Data[0x00] << 0); +#endif +} + +INLINE int FILE_getLE16(VGM_FILE* hFile, UINT16* RetValue) +{ +#ifndef VGM_BIG_ENDIAN + return hFile->Read(hFile, RetValue, 0x02); +#else + int RetVal; + UINT8 Data[0x02]; + + RetVal = hFile->Read(hFile, Data, 0x02); + *RetValue = (Data[0x01] << 8) | (Data[0x00] << 0); + return RetVal; +#endif +} + +INLINE int FILE_getLE32(VGM_FILE* hFile, UINT32* RetValue) +{ +#ifndef VGM_BIG_ENDIAN + return hFile->Read(hFile, RetValue, 0x04); +#else + int RetVal; + UINT8 Data[0x04]; + + RetVal = hFime->Read(hFile, Data, 0x04); + *RetValue = (Data[0x03] << 24) | (Data[0x02] << 16) | + (Data[0x01] << 8) | (Data[0x00] << 0); + return RetVal; +#endif +} + +static UINT32 gcd(UINT32 x, UINT32 y) +{ + UINT32 shift; + UINT32 diff; + + // Thanks to Wikipedia for this algorithm + // http://en.wikipedia.org/wiki/Binary_GCD_algorithm + if (! x || ! y) + return x | y; + + for (shift = 0; ((x | y) & 1) == 0; shift ++) + { + x >>= 1; + y >>= 1; + } + + while((x & 1) == 0) + x >>= 1; + + do + { + while((y & 1) == 0) + y >>= 1; + + if (x < y) + { + y -= x; + } + else + { + diff = x - y; + x = y; + y = diff; + } + y >>= 1; + } while(y); + + return x << shift; +} + +void PlayVGM(void *_p) +{ + UINT8 CurChip; + UINT8 FMVal; + INT32 TempSLng; + + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + if (p->PlayingMode != 0xFF) + return; + + p->FadePlay = false; + p->MasterVol = 1.0f; + p->ForceVGMExec = false; + p->FadeStart = 0; + p->ForceVGMExec = true; + + p->PlayingMode = 0x00; // Normal Mode + + if (p->VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP) + TempSLng = p->VGMHead.bytVolumeModifier; + else if (p->VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) + TempSLng = VOLUME_MODIF_WRAP - 0x100; + else + TempSLng = p->VGMHead.bytVolumeModifier - 0x100; + p->VolumeLevelM = (float)(p->VolumeLevel * pow(2.0, TempSLng / (double)0x20)); + p->FinalVol = p->VolumeLevelM; + + if (! p->VGMMaxLoop) + { + p->VGMMaxLoopM = 0x00; + } + else + { + TempSLng = (p->VGMMaxLoop * p->VGMHead.bytLoopModifier + 0x08) / 0x10 - p->VGMHead.bytLoopBase; + p->VGMMaxLoopM = (TempSLng >= 0x01) ? TempSLng : 0x01; + } + + if (! p->VGMPbRate || ! p->VGMHead.lngRate) + { + p->VGMPbRateMul = 1; + p->VGMPbRateDiv = 1; + } + else + { + // I prefer small Multiplers and Dividers, as they're used very often + TempSLng = gcd(p->VGMHead.lngRate, p->VGMPbRate); + p->VGMPbRateMul = p->VGMHead.lngRate / TempSLng; + p->VGMPbRateDiv = p->VGMPbRate / TempSLng; + } + p->VGMSmplRateMul = p->SampleRate * p->VGMPbRateMul; + p->VGMSmplRateDiv = p->VGMSampleRate * p->VGMPbRateDiv; + // same as above - to speed up the VGM <-> Playback calculation + TempSLng = gcd(p->VGMSmplRateMul, p->VGMSmplRateDiv); + p->VGMSmplRateMul /= TempSLng; + p->VGMSmplRateDiv /= TempSLng; + + p->PlayingTime = 0; + p->EndPlay = false; + + p->VGMPos = p->VGMHead.lngDataOffset; + p->VGMSmplPos = 0; + p->VGMSmplPlayed = 0; + p->VGMEnd = false; + p->VGMCurLoop = 0x00; + if (p->VGMPos >= p->VGMHead.lngEOFOffset) + p->VGMEnd = true; + + Chips_GeneralActions(p, 0x00); // Start chips + // also does Reset (0x01), Muting Mask (0x10) and Panning (0x20) + + p->Last95Drum = 0xFFFF; + p->Last95Freq = 0; + p->Last95Max = 0xFFFF; + p->IsVGMInit = true; + p->ErrorHappened = false; + InterpretFile(p, 0); + p->IsVGMInit = false; + + p->ForceVGMExec = false; + + return; +} + +void StopVGM(void *_p) +{ + VGM_PLAYER* p = (VGM_PLAYER*)_p; + if (p->PlayingMode == 0xFF) + return; + + Chips_GeneralActions(p, 0x02); // Stop chips + p->PlayingMode = 0xFF; + + return; +} + +void RestartVGM(void *_p) +{ + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + if (p->PlayingMode == 0xFF || ! p->VGMSmplPlayed) + return; + + RestartPlaying(p); + + return; +} + +void SeekVGM(void *_p, bool Relative, INT32 PlayBkSamples) +{ + INT32 Samples; + UINT32 LoopSmpls; + + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + if (p->PlayingMode == 0xFF || (Relative && ! PlayBkSamples)) + return; + + LoopSmpls = p->VGMCurLoop * SampleVGM2Pbk_I(p, p->VGMHead.lngLoopSamples); + if (! Relative) + Samples = PlayBkSamples - (LoopSmpls + p->VGMSmplPlayed); + else + Samples = PlayBkSamples; + + if (Samples < 0) + { + Samples = LoopSmpls + p->VGMSmplPlayed + Samples; + if (Samples < 0) + Samples = 0; + RestartPlaying(p); + } + + p->ForceVGMExec = true; + InterpretFile(p, Samples); + p->ForceVGMExec = false; + + return; +} + +void RefreshMuting(void *_p) +{ + VGM_PLAYER* p = (VGM_PLAYER*)_p; + Chips_GeneralActions(p, 0x10); // set muting mask + + return; +} + +void RefreshPanning(void *_p) +{ + VGM_PLAYER* p = (VGM_PLAYER*)_p; + Chips_GeneralActions(p, 0x20); // set panning + + return; +} + +void RefreshPlaybackOptions(void *_p) +{ + INT32 TempVol; + UINT8 CurChip; + CHIP_OPTS* TempCOpt1; + CHIP_OPTS* TempCOpt2; + + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + if (p->VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP) + TempVol = p->VGMHead.bytVolumeModifier; + else if (p->VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) + TempVol = VOLUME_MODIF_WRAP - 0x100; + else + TempVol = p->VGMHead.bytVolumeModifier - 0x100; + p->VolumeLevelM = (float)(p->VolumeLevel * pow(2.0, TempVol / (double)0x20)); + p->FinalVol = p->VolumeLevelM * p->MasterVol * p->MasterVol; + + if (p->PlayingMode == 0xFF) + { + TempCOpt1 = (CHIP_OPTS*)&p->ChipOpts[0x00]; + TempCOpt2 = (CHIP_OPTS*)&p->ChipOpts[0x01]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt1 ++, TempCOpt2 ++) + { + TempCOpt2->EmuCore = TempCOpt1->EmuCore; + TempCOpt2->SpecialFlags = TempCOpt1->SpecialFlags; + } + } + + return; +} + + +UINT32 GetGZFileLength(const char* FileName) +{ + FILE* hFile; + UINT32 FileSize; + + hFile = fopen(FileName, "rb"); + if (hFile == NULL) + return 0xFFFFFFFF; + + FileSize = GetGZFileLength_Internal(hFile); + + fclose(hFile); + return FileSize; +} + +#ifndef NO_WCHAR_FILENAMES +UINT32 GetGZFileLengthW(const wchar_t* FileName) +{ + FILE* hFile; + UINT32 FileSize; + + hFile = _wfopen(FileName, L"rb"); + if (hFile == NULL) + return 0xFFFFFFFF; + + FileSize = GetGZFileLength_Internal(hFile); + + fclose(hFile); + return FileSize; +} +#endif + +static UINT32 GetGZFileLength_Internal(FILE* hFile) +{ + UINT32 FileSize; + UINT16 gzHead; + size_t RetVal; + + RetVal = fread(&gzHead, 0x02, 0x01, hFile); + if (RetVal >= 1) + { + gzHead = ReadBE16((UINT8*)&gzHead); + if (gzHead != 0x1F8B) + { + RetVal = 0; // no .gz signature - treat as normal file + } + else + { + // .gz File + fseek(hFile, -4, SEEK_END); + // Note: In the error case it falls back to fseek/ftell. + RetVal = fread(&FileSize, 0x04, 0x01, hFile); +#ifdef VGM_BIG_ENDIAN + FileSize = ReadLE32((UINT8*)&FileSize); +#endif + } + } + if (RetVal <= 0) + { + // normal file + fseek(hFile, 0x00, SEEK_END); + FileSize = ftell(hFile); + } + + return FileSize; +} + +#ifndef NO_ZLIB +typedef struct vgm_file_gz +{ + VGM_FILE vf; + gzFile hFile; + UINT32 Size; +} VGM_FILE_gz; + +static int VGMF_gzread(VGM_FILE* hFile, void* ptr, UINT32 count) +{ + VGM_FILE_gz* File = (VGM_FILE_gz *)hFile; + return gzread(File->hFile, ptr, count); +} + +static int VGMF_gzseek(VGM_FILE* hFile, UINT32 offset) +{ + VGM_FILE_gz* File = (VGM_FILE_gz *)hFile; + return gzseek(File->hFile, offset, SEEK_SET); +} + +static UINT32 VGMF_gzgetsize(VGM_FILE* hFile) +{ + VGM_FILE_gz* File = (VGM_FILE_gz *)hFile; + return File->Size; +} +#endif + +bool OpenVGMFile(void *_p, const char* FileName) +{ +#ifdef NO_ZLIB + return false; +#else + gzFile hFile; + UINT32 FileSize; + bool RetVal; + + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + FileSize = GetGZFileLength(FileName); + + hFile = gzopen(FileName, "rb"); + if (hFile == NULL) + return false; + + VGM_FILE_gz vgmFile; + + vgmFile.vf.Read = VGMF_gzread; + vgmFile.vf.Seek = VGMF_gzseek; + vgmFile.vf.GetSize = VGMF_gzgetsize; + vgmFile.hFile = hFile; + vgmFile.Size = FileSize; + + RetVal = OpenVGMFile_Internal(p, (VGM_FILE *)&vgmFile, FileSize); + + gzclose(hFile); + return RetVal; +#endif +} + +#ifndef NO_WCHAR_FILENAMES +bool OpenVGMFileW(void *_p, const wchar_t* FileName) +{ +#ifdef NO_ZLIB + return false; +#else + gzFile hFile; + UINT32 FileSize; + bool RetVal; + + VGM_PLAYER* p = (VGM_PLAYER*)_p; + +#if ZLIB_VERNUM < 0x1270 + int fDesc; + + FileSize = GetGZFileLengthW(FileName); + + fDesc = _wopen(FileName, _O_RDONLY | _O_BINARY); + hFile = gzdopen(fDesc, "rb"); + if (hFile == NULL) + { + _close(fDesc); + return false; + } +#else + FileSize = GetGZFileLengthW(FileName); + + hFile = gzopen_w(FileName, "rb"); + if (hFile == NULL) + return false; +#endif + VGM_FILE_gz vgmFile; + + vgmFile.vf.Read = VGMF_gzread; + vgmFile.vf.Seek = VGMF_gzseek; + vgmFile.vf.GetSize = VGMF_gzgetsize; + vgmFile.hFile = hFile; + vgmFile.Size = FileSize; + + RetVal = OpenVGMFile_Internal(p, (VGM_FILE *)&vgmFile, FileSize); + + gzclose(hFile); + return RetVal; +#endif +} +#endif + +bool OpenVGMFile_Handle(void* _p, VGM_FILE* hFile) +{ + UINT32 FileSize = hFile->GetSize(hFile); + return OpenVGMFile_Internal((VGM_PLAYER*)_p, hFile, FileSize); +} + +static bool OpenVGMFile_Internal(VGM_PLAYER* p, VGM_FILE* hFile, UINT32 FileSize) +{ + UINT32 fccHeader; + UINT32 CurPos; + UINT32 HdrLimit; + + hFile->Seek(hFile, 0x00); + FILE_getLE32(hFile, &fccHeader); + if (fccHeader != FCC_VGM) + return false; + + if (p->FileMode != 0xFF) + CloseVGMFile(p); + + p->FileMode = 0x00; + p->VGMDataLen = FileSize; + + hFile->Seek(hFile, 0x00); + ReadVGMHeader(hFile, &p->VGMHead); + + p->VGMSampleRate = 44100; + if (! p->VGMDataLen) + p->VGMDataLen = p->VGMHead.lngEOFOffset; + if (! p->VGMHead.lngEOFOffset || p->VGMHead.lngEOFOffset > p->VGMDataLen) + { + p->VGMHead.lngEOFOffset = p->VGMDataLen; + } + if (p->VGMHead.lngLoopOffset && ! p->VGMHead.lngLoopSamples) + { + // 0-Sample-Loops causes the program to hangs in the playback routine + p->VGMHead.lngLoopOffset = 0x00000000; + } + if (p->VGMHead.lngDataOffset < 0x00000040) + { + p->VGMHead.lngDataOffset = 0x00000040; + } + + memset(&p->VGMHeadX, 0x00, sizeof(VGM_HDR_EXTRA)); + memset(&p->VGMH_Extra, 0x00, sizeof(VGM_EXTRA)); + + // Read Data + p->VGMDataLen = p->VGMHead.lngEOFOffset; + p->VGMData = (UINT8*)malloc(p->VGMDataLen); + if (p->VGMData == NULL) + return false; + hFile->Seek(hFile, 0x00); + hFile->Read(hFile, p->VGMData, p->VGMDataLen); + + // Read Extra Header Data + if (p->VGMHead.lngExtraOffset) + { + UINT32* TempPtr; + + CurPos = p->VGMHead.lngExtraOffset; + TempPtr = (UINT32*)&p->VGMHeadX; + // Read Header Size + p->VGMHeadX.DataSize = ReadLE32(&p->VGMData[CurPos]); + if (p->VGMHeadX.DataSize > sizeof(VGM_HDR_EXTRA)) + p->VGMHeadX.DataSize = sizeof(VGM_HDR_EXTRA); + HdrLimit = CurPos + p->VGMHeadX.DataSize; + CurPos += 0x04; + TempPtr ++; + + // Read all relative offsets of this header and make them absolute. + for (; CurPos < HdrLimit; CurPos += 0x04, TempPtr ++) + { + *TempPtr = ReadLE32(&p->VGMData[CurPos]); + if (*TempPtr) + *TempPtr += CurPos; + } + + ReadChipExtraData32(p, p->VGMHeadX.Chp2ClkOffset, &p->VGMH_Extra.Clocks); + ReadChipExtraData16(p, p->VGMHeadX.ChpVolOffset, &p->VGMH_Extra.Volumes); + } + + // Read GD3 Tag + HdrLimit = ReadGD3Tag(hFile, p->VGMHead.lngGD3Offset, &p->VGMTag); + if (HdrLimit == 0x10) + { + p->VGMHead.lngGD3Offset = 0x00000000; + //return false; + } + if (! p->VGMHead.lngGD3Offset) + { + // replace all NULL pointers with empty strings + p->VGMTag.strTrackNameE = MakeEmptyWStr(); + p->VGMTag.strTrackNameJ = MakeEmptyWStr(); + p->VGMTag.strGameNameE = MakeEmptyWStr(); + p->VGMTag.strGameNameJ = MakeEmptyWStr(); + p->VGMTag.strSystemNameE = MakeEmptyWStr(); + p->VGMTag.strSystemNameJ = MakeEmptyWStr(); + p->VGMTag.strAuthorNameE = MakeEmptyWStr(); + p->VGMTag.strAuthorNameJ = MakeEmptyWStr(); + p->VGMTag.strReleaseDate = MakeEmptyWStr(); + p->VGMTag.strCreator = MakeEmptyWStr(); + p->VGMTag.strNotes = MakeEmptyWStr(); + } + + return true; +} + +static void ReadVGMHeader(VGM_FILE* hFile, VGM_HEADER* RetVGMHead) +{ + VGM_HEADER CurHead; + UINT32 CurPos; + UINT32 HdrLimit; + + hFile->Read(hFile, &CurHead, sizeof(VGM_HEADER)); +#ifdef VGM_BIG_ENDIAN + { + UINT8* TempPtr; + + // Warning: Lots of pointer casting ahead! + for (CurPos = 0x00; CurPos < sizeof(VGM_HEADER); CurPos += 0x04) + { + TempPtr = (UINT8*)&CurHead + CurPos; + switch(CurPos) + { + case 0x28: + // 0x28 [16-bit] SN76496 Feedback Mask + // 0x2A [ 8-bit] SN76496 Shift Register Width + // 0x2B [ 8-bit] SN76496 Flags + *(UINT16*)TempPtr = ReadLE16(TempPtr); + break; + case 0x78: // 78-7B [8-bit] AY8910 Type/Flags + case 0x7C: // 7C-7F [8-bit] Volume/Loop Modifiers + case 0x94: // 94-97 [8-bit] various flags + break; + default: + // everything else is 32-bit + *(UINT32*)TempPtr = ReadLE32(TempPtr); + break; + } + } + } +#endif + + // Header preperations + if (CurHead.lngVersion < 0x00000101) + { + CurHead.lngRate = 0; + } + if (CurHead.lngVersion < 0x00000110) + { + CurHead.shtPSG_Feedback = 0x0000; + CurHead.bytPSG_SRWidth = 0x00; + CurHead.lngHzYM2612 = CurHead.lngHzYM2413; + CurHead.lngHzYM2151 = CurHead.lngHzYM2413; + } + if (CurHead.lngVersion < 0x00000150) + { + CurHead.lngDataOffset = 0x00000000; + // If I would aim to be very strict, I would uncomment these few lines, + // but I sometimes use v1.51 Flags with v1.50 for better compatibility. + // (Some hyper-strict players refuse to play v1.51 files, even if there's + // no new chip used.) + //} + //if (CurHead.lngVersion < 0x00000151) + //{ + CurHead.bytPSG_Flags = 0x00; + CurHead.lngHzSPCM = 0x0000; + CurHead.lngSPCMIntf = 0x00000000; + // all others are zeroed by memset + } + + if (CurHead.lngHzPSG) + { + if (! CurHead.shtPSG_Feedback) + CurHead.shtPSG_Feedback = 0x0009; + if (! CurHead.bytPSG_SRWidth) + CurHead.bytPSG_SRWidth = 0x10; + } + + // relative -> absolute addresses + if (CurHead.lngEOFOffset) + CurHead.lngEOFOffset += 0x00000004; + if (CurHead.lngGD3Offset) + CurHead.lngGD3Offset += 0x00000014; + if (CurHead.lngLoopOffset) + CurHead.lngLoopOffset += 0x0000001C; + + if (CurHead.lngVersion < 0x00000150) + CurHead.lngDataOffset = 0x0000000C; + //if (CurHead.lngDataOffset < 0x0000000C) + // CurHead.lngDataOffset = 0x0000000C; + if (CurHead.lngDataOffset) + CurHead.lngDataOffset += 0x00000034; + + CurPos = CurHead.lngDataOffset; + // should actually check v1.51 (first real usage of DataOffset) + // v1.50 is checked to support things like the Volume Modifiers in v1.50 files + if (CurHead.lngVersion < 0x00000150 /*0x00000151*/) + CurPos = 0x40; + if (! CurPos) + CurPos = 0x40; + HdrLimit = sizeof(VGM_HEADER); + if (HdrLimit > CurPos) + memset((UINT8*)&CurHead + CurPos, 0x00, HdrLimit - CurPos); + + if (! CurHead.bytLoopModifier) + CurHead.bytLoopModifier = 0x10; + + if (CurHead.lngExtraOffset) + { + CurHead.lngExtraOffset += 0xBC; + + CurPos = CurHead.lngExtraOffset; + if (CurPos < HdrLimit) + memset((UINT8*)&CurHead + CurPos, 0x00, HdrLimit - CurPos); + } + + if (CurHead.lngGD3Offset >= CurHead.lngEOFOffset) + CurHead.lngGD3Offset = 0x00; + if (CurHead.lngLoopOffset >= CurHead.lngEOFOffset) + CurHead.lngLoopOffset = 0x00; + if (CurHead.lngDataOffset >= CurHead.lngEOFOffset) + CurHead.lngDataOffset = 0x40; + if (CurHead.lngExtraOffset >= CurHead.lngEOFOffset) + CurHead.lngExtraOffset = 0x00; + + *RetVGMHead = CurHead; + return; +} + +static UINT8 ReadGD3Tag(VGM_FILE* hFile, UINT32 GD3Offset, GD3_TAG* RetGD3Tag) +{ + UINT32 CurPos; + UINT32 TempLng; + UINT8 ResVal; + + ResVal = 0x00; + + // Read GD3 Tag + if (GD3Offset) + { + hFile->Seek(hFile, GD3Offset); + FILE_getLE32(hFile, &TempLng); + if (TempLng != FCC_GD3) + { + GD3Offset = 0x00000000; + ResVal = 0x10; // invalid GD3 offset + } + } + + if (RetGD3Tag == NULL) + return ResVal; + + if (! GD3Offset) + { + RetGD3Tag->fccGD3 = 0x00000000; + RetGD3Tag->lngVersion = 0x00000000; + RetGD3Tag->lngTagLength = 0x00000000; + RetGD3Tag->strTrackNameE = NULL; + RetGD3Tag->strTrackNameJ = NULL; + RetGD3Tag->strGameNameE = NULL; + RetGD3Tag->strGameNameJ = NULL; + RetGD3Tag->strSystemNameE = NULL; + RetGD3Tag->strSystemNameJ = NULL; + RetGD3Tag->strAuthorNameE = NULL; + RetGD3Tag->strAuthorNameJ = NULL; + RetGD3Tag->strReleaseDate = NULL; + RetGD3Tag->strCreator = NULL; + RetGD3Tag->strNotes = NULL; + } + else + { + //CurPos = GD3Offset; + //hFile->Seek(hFile, CurPos, SEEK_SET); + //CurPos += FILE_getLE32(hFile, &RetGD3Tag->fccGD3); + + CurPos = GD3Offset + 0x04; // Save some back seeking, yay! + RetGD3Tag->fccGD3 = TempLng; // (That costs lots of CPU in .gz files.) + CurPos += FILE_getLE32(hFile, &RetGD3Tag->lngVersion); + CurPos += FILE_getLE32(hFile, &RetGD3Tag->lngTagLength); + + TempLng = CurPos + RetGD3Tag->lngTagLength; + RetGD3Tag->strTrackNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strTrackNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strGameNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strGameNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strSystemNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strSystemNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strAuthorNameE = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strAuthorNameJ = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strReleaseDate = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strCreator = ReadWStrFromFile(hFile, &CurPos, TempLng); + RetGD3Tag->strNotes = ReadWStrFromFile(hFile, &CurPos, TempLng); + } + + return ResVal; +} + +static void ReadChipExtraData32(VGM_PLAYER* p, UINT32 StartOffset, VGMX_CHP_EXTRA32* ChpExtra) +{ + UINT32 CurPos; + UINT8 CurChp; + VGMX_CHIP_DATA32* TempCD; + + if (! StartOffset || StartOffset >= p->VGMDataLen) + { + ChpExtra->ChipCnt = 0x00; + ChpExtra->CCData = NULL; + return; + } + + CurPos = StartOffset; + ChpExtra->ChipCnt = p->VGMData[CurPos]; + if (ChpExtra->ChipCnt) + ChpExtra->CCData = (VGMX_CHIP_DATA32*)malloc(sizeof(VGMX_CHIP_DATA32) * + ChpExtra->ChipCnt); + else + ChpExtra->CCData = NULL; + CurPos ++; + + for (CurChp = 0x00; CurChp < ChpExtra->ChipCnt; CurChp ++) + { + TempCD = &ChpExtra->CCData[CurChp]; + TempCD->Type = p->VGMData[CurPos + 0x00]; + TempCD->Data = ReadLE32(&p->VGMData[CurPos + 0x01]); + CurPos += 0x05; + } + + return; +} + +static void ReadChipExtraData16(VGM_PLAYER* p, UINT32 StartOffset, VGMX_CHP_EXTRA16* ChpExtra) +{ + UINT32 CurPos; + UINT8 CurChp; + VGMX_CHIP_DATA16* TempCD; + + if (! StartOffset || StartOffset >= p->VGMDataLen) + { + ChpExtra->ChipCnt = 0x00; + ChpExtra->CCData = NULL; + return; + } + + CurPos = StartOffset; + ChpExtra->ChipCnt = p->VGMData[CurPos]; + if (ChpExtra->ChipCnt) + ChpExtra->CCData = (VGMX_CHIP_DATA16*)malloc(sizeof(VGMX_CHIP_DATA16) * + ChpExtra->ChipCnt); + else + ChpExtra->CCData = NULL; + CurPos ++; + + for (CurChp = 0x00; CurChp < ChpExtra->ChipCnt; CurChp ++) + { + TempCD = &ChpExtra->CCData[CurChp]; + TempCD->Type = p->VGMData[CurPos + 0x00]; + TempCD->Flags = p->VGMData[CurPos + 0x01]; + TempCD->Data = ReadLE16(&p->VGMData[CurPos + 0x02]); + CurPos += 0x04; + } + + return; +} + +void CloseVGMFile(void *_p) +{ + VGM_PLAYER* p = (VGM_PLAYER*)_p; + + if (p->FileMode == 0xFF) + return; + + p->VGMHead.fccVGM = 0x00; + free(p->VGMH_Extra.Clocks.CCData); p->VGMH_Extra.Clocks.CCData = NULL; + free(p->VGMH_Extra.Volumes.CCData); p->VGMH_Extra.Volumes.CCData = NULL; + free(p->VGMData); p->VGMData = NULL; + + if (p->FileMode == 0x00) + FreeGD3Tag(&p->VGMTag); + + p->FileMode = 0xFF; + + return; +} + +void FreeGD3Tag(GD3_TAG* TagData) +{ + if (TagData == NULL) + return; + + TagData->fccGD3 = 0x00; + free(TagData->strTrackNameE); TagData->strTrackNameE = NULL; + free(TagData->strTrackNameJ); TagData->strTrackNameJ = NULL; + free(TagData->strGameNameE); TagData->strGameNameE = NULL; + free(TagData->strGameNameJ); TagData->strGameNameJ = NULL; + free(TagData->strSystemNameE); TagData->strSystemNameE = NULL; + free(TagData->strSystemNameJ); TagData->strSystemNameJ = NULL; + free(TagData->strAuthorNameE); TagData->strAuthorNameE = NULL; + free(TagData->strAuthorNameJ); TagData->strAuthorNameJ = NULL; + free(TagData->strReleaseDate); TagData->strReleaseDate = NULL; + free(TagData->strCreator); TagData->strCreator = NULL; + free(TagData->strNotes); TagData->strNotes = NULL; + + return; +} + +static wchar_t* MakeEmptyWStr(void) +{ + wchar_t* Str; + + Str = (wchar_t*)malloc(0x01 * sizeof(wchar_t)); + Str[0x00] = L'\0'; + + return Str; +} + +static wchar_t* ReadWStrFromFile(VGM_FILE* hFile, UINT32* FilePos, UINT32 EOFPos) +{ + // Note: Works with Windows (16-bit wchar_t) as well as Linux (32-bit wchar_t) + UINT32 CurPos; + wchar_t* TextStr; + wchar_t* TempStr; + UINT32 StrLen; + UINT16 UnicodeChr; + + CurPos = *FilePos; + if (CurPos >= EOFPos) + return NULL; + TextStr = (wchar_t*)malloc((EOFPos - CurPos) / 0x02 * sizeof(wchar_t)); + if (TextStr == NULL) + return NULL; + + hFile->Seek(hFile, CurPos); + TempStr = TextStr - 1; + StrLen = 0x00; + do + { + TempStr ++; + FILE_getLE16(hFile, &UnicodeChr); + *TempStr = (wchar_t)UnicodeChr; + CurPos += 0x02; + StrLen ++; + if (CurPos >= EOFPos) + { + *TempStr = L'\0'; + break; + } + } while(*TempStr != L'\0'); + + TextStr = (wchar_t*)realloc(TextStr, StrLen * sizeof(wchar_t)); + *FilePos = CurPos; + + return TextStr; +} + +UINT32 GetVGMFileInfo(const char* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) +{ +#ifdef NO_ZLIB + return 0; +#else + gzFile hFile; + UINT32 FileSize; + UINT32 RetVal; + + FileSize = GetGZFileLength(FileName); + + hFile = gzopen(FileName, "rb"); + if (hFile == NULL) + return 0x00; + + VGM_FILE_gz vgmFile; + + vgmFile.vf.Read = VGMF_gzread; + vgmFile.vf.Seek = VGMF_gzseek; + vgmFile.vf.GetSize = VGMF_gzgetsize; + vgmFile.hFile = hFile; + vgmFile.Size = FileSize; + + RetVal = GetVGMFileInfo_Internal((VGM_FILE *)&vgmFile, FileSize, RetVGMHead, RetGD3Tag); + + gzclose(hFile); + return RetVal; +#endif +} + +#ifndef NO_WCHAR_FILENAMES +UINT32 GetVGMFileInfoW(const wchar_t* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) +{ +#ifdef NO_ZLIB + return 0; +#else + gzFile hFile; + UINT32 FileSize; + UINT32 RetVal; +#if ZLIB_VERNUM < 0x1270 + int fDesc; + + FileSize = GetGZFileLengthW(FileName); + + fDesc = _wopen(FileName, _O_RDONLY | _O_BINARY); + hFile = gzdopen(fDesc, "rb"); + if (hFile == NULL) + { + _close(fDesc); + return 0x00; + } +#else + FileSize = GetGZFileLengthW(FileName); + + hFile = gzopen_w(FileName, "rb"); + if (hFile == NULL) + return 0x00; +#endif + + VGM_FILE_gz vgmFile; + + vgmFile.vf.Read = VGMF_gzread; + vgmFile.vf.Seek = VGMF_gzseek; + vgmFile.vf.GetSize = VGMF_gzgetsize; + vgmFile.hFile = hFile; + vgmFile.Size = FileSize; + + RetVal = GetVGMFileInfo_Internal((VGM_FILE *)&vgmFile, FileSize, RetVGMHead, RetGD3Tag); + + gzclose(hFile); + return RetVal; +#endif +} +#endif + +UINT32 GetVGMFileInfo_Handle(VGM_FILE* hFile, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) +{ + UINT32 FileSize = hFile->GetSize(hFile); + return GetVGMFileInfo_Internal(hFile, FileSize, RetVGMHead, RetGD3Tag); +} + +static UINT32 GetVGMFileInfo_Internal(VGM_FILE* hFile, UINT32 FileSize, + VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag) +{ + // this is a copy-and-paste from OpenVGM, just a little stripped + UINT32 fccHeader; + UINT32 TempLng; + VGM_HEADER TempHead; + + hFile->Seek(hFile, 0x00); + FILE_getLE32(hFile, &fccHeader); + if (fccHeader != FCC_VGM) + return 0x00; + + if (RetVGMHead == NULL && RetGD3Tag == NULL) + return FileSize; + + hFile->Seek(hFile, 0x00); + ReadVGMHeader(hFile, &TempHead); + + if (! TempHead.lngEOFOffset || TempHead.lngEOFOffset > FileSize) + TempHead.lngEOFOffset = FileSize; + if (TempHead.lngDataOffset < 0x00000040) + TempHead.lngDataOffset = 0x00000040; + + /*if (TempHead.lngGD3Offset) + { + gzseek(hFile, TempHead.lngGD3Offset, SEEK_SET); + gzgetLE32(hFile, &fccHeader); + if (fccHeader != FCC_GD3) + TempHead.lngGD3Offset = 0x00000000; + //return 0x00; + }*/ + + if (RetVGMHead != NULL) + *RetVGMHead = TempHead; + + // Read GD3 Tag + if (RetGD3Tag != NULL) + TempLng = ReadGD3Tag(hFile, TempHead.lngGD3Offset, RetGD3Tag); + + return FileSize; +} + +INLINE UINT32 MulDivRound(UINT64 Number, UINT64 Numerator, UINT64 Denominator) +{ + return (UINT32)((Number * Numerator + Denominator / 2) / Denominator); +} + +UINT32 CalcSampleMSec(void* _p, UINT64 Value, UINT8 Mode) +{ + // Mode: + // Bit 0 (01): Calculation Mode + // 0 - Sample2MSec + // 1 - MSec2Sample + // Bit 1 (02): Calculation Samlpe Rate + // 0 - current playback rate + // 1 - 44.1 KHz (VGM native) + UINT32 SmplRate; + UINT32 PbMul; + UINT32 PbDiv; + UINT32 RetVal; + + VGM_PLAYER* p = (VGM_PLAYER *)_p; + + if (! (Mode & 0x02)) + { + SmplRate = p->SampleRate; + PbMul = 1; + PbDiv = 1; + } + else + { + SmplRate = p->VGMSampleRate; + PbMul = p->VGMPbRateMul; + PbDiv = p->VGMPbRateDiv; + } + + switch(Mode & 0x01) + { + case 0x00: + RetVal = MulDivRound(Value, (UINT64)1000 * PbMul, (UINT64)SmplRate * PbDiv); + break; + case 0x01: + RetVal = MulDivRound(Value, (UINT64)SmplRate * PbDiv, (UINT64)1000 * PbMul); + break; + } + + return RetVal; +} + +UINT32 CalcSampleMSecExt(void *_p, UINT64 Value, UINT8 Mode, VGM_HEADER* FileHead) +{ + // Note: This function was NOT tested with non-VGM formats! + + // Mode: see function above + UINT32 SmplRate; + UINT32 PbMul; + UINT32 PbDiv; + UINT32 RetVal; + + VGM_PLAYER* p = (VGM_PLAYER *)_p; + + if (! (Mode & 0x02)) + { + SmplRate = p->SampleRate; + PbMul = 1; + PbDiv = 1; + } + else + { + // TODO: make it work for non-VGM formats + // (i.e. get VGMSampleRate information from FileHead) + // + // But currently GetVGMFileInfo doesn't support them, it doesn't matter either way + SmplRate = 44100; + if (! p->VGMPbRate || ! FileHead->lngRate) + { + PbMul = 1; + PbDiv = 1; + } + else + { + PbMul = FileHead->lngRate; + PbDiv = p->VGMPbRate; + } + } + + switch(Mode & 0x01) + { + case 0x00: + RetVal = MulDivRound(Value, 1000 * PbMul, SmplRate * PbDiv); + break; + case 0x01: + RetVal = MulDivRound(Value, SmplRate * PbDiv, 1000 * PbMul); + break; + } + + return RetVal; +} + +static UINT32 EncryptChipName(void* DstBuf, const void* SrcBuf, UINT32 Length) +{ + // using nineko's awesome encryption algorithm + // http://forums.sonicretro.org/index.php?showtopic=25300 + // based on C code by sasuke + const UINT8* SrcPos; + UINT8* DstPos; + UINT32 CurPos; + UINT8 CryptShift; // Src Bit/Dst Byte + UINT8 PlainShift; // Src Byte/Dst Bit + + if (Length & 0x07) + return 0x00; // Length MUST be a multiple of 8 + + SrcPos = (const UINT8*)SrcBuf; + DstPos = (UINT8*)DstBuf; + for (CurPos = 0; CurPos < Length; CurPos += 8, SrcPos += 8, DstPos += 8) + { + for (CryptShift = 0; CryptShift < 8; CryptShift ++) + { + DstPos[CryptShift] = 0x00; + for (PlainShift = 0; PlainShift < 8; PlainShift ++) + { + if (SrcPos[PlainShift] & (1 << CryptShift)) + DstPos[CryptShift] |= (1 << PlainShift); + } + } + } + + return Length; +} + +const char* GetChipName(UINT8 ChipID) +{ + const char* CHIP_STRS[CHIP_COUNT] = + { "SN76496", "YM2413", "YM2612", "YM2151", "SegaPCM", "RF5C68", "YM2203", "YM2608", + "YM2610", "YM3812", "YM3526", "Y8950", "YMF262", "YMF278B", "YMF271", "YMZ280B", + "RF5C164", "PWM", "AY8910", "GameBoy", "NES APU", "MultiPCM", "uPD7759", "OKIM6258", + "OKIM6295", "K051649", "K054539", "HuC6280", "C140", "K053260", "Pokey", "QSound", + "SCSP", "WSwan", "VSU", "SAA1099", "ES5503", "ES5506", "X1-010", "C352", + "GA20"}; + + /*if (ChipID == 0x21) + { + static char TempStr[0x08]; + UINT32 TempData[2]; + + //EncryptChipName(TempData, "WSwan", 0x08); + TempData[0] = 0x1015170F; + TempData[1] = 0x001F1C07; + EncryptChipName(TempStr, TempData, 0x08); + return TempStr; // "WSwan" + }*/ + + if (ChipID < CHIP_COUNT) + return CHIP_STRS[ChipID]; + else + return NULL; +} + +const char* GetAccurateChipName(UINT8 ChipID, UINT8 SubType) +{ + const char* RetStr; + static char TempStr[0x10]; + + if ((ChipID & 0x7F) >= CHIP_COUNT) + return NULL; + + RetStr = NULL; + switch(ChipID & 0x7F) + { + case 0x00: + if (! (ChipID & 0x80)) + { + switch(SubType) + { + case 0x01: + RetStr = "SN76489"; + break; + case 0x02: + RetStr = "SN76489A"; + break; + case 0x03: + RetStr = "SN76494"; + break; + case 0x04: + RetStr = "SN76496"; + break; + case 0x05: + RetStr = "SN94624"; + break; + case 0x06: + RetStr = "NCR7496"; + break; + case 0x07: + RetStr = "SEGA PSG"; + break; + default: + RetStr = "SN76496"; + break; + } + } + else + { + RetStr = "T6W28"; + } + break; + case 0x01: + if (ChipID & 0x80) + RetStr = "VRC7"; + break; + case 0x04: + RetStr = "Sega PCM"; + break; + case 0x08: + if (! (ChipID & 0x80)) + RetStr = "YM2610"; + else + RetStr = "YM2610B"; + break; + case 0x12: // AY8910 + switch(SubType) + { + case 0x00: + RetStr = "AY-3-8910"; + break; + case 0x01: + RetStr = "AY-3-8912"; + break; + case 0x02: + RetStr = "AY-3-8913"; + break; + case 0x03: + RetStr = "AY8930"; + break; + case 0x04: + RetStr = "AY-3-8914"; + break; + case 0x10: + RetStr = "YM2149"; + break; + case 0x11: + RetStr = "YM3439"; + break; + case 0x12: + RetStr = "YMZ284"; + break; + case 0x13: + RetStr = "YMZ294"; + break; + } + break; + case 0x13: + RetStr = "GB DMG"; + break; + case 0x14: + if (! (ChipID & 0x80)) + RetStr = "NES APU"; + else + RetStr = "NES APU + FDS"; + break; + case 0x1C: + switch(SubType) + { + case 0x00: + case 0x01: + RetStr = "C140"; + break; + case 0x02: + RetStr = "C140 (219)"; + break; + } + break; + case 0x21: + RetStr = "WonderSwan"; + break; + case 0x22: + RetStr = "VSU-VUE"; + break; + case 0x25: + if (! (ChipID & 0x80)) + RetStr = "ES5505"; + else + RetStr = "ES5506"; + break; + case 0x28: + RetStr = "Irem GA20"; + break; + } + // catch all default-cases + if (RetStr == NULL) + RetStr = GetChipName(ChipID & 0x7F); + + return RetStr; +} + +UINT32 GetChipClock(void* _p, UINT8 ChipID, UINT8* RetSubType) +{ + UINT32 Clock; + UINT8 SubType; + UINT8 CurChp; + bool AllowBit31; + + VGM_PLAYER* p = (VGM_PLAYER *)_p; + + VGM_HEADER* FileHead = &p->VGMHead; + + SubType = 0x00; + AllowBit31 = 0x00; + switch(ChipID & 0x7F) + { + case 0x00: + Clock = FileHead->lngHzPSG; + AllowBit31 = 0x01; // T6W28 Mode + if (RetSubType != NULL && ! (Clock & 0x80000000)) // The T6W28 is handles differently. + { + switch(FileHead->bytPSG_SRWidth) + { + case 0x0F: // 0x4000 + if (FileHead->bytPSG_Flags & 0x08) // Clock Divider == 1? + SubType = 0x05; // SN94624 + else + SubType = 0x01; // SN76489 + break; + case 0x10: // 0x8000 + if (FileHead->shtPSG_Feedback == 0x0009) + SubType = 0x07; // SEGA PSG + else if (FileHead->shtPSG_Feedback == 0x0022) + SubType = 0x06; // NCR7496 + break; + case 0x11: // 0x10000 + if (FileHead->bytPSG_Flags & 0x08) // Clock Divider == 1? + SubType = 0x03; // SN76494 + else + SubType = 0x02; // SN76489A/SN76496 + break; + } + /* + FbMask Noise Taps Negate Stereo Dv Freq0 Fb SR Flags + 01 SN76489 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE 03 0F 07 (02|04|00|01) [unverified] + 02 SN76489A 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE 0C 11 05 (00|04|00|01) + 03 SN76494 0x10000, 0x04, 0x08, FALSE, FALSE, 1, TRUE 0C 11 0D (00|04|08|01) + 04 SN76496 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE 0C 11 05 (00|04|00|01) [same as SN76489A] + 05 SN94624 0x4000, 0x01, 0x02, TRUE, FALSE, 1, TRUE 03 0F 0F (02|04|08|01) [unverified, SN76489A without /8] + 06 NCR7496 0x8000, 0x02, 0x20, FALSE, FALSE, 8, TRUE 22 10 05 (00|04|00|01) [unverified] + 07 GameGear PSG 0x8000, 0x01, 0x08, TRUE, TRUE, 8, FALSE 09 10 02 (02|00|00|00) + 07 SEGA VDP PSG 0x8000, 0x01, 0x08, TRUE, FALSE, 8, FALSE 09 10 06 (02|04|00|00) + 01 U8106 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE 03 0F 07 (02|04|00|01) [unverified, same as SN76489] + 02 Y2404 0x10000, 0x04, 0x08, FALSE, FALSE; 8, TRUE 0C 11 05 (00|04|00|01) [unverified, same as SN76489A] + -- T6W28 0x4000, 0x01, 0x04, ????, FALSE, 8, ???? 05 0F ?? (??|??|00|01) [It IS stereo, but not in GameGear way]. + */ + } + break; + case 0x01: + Clock = FileHead->lngHzYM2413; + AllowBit31 = 0x01; // VRC7 Mode + break; + case 0x02: + Clock = FileHead->lngHzYM2612; + break; + case 0x03: + Clock = FileHead->lngHzYM2151; + break; + case 0x04: + Clock = FileHead->lngHzSPCM; + break; + case 0x05: + if (ChipID & 0x80) + return 0; + Clock = FileHead->lngHzRF5C68; + break; + case 0x06: + Clock = FileHead->lngHzYM2203; + break; + case 0x07: + Clock = FileHead->lngHzYM2608; + break; + case 0x08: + Clock = FileHead->lngHzYM2610; + AllowBit31 = 0x01; // YM2610B Mode + break; + case 0x09: + Clock = FileHead->lngHzYM3812; + AllowBit31 = 0x01; // Dual OPL2, panned to the L/R speakers + break; + case 0x0A: + Clock = FileHead->lngHzYM3526; + break; + case 0x0B: + Clock = FileHead->lngHzY8950; + break; + case 0x0C: + Clock = FileHead->lngHzYMF262; + break; + case 0x0D: + Clock = FileHead->lngHzYMF278B; + break; + case 0x0E: + Clock = FileHead->lngHzYMF271; + break; + case 0x0F: + Clock = FileHead->lngHzYMZ280B; + break; + case 0x10: + if (ChipID & 0x80) + return 0; + Clock = FileHead->lngHzRF5C164; + AllowBit31 = 0x01; // hack for Cosmic Fantasy Stories + break; + case 0x11: + if (ChipID & 0x80) + return 0; + Clock = FileHead->lngHzPWM; + break; + case 0x12: + Clock = FileHead->lngHzAY8910; + SubType = FileHead->bytAYType; + break; + case 0x13: + Clock = FileHead->lngHzGBDMG; + break; + case 0x14: + Clock = FileHead->lngHzNESAPU; + AllowBit31 = 0x01; // FDS Enable + break; + case 0x15: + Clock = FileHead->lngHzMultiPCM; + break; + case 0x16: + Clock = FileHead->lngHzUPD7759; + AllowBit31 = 0x01; // Master/Slave Bit + break; + case 0x17: + Clock = FileHead->lngHzOKIM6258; + break; + case 0x18: + Clock = FileHead->lngHzOKIM6295; + AllowBit31 = 0x01; // Pin 7 State + break; + case 0x19: + Clock = FileHead->lngHzK051649; + break; + case 0x1A: + Clock = FileHead->lngHzK054539; + break; + case 0x1B: + Clock = FileHead->lngHzHuC6280; + break; + case 0x1C: + Clock = FileHead->lngHzC140; + SubType = FileHead->bytC140Type; + break; + case 0x1D: + Clock = FileHead->lngHzK053260; + break; + case 0x1E: + Clock = FileHead->lngHzPokey; + break; + case 0x1F: + if (ChipID & 0x80) + return 0; + Clock = FileHead->lngHzQSound; + break; + case 0x20: + Clock = FileHead->lngHzSCSP; + break; + case 0x21: + Clock = FileHead->lngHzWSwan; + break; + case 0x22: + Clock = FileHead->lngHzVSU; + break; + case 0x23: + Clock = FileHead->lngHzSAA1099; + break; + case 0x24: + Clock = FileHead->lngHzES5503; + break; + case 0x25: + Clock = FileHead->lngHzES5506; + AllowBit31 = 0x01; // ES5505/5506 switch + break; + case 0x26: + Clock = FileHead->lngHzX1_010; + break; + case 0x27: + Clock = FileHead->lngHzC352; + break; + case 0x28: + Clock = FileHead->lngHzGA20; + break; + default: + return 0; + } + if (ChipID & 0x80) + { + VGMX_CHP_EXTRA32* TempCX; + + if (! (Clock & 0x40000000)) + return 0; + + ChipID &= 0x7F; + TempCX = &p->VGMH_Extra.Clocks; + for (CurChp = 0x00; CurChp < TempCX->ChipCnt; CurChp ++) + { + if (TempCX->CCData[CurChp].Type == ChipID) + { + if (TempCX->CCData[CurChp].Data) + Clock = TempCX->CCData[CurChp].Data; + break; + } + } + } + + if (RetSubType != NULL) + *RetSubType = SubType; + if (AllowBit31) + return Clock & 0xBFFFFFFF; + else + return Clock & 0x3FFFFFFF; +} + +static UINT16 GetChipVolume(VGM_PLAYER* p, UINT8 ChipID, UINT8 ChipNum, UINT8 ChipCnt) +{ + // ChipID: ID of Chip + // Bit 7 - Is Paired Chip + // ChipNum: chip number (0 - first chip, 1 - second chip) + // ChipCnt: chip volume divider (number of used chips) + const UINT16 CHIP_VOLS[CHIP_COUNT] = + { 0x80, 0x200/*0x155*/, 0x100, 0x100, 0x180, 0xB0, 0x100, 0x80, // 00-07 + 0x80, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x98, // 08-0F + 0x80, 0xE0/*0xCD*/, 0x100, 0xC0, 0x100, 0x40, 0x11E, 0x1C0, // 10-17 + 0x100/*110*/, 0xA0, 0x100, 0x100, 0x100, 0xB3, 0x100, 0x100, // 18-1F + 0x100, 0x100, 0x100, 0x100, 0x40, 0x20, 0x100, 0x40, // 20-27 + 0x280}; + UINT16 Volume; + UINT8 CurChp; + VGMX_CHP_EXTRA16* TempCX; + VGMX_CHIP_DATA16* TempCD; + + VGM_HEADER* FileHead = &p->VGMHead; + + Volume = CHIP_VOLS[ChipID & 0x7F]; + switch(ChipID) + { + case 0x00: // SN76496 + // if T6W28, set Volume Divider to 01 + if (GetChipClock(p, (ChipID << 7) | ChipID, NULL) & 0x80000000) + { + // The T6W28 consists of 2 "half" chips. + ChipNum = 0x01; + ChipCnt = 0x01; + } + break; + case 0x18: // OKIM6295 + // CP System 1 patch + if (p->VGMTag.strSystemNameE != NULL && ! wcsncmp(p->VGMTag.strSystemNameE, L"CP", 0x02)) + Volume = 110; + break; + case 0x86: // YM2203's AY + Volume /= 2; + break; + case 0x87: // YM2608's AY + // The YM2608 outputs twice as loud as the YM2203 here. + //Volume *= 1; + break; + case 0x88: // YM2610's AY + //Volume *= 1; + break; + } + if (ChipCnt > 1) + Volume /= ChipCnt; + + TempCX = &p->VGMH_Extra.Volumes; + TempCD = TempCX->CCData; + for (CurChp = 0x00; CurChp < TempCX->ChipCnt; CurChp ++, TempCD ++) + { + if (TempCD->Type == ChipID && (TempCD->Flags & 0x01) == ChipNum) + { + // Bit 15 - absolute/relative volume + // 0 - absolute + // 1 - relative (0x0100 = 1.0, 0x80 = 0.5, etc.) + if (TempCD->Data & 0x8000) + Volume = (Volume * (TempCD->Data & 0x7FFF) + 0x80) >> 8; + else + { + Volume = TempCD->Data; + if ((ChipID & 0x80) && p->DoubleSSGVol) + Volume *= 2; + } + break; + } + } + + return Volume; +} + + +static void RestartPlaying(VGM_PLAYER* p) +{ + p->VGMPos = p->VGMHead.lngDataOffset; + p->VGMSmplPos = 0; + p->VGMSmplPlayed = 0; + p->VGMEnd = false; + p->EndPlay = false; + p->VGMCurLoop = 0x00; + + Chips_GeneralActions(p, 0x01); // Reset Chips + // also does Muting Mask (0x10) and Panning (0x20) + + p->Last95Drum = 0xFFFF; + p->Last95Freq = 0; + p->ForceVGMExec = true; + p->IsVGMInit = true; + InterpretFile(p, 0); + p->IsVGMInit = false; + p->ForceVGMExec = false; + + return; +} + +static void Chips_GeneralActions(VGM_PLAYER* p, UINT8 Mode) +{ + UINT32 AbsVol; + //UINT16 ChipVol; + CAUD_ATTR* CAA; + CHIP_OPTS* COpt; + UINT8 ChipCnt; + UINT8 CurChip; + UINT8 CurCSet; // Chip Set + UINT32 MaskVal; + UINT32 ChipClk; + + switch(Mode) + { + case 0x00: // Start Chips + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + { + CAA->SmpRate = 0x00; + CAA->Volume = 0x00; + CAA->ChipType = 0xFF; + CAA->ChipID = CurCSet; + CAA->Resampler = 0x00; + CAA->StreamUpdate = &null_update; + CAA->StreamUpdateParam = NULL; + CAA->Paired = NULL; + } + CAA = p->CA_Paired[CurCSet]; + for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) + { + CAA->SmpRate = 0x00; + CAA->Volume = 0x00; + CAA->ChipType = 0xFF; + CAA->ChipID = CurCSet; + CAA->Resampler = 0x00; + CAA->StreamUpdate = &null_update; + CAA->StreamUpdateParam = NULL; + CAA->Paired = NULL; + } + } + + // Initialize Sound Chips + AbsVol = 0x00; + if (p->VGMHead.lngHzPSG) + { + //ChipVol = UseFM ? 0x00 : 0x80; + p->ChipOpts[0x01].SN76496.EmuCore = p->ChipOpts[0x00].SN76496.EmuCore; + + ChipCnt = (p->VGMHead.lngHzPSG & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].SN76496; + CAA->ChipType = 0x00; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + ChipClk &= ~0x80000000; + ChipClk |= p->VGMHead.lngHzPSG & ((CurChip & 0x01) << 31); + CAA->SmpRate = device_start_sn764xx(&p->sn764xx[CurChip], + p->ChipOpts[CurChip].SN76496.EmuCore, ChipClk, p->SampleRate, + p->VGMHead.bytPSG_SRWidth, + p->VGMHead.shtPSG_Feedback, + (p->VGMHead.bytPSG_Flags & 0x02) >> 1, + (p->VGMHead.bytPSG_Flags & 0x04) >> 2, + (p->VGMHead.bytPSG_Flags & 0x08) >> 3, + (p->VGMHead.bytPSG_Flags & 0x01) >> 0); + CAA->StreamUpdate = &sn764xx_stream_update; + CAA->StreamUpdateParam = p->sn764xx[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + if (! CurChip || ! (ChipClk & 0x80000000)) + AbsVol += CAA->Volume; + } + if (p->VGMHead.lngHzPSG & 0x80000000) + ChipCnt = 0x01; + } + if (p->VGMHead.lngHzYM2413) + { + //ChipVol = UseFM ? 0x00 : 0x200/*0x155*/; + p->ChipOpts[0x01].YM2413.EmuCore = p->ChipOpts[0x00].YM2413.EmuCore; + + ChipCnt = (p->VGMHead.lngHzYM2413 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM2413; + CAA->ChipType = 0x01; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym2413(&p->ym2413[CurChip], p->ChipOpts[CurChip].YM2413.EmuCore, ChipClk, p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ym2413_stream_update; + CAA->StreamUpdateParam = p->ym2413[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + // WHY has this chip such a low volume??? + //AbsVol += (CAA->Volume + 1) * 3 / 4; + AbsVol += CAA->Volume / 2; + } + } + if (p->VGMHead.lngHzYM2612) + { + //ChipVol = 0x100; + p->ChipOpts[0x01].YM2612.EmuCore = p->ChipOpts[0x00].YM2612.EmuCore; + p->ChipOpts[0x01].YM2612.SpecialFlags = p->ChipOpts[0x00].YM2612.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzYM2612 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM2612; + CAA->ChipType = 0x02; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym2612(&p->ym2612[CurChip], p->ChipOpts[CurChip].YM2612.EmuCore, p->ChipOpts[CurChip].YM2612.SpecialFlags, ChipClk, p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE, &p->IsVGMInit); + CAA->StreamUpdate = &ym2612_stream_update; + CAA->StreamUpdateParam = p->ym2612[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzYM2151) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzYM2151 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM2151; + CAA->ChipType = 0x03; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym2151(&p->ym2151[CurChip], ChipClk, p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ym2151_update; + CAA->StreamUpdateParam = p->ym2151[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzSPCM) + { + //ChipVol = 0x180; + ChipCnt = (p->VGMHead.lngHzSPCM & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].SegaPCM; + CAA->ChipType = 0x04; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_segapcm(&p->segapcm[CurChip], ChipClk, p->VGMHead.lngSPCMIntf); + CAA->StreamUpdate = &SEGAPCM_update; + CAA->StreamUpdateParam = p->segapcm[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzRF5C68) + { + //ChipVol = 0xB0; // that's right according to MAME, but it's almost too loud + ChipCnt = 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].RF5C68; + CAA->ChipType = 0x05; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_rf5c68(&p->rf5c68, ChipClk); + CAA->StreamUpdate = &rf5c68_update; + CAA->StreamUpdateParam = p->rf5c68; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzYM2203) + { + //ChipVol = 0x100; + p->ChipOpts[0x01].YM2203.EmuCore = p->ChipOpts[0x00].YM2203.EmuCore; + p->ChipOpts[0x01].YM2203.SpecialFlags = p->ChipOpts[0x00].YM2203.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzYM2203 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM2203; + COpt = &p->ChipOpts[CurChip].YM2203; + CAA->ChipType = 0x06; + CAA->Paired = &p->CA_Paired[CurChip][0x00]; + CAA->Paired->ChipType = 0x80 | CAA->ChipType; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym2203(&p->ym2203[CurChip], COpt->EmuCore, + ChipClk, COpt->SpecialFlags & 0x01, + p->VGMHead.bytAYFlagYM2203, + (int*) &CAA->Paired->SmpRate, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ym2203_stream_update; + CAA->StreamUpdateParam = p->ym2203[CurChip]; + CAA->Paired->StreamUpdate = &ym2203_stream_update_ay; + CAA->Paired->StreamUpdateParam = p->ym2203[CurChip]; + ym2203_set_srchg_cb(p->ym2203[CurChip], &ChangeChipSampleRate, CAA, CAA->Paired); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + CAA->Paired->Volume = GetChipVolume(p, CAA->Paired->ChipType, + CurChip, ChipCnt); + AbsVol += CAA->Volume + CAA->Paired->Volume; + } + } + if (p->VGMHead.lngHzYM2608) + { + //ChipVol = 0x80; + p->ChipOpts[0x01].YM2608.EmuCore = p->ChipOpts[0x00].YM2608.EmuCore; + p->ChipOpts[0x01].YM2608.SpecialFlags = p->ChipOpts[0x00].YM2608.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzYM2608 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM2608; + COpt = &p->ChipOpts[CurChip].YM2608; + CAA->ChipType = 0x07; + CAA->Paired = &p->CA_Paired[CurChip][0x01]; + CAA->Paired->ChipType = 0x80 | CAA->ChipType; + + ChipClk = GetChipClock(&p->VGMHead, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym2608(&p->ym2608[CurChip], COpt->EmuCore, + ChipClk, COpt->SpecialFlags & 0x01, + p->VGMHead.bytAYFlagYM2608, + (int*) &CAA->Paired->SmpRate, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ym2608_stream_update; + CAA->StreamUpdateParam = p->ym2608[CurChip]; + CAA->Paired->StreamUpdate = &ym2608_stream_update_ay; + CAA->Paired->StreamUpdateParam = p->ym2608[CurChip]; + ym2608_set_srchg_cb(p->ym2608[CurChip], &ChangeChipSampleRate, CAA, CAA->Paired); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + CAA->Paired->Volume = GetChipVolume(p, CAA->Paired->ChipType, + CurChip, ChipCnt); + AbsVol += CAA->Volume + CAA->Paired->Volume; + //CAA->Volume = ChipVol; + //CAA->Paired->Volume = ChipVol * 2; + } + } + if (p->VGMHead.lngHzYM2610) + { + //ChipVol = 0x80; + p->ChipOpts[0x01].YM2610.EmuCore = p->ChipOpts[0x00].YM2610.EmuCore; + p->ChipOpts[0x01].YM2610.SpecialFlags = p->ChipOpts[0x00].YM2610.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzYM2610 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM2610; + COpt = &p->ChipOpts[CurChip].YM2610; + CAA->ChipType = 0x08; + CAA->Paired = &p->CA_Paired[CurChip][0x02]; + CAA->Paired->ChipType = 0x80 | CAA->ChipType; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym2610(&p->ym2610[CurChip], COpt->EmuCore, + ChipClk, COpt->SpecialFlags & 0x01, + (int*) &CAA->Paired->SmpRate, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = (ChipClk & 0x80000000) ? ym2610b_stream_update : + ym2610_stream_update; + CAA->StreamUpdateParam = p->ym2610[CurChip]; + CAA->Paired->StreamUpdate = &ym2610_stream_update_ay; + CAA->Paired->StreamUpdateParam = p->ym2610[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + CAA->Paired->Volume = GetChipVolume(p, CAA->Paired->ChipType, + CurChip, ChipCnt); + AbsVol += CAA->Volume + CAA->Paired->Volume; + //CAA->Volume = ChipVol; + //CAA->Paired->Volume = ChipVol * 2; + } + } + if (p->VGMHead.lngHzYM3812) + { + //ChipVol = UseFM ? 0x00 : 0x100; + p->ChipOpts[0x01].YM3812.EmuCore = p->ChipOpts[0x00].YM3812.EmuCore; + + ChipCnt = (p->VGMHead.lngHzYM3812 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM3812; + CAA->ChipType = 0x09; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym3812(&p->ym3812[CurChip], + p->ChipOpts[CurChip].YM3812.EmuCore, + ChipClk, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + if (ChipClk & 0x80000000) + { + struct dual_opl2_info * info = (struct dual_opl2_info *) malloc(sizeof(struct dual_opl2_info)); + + CAA->StreamUpdate = dual_opl2_stereo; + CAA->StreamUpdateParam = (void *) info; + + info->chip = p->ym3812[CurChip]; + info->ChipID = CurChip; + + p->ym3812_dual_data[CurChip] = (void *) info; + } + else + { + CAA->StreamUpdate = ym3812_stream_update; + CAA->StreamUpdateParam = p->ym3812[CurChip]; + p->ym3812_dual_data[CurChip] = NULL; + } + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + if (! CurChip || ! (ChipClk & 0x80000000)) + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzYM3526) + { + //ChipVol = UseFM ? 0x00 : 0x100; + ChipCnt = (p->VGMHead.lngHzYM3526 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YM3526; + CAA->ChipType = 0x0A; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ym3526(&p->ym3526[CurChip], ChipClk, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ym3526_stream_update; + CAA->StreamUpdateParam = p->ym3526[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzY8950) + { + //ChipVol = UseFM ? 0x00 : 0x100; + ChipCnt = (p->VGMHead.lngHzY8950 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].Y8950; + CAA->ChipType = 0x0B; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_y8950(&p->y8950[CurChip], ChipClk, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &y8950_stream_update; + CAA->StreamUpdateParam = p->y8950[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzYMF262) + { + //ChipVol = UseFM ? 0x00 : 0x100; + p->ChipOpts[0x01].YMF262.EmuCore = p->ChipOpts[0x00].YMF262.EmuCore; + + ChipCnt = (p->VGMHead.lngHzYMF262 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YMF262; + CAA->ChipType = 0x0C; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ymf262(&p->ymf262[CurChip], + p->ChipOpts[CurChip].YMF262.EmuCore, + ChipClk, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ymf262_stream_update; + CAA->StreamUpdateParam = p->ymf262[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzYMF278B) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzYMF278B & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YMF278B; + CAA->ChipType = 0x0D; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ymf278b(&p->ymf278b[CurChip], ChipClk); + CAA->StreamUpdate = &ymf278b_pcm_update; + CAA->StreamUpdateParam = p->ymf278b[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; //good as long as it only uses WaveTable Synth + } + } + if (p->VGMHead.lngHzYMF271) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzYMF271 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YMF271; + CAA->ChipType = 0x0E; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ymf271(&p->ymf271[CurChip], ChipClk); + CAA->StreamUpdate = &ymf271_update; + CAA->StreamUpdateParam = p->ymf271[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzYMZ280B) + { + //ChipVol = 0x98; + ChipCnt = (p->VGMHead.lngHzYMZ280B & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].YMZ280B; + CAA->ChipType = 0x0F; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ymz280b(&p->ymz280b[CurChip], ChipClk); + CAA->StreamUpdate = &ymz280b_update; + CAA->StreamUpdateParam = p->ymz280b[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += (CAA->Volume * 0x20 / 0x13); + } + } + if (p->VGMHead.lngHzRF5C164) + { + //ChipVol = 0x80; + ChipCnt = 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].RF5C164; + CAA->ChipType = 0x10; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_rf5c164(&p->rf5c164, ChipClk, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &rf5c164_update; + CAA->StreamUpdateParam = p->rf5c164; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzPWM) + { + //ChipVol = 0xE0; // 0xCD + ChipCnt = 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].PWM; + CAA->ChipType = 0x11; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_pwm(&p->pwm, ChipClk, p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &pwm_update; + CAA->StreamUpdateParam = p->pwm; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzAY8910) + { + //ChipVol = 0x100; + p->ChipOpts[0x01].AY8910.EmuCore = p->ChipOpts[0x00].AY8910.EmuCore; + + ChipCnt = (p->VGMHead.lngHzAY8910 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].AY8910; + CAA->ChipType = 0x12; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_ayxx(&p->ay8910[CurChip], + p->ChipOpts[CurChip].AY8910.EmuCore, + ChipClk, p->VGMHead.bytAYType, p->VGMHead.bytAYFlag, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ayxx_stream_update; + CAA->StreamUpdateParam = p->ay8910[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzGBDMG) + { + //ChipVol = 0xC0; + p->ChipOpts[0x01].GameBoy.SpecialFlags = p->ChipOpts[0x00].GameBoy.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzGBDMG & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].GameBoy; + CAA->ChipType = 0x13; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_gameboy_sound(&p->gbdmg[CurChip], ChipClk, + p->ChipOpts[CurChip].GameBoy.SpecialFlags, + p->SampleRate); + CAA->StreamUpdate = &gameboy_update; + CAA->StreamUpdateParam = p->gbdmg[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzNESAPU) + { + //ChipVol = 0x100; + p->ChipOpts[0x01].NES.EmuCore = p->ChipOpts[0x00].NES.EmuCore; + p->ChipOpts[0x01].NES.SpecialFlags = p->ChipOpts[0x00].NES.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzNESAPU & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].NES; + COpt = &p->ChipOpts[CurChip].NES; + CAA->ChipType = 0x14; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_nes(&p->nesapu[CurChip], COpt->EmuCore, + ChipClk, COpt->SpecialFlags, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &nes_stream_update; + CAA->StreamUpdateParam = p->nesapu[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzMultiPCM) + { + //ChipVol = 0x40; + ChipCnt = (p->VGMHead.lngHzMultiPCM & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].MultiPCM; + CAA->ChipType = 0x15; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_multipcm(&p->multipcm[CurChip], ChipClk); + CAA->StreamUpdate = &MultiPCM_update; + CAA->StreamUpdateParam = p->multipcm[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 4; + } + } + if (p->VGMHead.lngHzUPD7759) + { + //ChipVol = 0x11E; + ChipCnt = (p->VGMHead.lngHzUPD7759 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].UPD7759; + CAA->ChipType = 0x16; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_upd7759(&p->upd7759[CurChip], ChipClk); + CAA->StreamUpdate = &upd7759_update; + CAA->StreamUpdateParam = p->upd7759[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzOKIM6258) + { + //ChipVol = 0x1C0; + p->ChipOpts[0x01].OKIM6258.SpecialFlags = p->ChipOpts[0x00].OKIM6258.SpecialFlags; + + ChipCnt = (p->VGMHead.lngHzOKIM6258 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].OKIM6258; + CAA->ChipType = 0x17; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_okim6258(&p->okim6258[CurChip], ChipClk, + p->ChipOpts[CurChip].OKIM6258.SpecialFlags, + (p->VGMHead.bytOKI6258Flags & 0x03) >> 0, + (p->VGMHead.bytOKI6258Flags & 0x04) >> 2, + (p->VGMHead.bytOKI6258Flags & 0x08) >> 3); + CAA->StreamUpdate = &okim6258_update; + CAA->StreamUpdateParam = p->okim6258[CurChip]; + okim6258_set_srchg_cb(p->okim6258[CurChip], &ChangeChipSampleRate, CAA); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzOKIM6295) + { + /*// Use the System Tag to decide between normal and CP System volume level. + // I know, this is very hackish, but ATM there's no better solution. + if (VGMTag.strSystemNameE != NULL && ! wcsncmp(VGMTag.strSystemNameE, L"CP", 0x02)) + ChipVol = 110; + else + ChipVol = 0x100;*/ + ChipCnt = (p->VGMHead.lngHzOKIM6295 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].OKIM6295; + CAA->ChipType = 0x18; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_okim6295(&p->okim6295[CurChip], ChipClk); + CAA->StreamUpdate = &okim6295_update; + CAA->StreamUpdateParam = p->okim6295[CurChip]; + okim6295_set_srchg_cb(p->okim6295[CurChip], &ChangeChipSampleRate, CAA); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 2; + } + } + if (p->VGMHead.lngHzK051649) + { + //ChipVol = 0xA0; + ChipCnt = (p->VGMHead.lngHzK051649 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].K051649; + CAA->ChipType = 0x19; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_k051649(&p->k051649[CurChip], ChipClk); + CAA->StreamUpdate = &k051649_update; + CAA->StreamUpdateParam = p->k051649[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzK054539) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzK054539 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].K054539; + CAA->ChipType = 0x1A; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_k054539(&p->k054539[CurChip], ChipClk); + CAA->StreamUpdate = &k054539_update; + CAA->StreamUpdateParam = p->k054539[CurChip]; + k054539_init_flags(p->k054539[CurChip], p->VGMHead.bytK054539Flags); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzHuC6280) + { + //ChipVol = 0x100; + p->ChipOpts[0x01].HuC6280.EmuCore = p->ChipOpts[0x00].HuC6280.EmuCore; + + ChipCnt = (p->VGMHead.lngHzHuC6280 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].HuC6280; + CAA->ChipType = 0x1B; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_c6280(&p->huc6280[CurChip], + p->ChipOpts[CurChip].HuC6280.EmuCore, + ChipClk, p->SampleRate, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &c6280_update; + CAA->StreamUpdateParam = p->huc6280[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzC140) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzC140 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].C140; + CAA->ChipType = 0x1C; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_c140(&p->c140[CurChip], ChipClk, p->VGMHead.bytC140Type, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &c140_update; + CAA->StreamUpdateParam = p->c140[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzK053260) + { + //ChipVol = 0xB3; + ChipCnt = (p->VGMHead.lngHzK053260 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].K053260; + CAA->ChipType = 0x1D; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_k053260(&p->k053260[CurChip], ChipClk); + CAA->StreamUpdate = &k053260_update; + CAA->StreamUpdateParam = p->k053260[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzPokey) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzPokey & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].Pokey; + CAA->ChipType = 0x1E; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_pokey(&p->pokey[CurChip], ChipClk); + CAA->StreamUpdate = &pokey_update; + CAA->StreamUpdateParam = p->pokey[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzQSound) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzQSound & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].QSound; + CAA->ChipType = 0x1F; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_qsound(&p->qsound[CurChip], ChipClk); + CAA->StreamUpdate = &qsound_update; + CAA->StreamUpdateParam = p->qsound[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzSCSP) + { + p->ChipOpts[0x01].SCSP.SpecialFlags = p->ChipOpts[0x00].SCSP.SpecialFlags; + + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzSCSP & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].SCSP; + CAA->ChipType = 0x20; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_scsp(&p->scsp[CurChip], ChipClk, p->ChipOpts[CurChip].SCSP.SpecialFlags); + CAA->StreamUpdate = &SCSP_Update; + CAA->StreamUpdateParam = p->scsp[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzWSwan) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzWSwan & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].WSwan; + CAA->ChipType = 0x21; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = ws_audio_init(&p->wswan[CurChip], ChipClk, p->SampleRate, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &ws_audio_update; + CAA->StreamUpdateParam = p->wswan[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzVSU) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzVSU & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].VSU; + CAA->ChipType = 0x22; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_vsu(&p->vsu[CurChip], ChipClk, p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &vsu_stream_update; + CAA->StreamUpdateParam = p->vsu[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzSAA1099) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzSAA1099 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].SAA1099; + CAA->ChipType = 0x23; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_saa1099(&p->saa1099[CurChip], ChipClk); + CAA->StreamUpdate = &saa1099_update; + CAA->StreamUpdateParam = p->saa1099[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzES5503) + { + //ChipVol = 0x40; + ChipCnt = (p->VGMHead.lngHzES5503 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].ES5503; + CAA->ChipType = 0x24; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_es5503(&p->es5503[CurChip], ChipClk, p->VGMHead.bytES5503Chns); + CAA->StreamUpdate = &es5503_pcm_update; + CAA->StreamUpdateParam = p->es5503[CurChip]; + es5503_set_srchg_cb(p->es5503[CurChip], &ChangeChipSampleRate, CAA); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 8; + } + } + if (p->VGMHead.lngHzES5506) + { + //ChipVol = 0x20; + ChipCnt = (p->VGMHead.lngHzES5506 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].ES5506; + CAA->ChipType = 0x25; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_es5506(&p->es550x[CurChip], ChipClk, p->VGMHead.bytES5506Chns); + CAA->StreamUpdate = &es5506_update; + CAA->StreamUpdateParam = p->es550x[CurChip]; + es5506_set_srchg_cb(p->es550x[CurChip], &ChangeChipSampleRate, CAA); + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 16; + } + } + if (p->VGMHead.lngHzX1_010) + { + //ChipVol = 0x100; + ChipCnt = (p->VGMHead.lngHzX1_010 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].X1_010; + CAA->ChipType = 0x26; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_x1_010(&p->x1_010[CurChip], ChipClk, + p->CHIP_SAMPLING_MODE, p->CHIP_SAMPLE_RATE); + CAA->StreamUpdate = &seta_update; + CAA->StreamUpdateParam = p->x1_010[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + if (p->VGMHead.lngHzC352) + { + //ChipVol = 0x40; + ChipCnt = (p->VGMHead.lngHzC352 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].C352; + CAA->ChipType = 0x27; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_c352(&p->c352[CurChip], ChipClk, p->VGMHead.bytC352ClkDiv * 4); + CAA->StreamUpdate = &c352_update; + CAA->StreamUpdateParam = p->c352[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume * 8; + } + } + if (p->VGMHead.lngHzGA20) + { + //ChipVol = 0x280; + ChipCnt = (p->VGMHead.lngHzGA20 & 0x40000000) ? 0x02 : 0x01; + for (CurChip = 0x00; CurChip < ChipCnt; CurChip ++) + { + CAA = &p->ChipAudio[CurChip].GA20; + CAA->ChipType = 0x28; + + ChipClk = GetChipClock(p, (CurChip << 7) | CAA->ChipType, NULL); + CAA->SmpRate = device_start_iremga20(&p->ga20[CurChip], ChipClk); + CAA->StreamUpdate = &IremGA20_update; + CAA->StreamUpdateParam = p->ga20[CurChip]; + + CAA->Volume = GetChipVolume(p, CAA->ChipType, CurChip, ChipCnt); + AbsVol += CAA->Volume; + } + } + + // Initialize DAC Control and PCM Bank + p->DacCtrlUsed = 0x00; + //memset(DacCtrlUsg, 0x00, 0x01 * 0xFF); + for (CurChip = 0x00; CurChip < 0xFF; CurChip ++) + { + p->DacCtrl[CurChip].Enable = false; + } + //memset(p->DacCtrl, 0x00, sizeof(DACCTRL_DATA) * 0xFF); + + memset(p->PCMBank, 0x00, sizeof(VGM_PCM_BANK) * PCM_BANK_COUNT); + memset(&p->PCMTbl, 0x00, sizeof(PCMBANK_TBL)); + + // Reset chips + Chips_GeneralActions(p, 0x01); + + while(AbsVol < 0x200 && AbsVol) + { + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + CAA->Volume *= 2; + CAA = p->CA_Paired[CurCSet]; + for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) + CAA->Volume *= 2; + } + AbsVol *= 2; + } + while(AbsVol > 0x300) + { + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + CAA->Volume /= 2; + CAA = p->CA_Paired[CurCSet]; + for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) + CAA->Volume /= 2; + } + AbsVol /= 2; + } + + // Initialize Resampler + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + SetupResampler(p, CAA); + + CAA = p->CA_Paired[CurCSet]; + for (CurChip = 0x00; CurChip < 0x03; CurChip ++, CAA ++) + SetupResampler(p, CAA); + } + + GeneralChipLists(p); + break; + case 0x01: // Reset chips + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + { + if (CAA->ChipType == 0xFF) // chip unused + continue; + else if (CAA->ChipType == 0x00) + device_reset_sn764xx(p->sn764xx[CurCSet]); + else if (CAA->ChipType == 0x01) + device_reset_ym2413(p->ym2413[CurCSet]); + else if (CAA->ChipType == 0x02) + device_reset_ym2612(p->ym2612[CurCSet]); + else if (CAA->ChipType == 0x03) + device_reset_ym2151(p->ym2151[CurCSet]); + else if (CAA->ChipType == 0x04) + device_reset_segapcm(p->segapcm[CurCSet]); + else if (CAA->ChipType == 0x05 && CurCSet == 0x00) + device_reset_rf5c68(p->rf5c68); + else if (CAA->ChipType == 0x06) + device_reset_ym2203(p->ym2203[CurCSet]); + else if (CAA->ChipType == 0x07) + device_reset_ym2608(p->ym2608[CurCSet]); + else if (CAA->ChipType == 0x08) + device_reset_ym2610(p->ym2610[CurCSet]); + else if (CAA->ChipType == 0x09) + device_reset_ym3812(p->ym3812[CurCSet]); + else if (CAA->ChipType == 0x0A) + device_reset_ym3526(p->ym3526[CurCSet]); + else if (CAA->ChipType == 0x0B) + device_reset_y8950(p->y8950[CurCSet]); + else if (CAA->ChipType == 0x0C) + device_reset_ymf262(p->ymf262[CurCSet]); + else if (CAA->ChipType == 0x0D) + device_reset_ymf278b(p->ymf278b[CurCSet]); + else if (CAA->ChipType == 0x0E) + device_reset_ymf271(p->ymf271[CurCSet]); + else if (CAA->ChipType == 0x0F) + device_reset_ymz280b(p->ymz280b[CurCSet]); + else if (CAA->ChipType == 0x10 && CurCSet == 0x00) + device_reset_rf5c164(p->rf5c164); + else if (CAA->ChipType == 0x11 && CurCSet == 0x00) + device_reset_pwm(p->pwm); + else if (CAA->ChipType == 0x12) + device_reset_ayxx(p->ay8910[CurCSet]); + else if (CAA->ChipType == 0x13) + device_reset_gameboy_sound(p->gbdmg[CurCSet]); + else if (CAA->ChipType == 0x14) + device_reset_nes(p->nesapu[CurCSet]); + else if (CAA->ChipType == 0x15) + device_reset_multipcm(p->multipcm[CurCSet]); + else if (CAA->ChipType == 0x16) + device_reset_upd7759(p->upd7759[CurCSet]); + else if (CAA->ChipType == 0x17) + device_reset_okim6258(p->okim6258[CurCSet]); + else if (CAA->ChipType == 0x18) + device_reset_okim6295(p->okim6295[CurCSet]); + else if (CAA->ChipType == 0x19) + device_reset_k051649(p->k051649[CurCSet]); + else if (CAA->ChipType == 0x1A) + device_reset_k054539(p->k054539[CurCSet]); + else if (CAA->ChipType == 0x1B) + device_reset_c6280(p->huc6280[CurCSet]); + else if (CAA->ChipType == 0x1C) + device_reset_c140(p->c140[CurCSet]); + else if (CAA->ChipType == 0x1D) + device_reset_k053260(p->k053260[CurCSet]); + else if (CAA->ChipType == 0x1E) + device_reset_pokey(p->pokey[CurCSet]); + else if (CAA->ChipType == 0x1F) + device_reset_qsound(p->qsound[CurCSet]); + else if (CAA->ChipType == 0x20) + device_reset_scsp(p->scsp[CurCSet]); + else if (CAA->ChipType == 0x21) + ws_audio_reset(p->wswan[CurCSet]); + else if (CAA->ChipType == 0x22) + device_reset_vsu(p->vsu[CurCSet]); + else if (CAA->ChipType == 0x23) + device_reset_saa1099(p->saa1099[CurCSet]); + else if (CAA->ChipType == 0x24) + device_reset_es5503(p->es5503[CurCSet]); + else if (CAA->ChipType == 0x25) + device_reset_es5506(p->es550x[CurCSet]); + else if (CAA->ChipType == 0x26) + device_reset_x1_010(p->x1_010[CurCSet]); + else if (CAA->ChipType == 0x27) + device_reset_c352(p->c352[CurCSet]); + else if (CAA->ChipType == 0x28) + device_reset_iremga20(p->ga20[CurCSet]); + } // end for CurChip + + } // end for CurCSet + + Chips_GeneralActions(p, 0x10); // set muting mask + Chips_GeneralActions(p, 0x20); // set panning + + for (CurChip = 0x00; CurChip < p->DacCtrlUsed; CurChip ++) + { + CurCSet = p->DacCtrlUsg[CurChip]; + device_reset_daccontrol(p->daccontrol[CurCSet]); + //DacCtrl[CurCSet].Enable = false; + } + //DacCtrlUsed = 0x00; + //memset(DacCtrlUsg, 0x00, 0x01 * 0xFF); + + for (CurChip = 0x00; CurChip < PCM_BANK_COUNT; CurChip ++) + { + // reset PCM Bank, but not the data + // (this way I don't need to decompress the data again when restarting) + p->PCMBank[CurChip].DataPos = 0x00000000; + p->PCMBank[CurChip].BnkPos = 0x00000000; + } + p->PCMTbl.EntryCount = 0x00; + break; + case 0x02: // Stop chips + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + { + if (CAA->ChipType == 0xFF) // chip unused + continue; + else if (CAA->ChipType == 0x00) + device_stop_sn764xx(p->sn764xx[CurCSet]); + else if (CAA->ChipType == 0x01) + device_stop_ym2413(p->ym2413[CurCSet]); + else if (CAA->ChipType == 0x02) + device_stop_ym2612(p->ym2612[CurCSet]); + else if (CAA->ChipType == 0x03) + device_stop_ym2151(p->ym2151[CurCSet]); + else if (CAA->ChipType == 0x04) + device_stop_segapcm(p->segapcm[CurCSet]); + else if (CAA->ChipType == 0x05 && CurCSet == 0x00) + device_stop_rf5c68(p->rf5c68); + else if (CAA->ChipType == 0x06) + device_stop_ym2203(p->ym2203[CurCSet]); + else if (CAA->ChipType == 0x07) + device_stop_ym2608(p->ym2608[CurCSet]); + else if (CAA->ChipType == 0x08) + device_stop_ym2610(p->ym2610[CurCSet]); + else if (CAA->ChipType == 0x09) + { + device_stop_ym3812(p->ym3812[CurCSet]); + free(p->ym3812_dual_data[CurCSet]); + p->ym3812_dual_data[CurCSet] = NULL; + } + else if (CAA->ChipType == 0x0A) + device_stop_ym3526(p->ym3526[CurCSet]); + else if (CAA->ChipType == 0x0B) + device_stop_y8950(p->y8950[CurCSet]); + else if (CAA->ChipType == 0x0C) + device_stop_ymf262(p->ymf262[CurCSet]); + else if (CAA->ChipType == 0x0D) + device_stop_ymf278b(p->ymf278b[CurCSet]); + else if (CAA->ChipType == 0x0E) + device_stop_ymf271(p->ymf271[CurCSet]); + else if (CAA->ChipType == 0x0F) + device_stop_ymz280b(p->ymz280b[CurCSet]); + else if (CAA->ChipType == 0x10 && CurCSet == 0x00) + device_stop_rf5c164(p->rf5c164); + else if (CAA->ChipType == 0x11 && CurCSet == 0x00) + device_stop_pwm(p->pwm); + else if (CAA->ChipType == 0x12) + device_stop_ayxx(p->ay8910[CurCSet]); + else if (CAA->ChipType == 0x13) + device_stop_gameboy_sound(p->gbdmg[CurCSet]); + else if (CAA->ChipType == 0x14) + device_stop_nes(p->nesapu[CurCSet]); + else if (CAA->ChipType == 0x15) + device_stop_multipcm(p->multipcm[CurCSet]); + else if (CAA->ChipType == 0x16) + device_stop_upd7759(p->upd7759[CurCSet]); + else if (CAA->ChipType == 0x17) + device_stop_okim6258(p->okim6258[CurCSet]); + else if (CAA->ChipType == 0x18) + device_stop_okim6295(p->okim6295[CurCSet]); + else if (CAA->ChipType == 0x19) + device_stop_k051649(p->k051649[CurCSet]); + else if (CAA->ChipType == 0x1A) + device_stop_k054539(p->k054539[CurCSet]); + else if (CAA->ChipType == 0x1B) + device_stop_c6280(p->huc6280[CurCSet]); + else if (CAA->ChipType == 0x1C) + device_stop_c140(p->c140[CurCSet]); + else if (CAA->ChipType == 0x1D) + device_stop_k053260(p->k053260[CurCSet]); + else if (CAA->ChipType == 0x1E) + device_stop_pokey(p->pokey[CurCSet]); + else if (CAA->ChipType == 0x1F) + device_stop_qsound(p->qsound[CurCSet]); + else if (CAA->ChipType == 0x20) + device_stop_scsp(p->scsp[CurCSet]); + else if (CAA->ChipType == 0x21) + ws_audio_done(p->wswan[CurCSet]); + else if (CAA->ChipType == 0x22) + device_stop_vsu(p->vsu[CurCSet]); + else if (CAA->ChipType == 0x23) + device_stop_saa1099(p->saa1099[CurCSet]); + else if (CAA->ChipType == 0x24) + device_stop_es5503(p->es5503[CurCSet]); + else if (CAA->ChipType == 0x25) + device_stop_es5506(p->es550x[CurCSet]); + else if (CAA->ChipType == 0x26) + device_stop_x1_010(p->x1_010[CurCSet]); + else if (CAA->ChipType == 0x27) + device_stop_c352(p->c352[CurCSet]); + else if (CAA->ChipType == 0x28) + device_stop_iremga20(p->ga20[CurCSet]); + + resampler_destroy(CAA->Resampler); + CAA->Resampler = 0x00; + + CAA->ChipType = 0xFF; // mark as "unused" + } // end for CurChip + + } // end for CurCSet + + for (CurChip = 0x00; CurChip < p->DacCtrlUsed; CurChip ++) + { + CurCSet = p->DacCtrlUsg[CurChip]; + device_stop_daccontrol(p->daccontrol[CurCSet]); + p->DacCtrl[CurCSet].Enable = false; + } + p->DacCtrlUsed = 0x00; + + for (CurChip = 0x00; CurChip < PCM_BANK_COUNT; CurChip ++) + { + free(p->PCMBank[CurChip].Bank); + free(p->PCMBank[CurChip].Data); + } + //memset(PCMBank, 0x00, sizeof(VGM_PCM_BANK) * PCM_BANK_COUNT); + free(p->PCMTbl.Entries); + //memset(&PCMTbl, 0x00, sizeof(PCMBANK_TBL)); + break; + case 0x10: // Set Muting Mask + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + { + if (CAA->ChipType == 0xFF) // chip unused + continue; + else if (CAA->ChipType == 0x00) + sn764xx_set_mute_mask(p->sn764xx[CurCSet], p->ChipOpts[CurCSet].SN76496.ChnMute1); + else if (CAA->ChipType == 0x01) + ym2413_set_mute_mask(p->ym2413[CurCSet], p->ChipOpts[CurCSet].YM2413.ChnMute1); + else if (CAA->ChipType == 0x02) + ym2612_set_mute_mask(p->ym2612[CurCSet], p->ChipOpts[CurCSet].YM2612.ChnMute1); + else if (CAA->ChipType == 0x03) + ym2151_set_mute_mask(p->ym2151[CurCSet], p->ChipOpts[CurCSet].YM2151.ChnMute1); + else if (CAA->ChipType == 0x04) + segapcm_set_mute_mask(p->segapcm[CurCSet], p->ChipOpts[CurCSet].SegaPCM.ChnMute1); + else if (CAA->ChipType == 0x05 && CurCSet == 0x00) + rf5c68_set_mute_mask(p->rf5c68, p->ChipOpts[CurCSet].RF5C68.ChnMute1); + else if (CAA->ChipType == 0x06) + ym2203_set_mute_mask(p->ym2203[CurCSet], p->ChipOpts[CurCSet].YM2203.ChnMute1, + p->ChipOpts[CurCSet].YM2203.ChnMute3); + else if (CAA->ChipType == 0x07) + { + MaskVal = (p->ChipOpts[CurCSet].YM2608.ChnMute1 & 0x3F) << 0; + MaskVal |= (p->ChipOpts[CurCSet].YM2608.ChnMute2 & 0x7F) << 6; + ym2608_set_mute_mask(p->ym2608[CurCSet], MaskVal, p->ChipOpts[CurCSet].YM2608.ChnMute3); + } + else if (CAA->ChipType == 0x08) + { + MaskVal = (p->ChipOpts[CurCSet].YM2610.ChnMute1 & 0x3F) << 0; + MaskVal |= (p->ChipOpts[CurCSet].YM2610.ChnMute2 & 0x7F) << 6; + ym2610_set_mute_mask(p->ym2610[CurCSet], MaskVal, p->ChipOpts[CurCSet].YM2610.ChnMute3); + } + else if (CAA->ChipType == 0x09) + ym3812_set_mute_mask(p->ym3812[CurCSet], p->ChipOpts[CurCSet].YM3812.ChnMute1); + else if (CAA->ChipType == 0x0A) + ym3526_set_mute_mask(p->ym3526[CurCSet], p->ChipOpts[CurCSet].YM3526.ChnMute1); + else if (CAA->ChipType == 0x0B) + y8950_set_mute_mask(p->y8950[CurCSet], p->ChipOpts[CurCSet].Y8950.ChnMute1); + else if (CAA->ChipType == 0x0C) + ymf262_set_mute_mask(p->ymf262[CurCSet], p->ChipOpts[CurCSet].YMF262.ChnMute1); + else if (CAA->ChipType == 0x0D) + ymf278b_set_mute_mask(p->ymf278b[CurCSet], p->ChipOpts[CurCSet].YMF278B.ChnMute1, + p->ChipOpts[CurCSet].YMF278B.ChnMute2); + else if (CAA->ChipType == 0x0E) + ymf271_set_mute_mask(p->ymf271[CurCSet], p->ChipOpts[CurCSet].YMF271.ChnMute1); + else if (CAA->ChipType == 0x0F) + ymz280b_set_mute_mask(p->ymz280b[CurCSet], p->ChipOpts[CurCSet].YMZ280B.ChnMute1); + else if (CAA->ChipType == 0x10 && CurCSet == 0x00) + rf5c164_set_mute_mask(p->rf5c164, p->ChipOpts[CurCSet].RF5C164.ChnMute1); + else if (CAA->ChipType == 0x11) + ; // PWM - nothing to mute + else if (CAA->ChipType == 0x12) + ayxx_set_mute_mask(p->ay8910[CurCSet], p->ChipOpts[CurCSet].AY8910.ChnMute1); + else if (CAA->ChipType == 0x13) + gameboy_sound_set_mute_mask(p->gbdmg[CurCSet], p->ChipOpts[CurCSet].GameBoy.ChnMute1); + else if (CAA->ChipType == 0x14) + nes_set_mute_mask(p->nesapu[CurCSet], p->ChipOpts[CurCSet].NES.ChnMute1); + else if (CAA->ChipType == 0x15) + multipcm_set_mute_mask(p->multipcm[CurCSet], p->ChipOpts[CurCSet].MultiPCM.ChnMute1); + else if (CAA->ChipType == 0x16) + ; // UPD7759 - nothing to mute + else if (CAA->ChipType == 0x17) + ; // OKIM6258 - nothing to mute + else if (CAA->ChipType == 0x18) + okim6295_set_mute_mask(p->okim6295[CurCSet], p->ChipOpts[CurCSet].OKIM6295.ChnMute1); + else if (CAA->ChipType == 0x19) + k051649_set_mute_mask(p->k051649[CurCSet], p->ChipOpts[CurCSet].K051649.ChnMute1); + else if (CAA->ChipType == 0x1A) + k054539_set_mute_mask(p->k054539[CurCSet], p->ChipOpts[CurCSet].K054539.ChnMute1); + else if (CAA->ChipType == 0x1B) + c6280_set_mute_mask(p->huc6280[CurCSet], p->ChipOpts[CurCSet].HuC6280.ChnMute1); + else if (CAA->ChipType == 0x1C) + c140_set_mute_mask(p->c140[CurCSet], p->ChipOpts[CurCSet].C140.ChnMute1); + else if (CAA->ChipType == 0x1D) + k053260_set_mute_mask(p->k053260[CurCSet], p->ChipOpts[CurCSet].K053260.ChnMute1); + else if (CAA->ChipType == 0x1E) + pokey_set_mute_mask(p->pokey[CurCSet], p->ChipOpts[CurCSet].Pokey.ChnMute1); + else if (CAA->ChipType == 0x1F) + qsound_set_mute_mask(p->qsound[CurCSet], p->ChipOpts[CurCSet].QSound.ChnMute1); + else if (CAA->ChipType == 0x20) + scsp_set_mute_mask(p->scsp[CurCSet], p->ChipOpts[CurCSet].SCSP.ChnMute1); + else if (CAA->ChipType == 0x21) + ws_set_mute_mask(p->wswan[CurCSet], p->ChipOpts[CurCSet].WSwan.ChnMute1); + else if (CAA->ChipType == 0x22) + vsu_set_mute_mask(p->vsu[CurCSet], p->ChipOpts[CurCSet].VSU.ChnMute1); + else if (CAA->ChipType == 0x23) + saa1099_set_mute_mask(p->saa1099[CurCSet], p->ChipOpts[CurCSet].SAA1099.ChnMute1); + else if (CAA->ChipType == 0x24) + es5503_set_mute_mask(p->es5503[CurCSet], p->ChipOpts[CurCSet].ES5503.ChnMute1); + else if (CAA->ChipType == 0x25) + es5506_set_mute_mask(p->es550x[CurCSet], p->ChipOpts[CurCSet].ES5506.ChnMute1); + else if (CAA->ChipType == 0x26) + x1_010_set_mute_mask(p->x1_010[CurCSet], p->ChipOpts[CurCSet].X1_010.ChnMute1); + else if (CAA->ChipType == 0x27) + c352_set_mute_mask(p->c352[CurCSet], p->ChipOpts[CurCSet].C352.ChnMute1); + else if (CAA->ChipType == 0x28) + iremga20_set_mute_mask(p->ga20[CurCSet], p->ChipOpts[CurCSet].GA20.ChnMute1); + } // end for CurChip + + } // end for CurCSet + break; + case 0x20: // Set Panning + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, CAA ++) + { + if (CAA->ChipType == 0xFF) // chip unused + continue; + else if (CAA->ChipType == 0x00) + sn764xx_set_panning(p->sn764xx[CurCSet], p->ChipOpts[CurCSet].SN76496.Panning); + else if (CAA->ChipType == 0x01) + ym2413_set_panning(p->ym2413[CurCSet], p->ChipOpts[CurCSet].YM2413.Panning); + } // end for CurChip + + } // end for CurCSet + break; + } + + return; +} + +INLINE INT32 SampleVGM2Pbk_I(VGM_PLAYER* p, INT32 SampleVal) +{ + return (INT32)((INT64)SampleVal * p->VGMSmplRateMul / p->VGMSmplRateDiv); +} + +INLINE INT32 SamplePbk2VGM_I(VGM_PLAYER* p, INT32 SampleVal) +{ + return (INT32)((INT64)SampleVal * p->VGMSmplRateDiv / p->VGMSmplRateMul); +} + +INT32 SampleVGM2Playback(void* _p, INT32 SampleVal) +{ + VGM_PLAYER* p = (VGM_PLAYER *)_p; + return (INT32)((INT64)SampleVal * p->VGMSmplRateMul / p->VGMSmplRateDiv); +} + +INT32 SamplePlayback2VGM(void* _p, INT32 SampleVal) +{ + VGM_PLAYER* p = (VGM_PLAYER *)_p; + return (INT32)((INT64)SampleVal * p->VGMSmplRateDiv / p->VGMSmplRateMul); +} + + +static void InterpretFile(VGM_PLAYER* p, UINT32 SampleCount) +{ + UINT32 TempLng; + UINT8 CurChip; + + if (p->DacCtrlUsed && SampleCount > 1) // handle skipping + { + for (CurChip = 0x00; CurChip < p->DacCtrlUsed; CurChip ++) + { + daccontrol_update(p->daccontrol[p->DacCtrlUsg[CurChip]], SampleCount - 1); + } + } + + if (! p->FileMode) + InterpretVGM(p, SampleCount); +#ifdef ADDITIONAL_FORMATS + else + InterpretOther(p, SampleCount); +#endif + + if (p->DacCtrlUsed && SampleCount) + { + // calling this here makes "Emulating while Paused" nicer + for (CurChip = 0x00; CurChip < p->DacCtrlUsed; CurChip ++) + { + daccontrol_update(p->daccontrol[p->DacCtrlUsg[CurChip]], 1); + } + } + + p->VGMSmplPlayed += SampleCount; + p->PlayingTime += SampleCount; + + //if (FadePlay && ! FadeTime) + // EndPlay = true; + + return; +} + +static void AddPCMData(VGM_PLAYER* p, UINT8 Type, UINT32 DataSize, const UINT8* Data) +{ + UINT32 CurBnk; + VGM_PCM_BANK* TempPCM; + VGM_PCM_DATA* TempBnk; + UINT32 BankSize; + bool RetVal; + UINT8 BnkType; + UINT8 CurDAC; + + BnkType = Type & 0x3F; + if (BnkType >= PCM_BANK_COUNT || p->VGMCurLoop) + return; + + if (Type == 0x7F) + { + ReadPCMTable(p, DataSize, Data); + return; + } + + TempPCM = &p->PCMBank[BnkType]; + TempPCM->BnkPos ++; + if (TempPCM->BnkPos <= TempPCM->BankCount) + return; // Speed hack for restarting playback (skip already loaded blocks) + CurBnk = TempPCM->BankCount; + TempPCM->BankCount ++; + if (p->Last95Max != 0xFFFF) + p->Last95Max = TempPCM->BankCount; + TempPCM->Bank = (VGM_PCM_DATA*)realloc(TempPCM->Bank, + sizeof(VGM_PCM_DATA) * TempPCM->BankCount); + + if (! (Type & 0x40)) + BankSize = DataSize; + else + BankSize = ReadLE32(&Data[0x01]); + TempPCM->Data = realloc(TempPCM->Data, TempPCM->DataSize + BankSize); + TempBnk = &TempPCM->Bank[CurBnk]; + TempBnk->DataStart = TempPCM->DataSize; + if (! (Type & 0x40)) + { + TempBnk->DataSize = DataSize; + TempBnk->Data = TempPCM->Data + TempBnk->DataStart; + memcpy(TempBnk->Data, Data, DataSize); + } + else + { + TempBnk->Data = TempPCM->Data + TempBnk->DataStart; + RetVal = DecompressDataBlk(p, TempBnk, DataSize, Data); + if (! RetVal) + { + TempBnk->Data = NULL; + TempBnk->DataSize = 0x00; + //return; + goto RefreshDACStrm; // sorry for the goto, but I don't want to copy-paste the code + } + } + if (BankSize != TempBnk->DataSize) + printf("Error reading Data Block! Data Size conflict!\n"); + TempPCM->DataSize += BankSize; + + // realloc may've moved the Bank block, so refresh all DAC Streams +RefreshDACStrm: + for (CurDAC = 0x00; CurDAC < p->DacCtrlUsed; CurDAC ++) + { + if (p->DacCtrl[p->DacCtrlUsg[CurDAC]].Bank == BnkType) + daccontrol_refresh_data(p->daccontrol[p->DacCtrlUsg[CurDAC]], TempPCM->Data, TempPCM->DataSize); + } + + return; +} + +/*INLINE FUINT16 ReadBits(UINT8* Data, UINT32* Pos, FUINT8* BitPos, FUINT8 BitsToRead) +{ + FUINT8 BitReadVal; + UINT32 InPos; + FUINT8 InVal; + FUINT8 BitMask; + FUINT8 InShift; + FUINT8 OutBit; + FUINT16 RetVal; + + InPos = *Pos; + InShift = *BitPos; + OutBit = 0x00; + RetVal = 0x0000; + while(BitsToRead) + { + BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; + BitsToRead -= BitReadVal; + BitMask = (1 << BitReadVal) - 1; + + InShift += BitReadVal; + InVal = (Data[InPos] << InShift >> 8) & BitMask; + if (InShift >= 8) + { + InShift -= 8; + InPos ++; + if (InShift) + InVal |= (Data[InPos] << InShift >> 8) & BitMask; + } + + RetVal |= InVal << OutBit; + OutBit += BitReadVal; + } + + *Pos = InPos; + *BitPos = InShift; + return RetVal; +} + +static void DecompressDataBlk(VGM_PCM_DATA* Bank, UINT32 DataSize, const UINT8* Data) +{ + UINT8 ComprType; + UINT8 BitDec; + FUINT8 BitCmp; + UINT8 CmpSubType; + UINT16 AddVal; + UINT32 InPos; + UINT32 OutPos; + FUINT16 InVal; + FUINT16 OutVal; + FUINT8 ValSize; + FUINT8 InShift; + FUINT8 OutShift; + UINT8* Ent1B; + UINT16* Ent2B; + //UINT32 Time; + + //Time = GetTickCount(); + ComprType = Data[0x00]; + Bank->DataSize = ReadLE32(&Data[0x01]); + BitDec = Data[0x05]; + BitCmp = Data[0x06]; + CmpSubType = Data[0x07]; + AddVal = ReadLE16(&Data[0x08]); + + switch(ComprType) + { + case 0x00: // n-Bit compression + if (CmpSubType == 0x02) + { + Ent1B = (UINT8*)PCMTbl.Entries; + Ent2B = (UINT16*)PCMTbl.Entries; + if (! PCMTbl.EntryCount) + { + printf("Error loading table-compressed data block! No table loaded!\n"); + return; + } + else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp) + { + printf("Warning! Data block and loaded value table incompatible!\n"); + return; + } + } + + ValSize = (BitDec + 7) / 8; + InPos = 0x0A; + InShift = 0; + OutShift = BitDec - BitCmp; + + for (OutPos = 0x00; OutPos < Bank->DataSize; OutPos += ValSize) + { + if (InPos >= DataSize) + break; + InVal = ReadBits(Data, &InPos, &InShift, BitCmp); + switch(CmpSubType) + { + case 0x00: // Copy + OutVal = InVal + AddVal; + break; + case 0x01: // Shift Left + OutVal = (InVal << OutShift) + AddVal; + break; + case 0x02: // Table + switch(ValSize) + { + case 0x01: + OutVal = Ent1B[InVal]; + break; + case 0x02: + OutVal = Ent2B[InVal]; + break; + } + break; + } + memcpy(&Bank->Data[OutPos], &OutVal, ValSize); + } + break; + } + + //Time = GetTickCount() - Time; + //printf("Decompression Time: %lu\n", Time); + + return; +}*/ + +static bool DecompressDataBlk(VGM_PLAYER* p, VGM_PCM_DATA* Bank, UINT32 DataSize, const UINT8* Data) +{ + UINT8 ComprType; + UINT8 BitDec; + FUINT8 BitCmp; + UINT8 CmpSubType; + UINT16 AddVal; + const UINT8* InPos; + const UINT8* InDataEnd; + UINT8* OutPos; + const UINT8* OutDataEnd; + FUINT16 InVal; + FUINT16 OutVal; + FUINT8 ValSize; + FUINT8 InShift; + FUINT8 OutShift; + UINT8* Ent1B; + UINT16* Ent2B; + + // ReadBits Variables + FUINT8 BitsToRead; + FUINT8 BitReadVal; + FUINT8 InValB; + FUINT8 BitMask; + FUINT8 OutBit; + + // Variables for DPCM + UINT16 OutMask; + + ComprType = Data[0x00]; + Bank->DataSize = ReadLE32(&Data[0x01]); + + switch(ComprType) + { + case 0x00: // n-Bit compression + BitDec = Data[0x05]; + BitCmp = Data[0x06]; + CmpSubType = Data[0x07]; + AddVal = ReadLE16(&Data[0x08]); + + if (CmpSubType == 0x02) + { + Ent1B = (UINT8*)p->PCMTbl.Entries; // Big Endian note: Those are stored in LE and converted when reading. + Ent2B = (UINT16*)p->PCMTbl.Entries; + if (! p->PCMTbl.EntryCount) + { + Bank->DataSize = 0x00; + printf("Error loading table-compressed data block! No table loaded!\n"); + return false; + } + else if (BitDec != p->PCMTbl.BitDec || BitCmp != p->PCMTbl.BitCmp) + { + Bank->DataSize = 0x00; + printf("Warning! Data block and loaded value table incompatible!\n"); + return false; + } + } + + ValSize = (BitDec + 7) / 8; + InPos = Data + 0x0A; + InDataEnd = Data + DataSize; + InShift = 0; + OutShift = BitDec - BitCmp; + OutDataEnd = Bank->Data + Bank->DataSize; + + for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize) + { + //InVal = ReadBits(Data, InPos, &InShift, BitCmp); + // inlined - is 30% faster + OutBit = 0x00; + InVal = 0x0000; + BitsToRead = BitCmp; + while(BitsToRead) + { + BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; + BitsToRead -= BitReadVal; + BitMask = (1 << BitReadVal) - 1; + + InShift += BitReadVal; + InValB = (*InPos << InShift >> 8) & BitMask; + if (InShift >= 8) + { + InShift -= 8; + InPos ++; + if (InShift) + InValB |= (*InPos << InShift >> 8) & BitMask; + } + + InVal |= InValB << OutBit; + OutBit += BitReadVal; + } + + switch(CmpSubType) + { + case 0x00: // Copy + OutVal = InVal + AddVal; + break; + case 0x01: // Shift Left + OutVal = (InVal << OutShift) + AddVal; + break; + case 0x02: // Table + switch(ValSize) + { + case 0x01: + OutVal = Ent1B[InVal]; + break; + case 0x02: +#ifndef VGM_BIG_ENDIAN + OutVal = Ent2B[InVal]; +#else + OutVal = ReadLE16((UINT8*)&Ent2B[InVal]); +#endif + break; + } + break; + } + +#ifndef VGM_BIG_ENDIAN + //memcpy(OutPos, &OutVal, ValSize); + if (ValSize == 0x01) + *((UINT8*)OutPos) = (UINT8)OutVal; + else //if (ValSize == 0x02) + *((UINT16*)OutPos) = (UINT16)OutVal; +#else + if (ValSize == 0x01) + { + *OutPos = (UINT8)OutVal; + } + else //if (ValSize == 0x02) + { + OutPos[0x00] = (UINT8)((OutVal & 0x00FF) >> 0); + OutPos[0x01] = (UINT8)((OutVal & 0xFF00) >> 8); + } +#endif + } + break; + case 0x01: // Delta-PCM + BitDec = Data[0x05]; + BitCmp = Data[0x06]; + OutVal = ReadLE16(&Data[0x08]); + + Ent1B = (UINT8*)p->PCMTbl.Entries; + Ent2B = (UINT16*)p->PCMTbl.Entries; + if (! p->PCMTbl.EntryCount) + { + Bank->DataSize = 0x00; + printf("Error loading table-compressed data block! No table loaded!\n"); + return false; + } + else if (BitDec != p->PCMTbl.BitDec || BitCmp != p->PCMTbl.BitCmp) + { + Bank->DataSize = 0x00; + printf("Warning! Data block and loaded value table incompatible!\n"); + return false; + } + + ValSize = (BitDec + 7) / 8; + OutMask = (1 << BitDec) - 1; + InPos = Data + 0x0A; + InDataEnd = Data + DataSize; + InShift = 0; + OutShift = BitDec - BitCmp; + OutDataEnd = Bank->Data + Bank->DataSize; + AddVal = 0x0000; + + for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize) + { + //InVal = ReadBits(Data, InPos, &InShift, BitCmp); + // inlined - is 30% faster + OutBit = 0x00; + InVal = 0x0000; + BitsToRead = BitCmp; + while(BitsToRead) + { + BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead; + BitsToRead -= BitReadVal; + BitMask = (1 << BitReadVal) - 1; + + InShift += BitReadVal; + InValB = (*InPos << InShift >> 8) & BitMask; + if (InShift >= 8) + { + InShift -= 8; + InPos ++; + if (InShift) + InValB |= (*InPos << InShift >> 8) & BitMask; + } + + InVal |= InValB << OutBit; + OutBit += BitReadVal; + } + + switch(ValSize) + { + case 0x01: + AddVal = Ent1B[InVal]; + OutVal += AddVal; + OutVal &= OutMask; + *((UINT8*)OutPos) = (UINT8)OutVal; + break; + case 0x02: +#ifndef VGM_BIG_ENDIAN + AddVal = Ent2B[InVal]; +#else + AddVal = ReadLE16((UINT8*)&Ent2B[InVal]); +#endif + OutVal += AddVal; + OutVal &= OutMask; +#ifndef VGM_BIG_ENDIAN + *((UINT16*)OutPos) = (UINT16)OutVal; +#else + OutPos[0x00] = (UINT8)((OutVal & 0x00FF) >> 0); + OutPos[0x01] = (UINT8)((OutVal & 0xFF00) >> 8); +#endif + break; + } + } + break; + default: + printf("Error: Unknown data block compression!\n"); + return false; + } + + return true; +} + +static UINT8 GetDACFromPCMBank(VGM_PLAYER* p) +{ + // for YM2612 DAC data only + /*VGM_PCM_BANK* TempPCM; + UINT32 CurBnk;*/ + UINT32 DataPos; + + /*TempPCM = &PCMBank[0x00]; + DataPos = TempPCM->DataPos; + for (CurBnk = 0x00; CurBnk < TempPCM->BankCount; CurBnk ++) + { + if (DataPos < TempPCM->Bank[CurBnk].DataSize) + { + if (TempPCM->DataPos < TempPCM->DataSize) + TempPCM->DataPos ++; + return TempPCM->Bank[CurBnk].Data[DataPos]; + } + DataPos -= TempPCM->Bank[CurBnk].DataSize; + } + return 0x80;*/ + + DataPos = p->PCMBank[0x00].DataPos; + if (DataPos >= p->PCMBank[0x00].DataSize) + return 0x80; + + p->PCMBank[0x00].DataPos ++; + return p->PCMBank[0x00].Data[DataPos]; +} + +static UINT8* GetPointerFromPCMBank(VGM_PLAYER* p, UINT8 Type, UINT32 DataPos) +{ + if (Type >= PCM_BANK_COUNT) + return NULL; + + if (DataPos >= p->PCMBank[Type].DataSize) + return NULL; + + return &p->PCMBank[Type].Data[DataPos]; +} + +static void ReadPCMTable(VGM_PLAYER* p, UINT32 DataSize, const UINT8* Data) +{ + UINT8 ValSize; + UINT32 TblSize; + + p->PCMTbl.ComprType = Data[0x00]; + p->PCMTbl.CmpSubType = Data[0x01]; + p->PCMTbl.BitDec = Data[0x02]; + p->PCMTbl.BitCmp = Data[0x03]; + p->PCMTbl.EntryCount = ReadLE16(&Data[0x04]); + + ValSize = (p->PCMTbl.BitDec + 7) / 8; + TblSize = p->PCMTbl.EntryCount * ValSize; + + p->PCMTbl.Entries = realloc(p->PCMTbl.Entries, TblSize); + memcpy(p->PCMTbl.Entries, &Data[0x06], TblSize); + + if (DataSize < 0x06 + TblSize) + printf("Warning! Bad PCM Table Length!\n"); + + return; +} + +#define CHIP_CHECK(name) (p->ChipAudio[CurChip].name.ChipType != 0xFF) +static void InterpretVGM(VGM_PLAYER* p, UINT32 SampleCount) +{ + INT32 SmplPlayed; + UINT8 Command; + UINT8 TempByt; + UINT16 TempSht; + UINT32 TempLng; + VGM_PCM_BANK* TempPCM; + VGM_PCM_DATA* TempBnk; + UINT32 ROMSize; + UINT32 DataStart; + UINT32 DataLen; + const UINT8* ROMData; + UINT8 CurChip; + const UINT8* VGMPnt; + + if (p->VGMEnd) + return; + + SmplPlayed = SamplePbk2VGM_I(p, p->VGMSmplPlayed + SampleCount); + while(p->VGMSmplPos <= SmplPlayed) + { + Command = p->VGMData[p->VGMPos + 0x00]; + if (Command >= 0x70 && Command <= 0x8F) + { + switch(Command & 0xF0) + { + case 0x70: + p->VGMSmplPos += (Command & 0x0F) + 0x01; + break; + case 0x80: + TempByt = GetDACFromPCMBank(p); + if (p->VGMHead.lngHzYM2612) + { + chip_reg_write(p, 0x02, 0x00, 0x00, 0x2A, TempByt); + } + p->VGMSmplPos += (Command & 0x0F); + break; + } + p->VGMPos += 0x01; + } + else + { + VGMPnt = &p->VGMData[p->VGMPos]; + + // Cheat Mode (to use 2 instances of 1 chip) + CurChip = 0x00; + switch(Command) + { + case 0x30: + if (p->VGMHead.lngHzPSG & 0x40000000) + { + Command += 0x20; + CurChip = 0x01; + } + break; + case 0x3F: + if (p->VGMHead.lngHzPSG & 0x40000000) + { + Command += 0x10; + CurChip = 0x01; + } + break; + case 0xA1: + if (p->VGMHead.lngHzYM2413 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xA2: + case 0xA3: + if (p->VGMHead.lngHzYM2612 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xA4: + if (p->VGMHead.lngHzYM2151 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xA5: + if (p->VGMHead.lngHzYM2203 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xA6: + case 0xA7: + if (p->VGMHead.lngHzYM2608 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xA8: + case 0xA9: + if (p->VGMHead.lngHzYM2610 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xAA: + if (p->VGMHead.lngHzYM3812 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xAB: + if (p->VGMHead.lngHzYM3526 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xAC: + if (p->VGMHead.lngHzY8950 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xAE: + case 0xAF: + if (p->VGMHead.lngHzYMF262 & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + case 0xAD: + if (p->VGMHead.lngHzYMZ280B & 0x40000000) + { + Command -= 0x50; + CurChip = 0x01; + } + break; + } + + switch(Command) + { + case 0x66: // End Of File + if (p->VGMHead.lngLoopOffset) + { + p->VGMPos = p->VGMHead.lngLoopOffset; + p->VGMSmplPos -= p->VGMHead.lngLoopSamples; + p->VGMSmplPlayed -= SampleVGM2Pbk_I(p, p->VGMHead.lngLoopSamples); + SmplPlayed = SamplePbk2VGM_I(p, p->VGMSmplPlayed + SampleCount); + p->VGMCurLoop ++; + + if (p->VGMMaxLoopM && p->VGMCurLoop >= p->VGMMaxLoopM) + { + if (! p->FadePlay) + { + p->FadeStart = SampleVGM2Pbk_I(p, p->VGMHead.lngTotalSamples + + (p->VGMCurLoop - 1) * p->VGMHead.lngLoopSamples); + } + p->FadePlay = true; + } + if (p->FadePlay && ! p->FadeTime) + p->VGMEnd = true; + } + else + { + if (p->VGMHead.lngTotalSamples != (UINT32)p->VGMSmplPos) + { +#ifdef CONSOLE_MODE + printf("Warning! Header Samples: %u\t Counted Samples: %u\n", + p->VGMHead.lngTotalSamples, p->VGMSmplPos); + p->ErrorHappened = true; +#endif + p->VGMHead.lngTotalSamples = p->VGMSmplPos; + } + p->VGMEnd = true; + break; + } + break; + case 0x62: // 1/60s delay + p->VGMSmplPos += 735; + p->VGMPos += 0x01; + break; + case 0x63: // 1/50s delay + p->VGMSmplPos += 882; + p->VGMPos += 0x01; + break; + case 0x61: // xx Sample Delay + TempSht = ReadLE16(&VGMPnt[0x01]); + p->VGMSmplPos += TempSht; + p->VGMPos += 0x03; + break; + case 0x50: // SN76496 write + if (CHIP_CHECK(SN76496)) + { + chip_reg_write(p, 0x00, CurChip, 0x00, 0x00, VGMPnt[0x01]); + } + p->VGMPos += 0x02; + break; + case 0x51: // YM2413 write + if (CHIP_CHECK(YM2413)) + { + chip_reg_write(p, 0x01, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x52: // YM2612 write port 0 + case 0x53: // YM2612 write port 1 + if (CHIP_CHECK(YM2612)) + { + chip_reg_write(p, 0x02, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x67: // PCM Data Stream + TempByt = VGMPnt[0x02]; + TempLng = ReadLE32(&VGMPnt[0x03]); + if (TempLng & 0x80000000) + { + TempLng &= 0x7FFFFFFF; + CurChip = 0x01; + } + + switch(TempByt & 0xC0) + { + case 0x00: // Database Block + case 0x40: + AddPCMData(p, TempByt, TempLng, &VGMPnt[0x07]); + /*switch(TempByt) + { + case 0x00: // YM2612 PCM Database + break; + case 0x01: // RF5C68 PCM Database + break; + case 0x02: // RF5C164 PCM Database + break; + }*/ + break; + case 0x80: // ROM/RAM Dump + if (p->VGMCurLoop) + break; + + ROMSize = ReadLE32(&VGMPnt[0x07]); + DataStart = ReadLE32(&VGMPnt[0x0B]); + DataLen = TempLng - 0x08; + ROMData = &VGMPnt[0x0F]; + switch(TempByt) + { + case 0x80: // SegaPCM ROM + if (! CHIP_CHECK(SegaPCM)) + break; + sega_pcm_write_rom(p->segapcm[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x81: // YM2608 DELTA-T ROM Image + if (! CHIP_CHECK(YM2608)) + break; + ym2608_write_data_pcmrom(p->ym2608[CurChip], 0x02, ROMSize, DataStart, DataLen, + ROMData); + break; + case 0x82: // YM2610 ADPCM ROM Image + case 0x83: // YM2610 DELTA-T ROM Image + if (! CHIP_CHECK(YM2610)) + break; + TempByt = 0x01 + (TempByt - 0x82); + ym2610_write_data_pcmrom(p->ym2610[CurChip], TempByt, ROMSize, DataStart, DataLen, + ROMData); + break; + case 0x84: // YMF278B ROM Image + if (! CHIP_CHECK(YMF278B)) + break; + ymf278b_write_rom(p->ymf278b[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x85: // YMF271 ROM Image + if (! CHIP_CHECK(YMF271)) + break; + ymf271_write_rom(p->ymf271[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x86: // YMZ280B ROM Image + if (! CHIP_CHECK(YMZ280B)) + break; + ymz280b_write_rom(p->ymz280b[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x87: // YMF278B RAM Image + if (! CHIP_CHECK(YMF278B)) + break; + //ymf278b_write_ram(CurChip, ROMSize, DataStart, DataLen, ROMData); + break; + case 0x88: // Y8950 DELTA-T ROM Image + if (! CHIP_CHECK(Y8950) || p->PlayingMode == 0x01) + break; + y8950_write_data_pcmrom(p->y8950[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x89: // MultiPCM ROM Image + if (! CHIP_CHECK(MultiPCM)) + break; + multipcm_write_rom(p->multipcm[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x8A: // UPD7759 ROM Image + if (! CHIP_CHECK(UPD7759)) + break; + upd7759_write_rom(p->upd7759[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x8B: // OKIM6295 ROM Image + if (! CHIP_CHECK(OKIM6295)) + break; + okim6295_write_rom(p->okim6295[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x8C: // K054539 ROM Image + if (! CHIP_CHECK(K054539)) + break; + k054539_write_rom(p->k054539[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x8D: // C140 ROM Image + if (! CHIP_CHECK(C140)) + break; + c140_write_rom(p->c140[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x8E: // K053260 ROM Image + if (! CHIP_CHECK(K053260)) + break; + k053260_write_rom(p->k053260[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x8F: // QSound ROM Image + if (! CHIP_CHECK(QSound)) + break; + qsound_write_rom(p->qsound[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x90: // ES5506 ROM Image + if (! CHIP_CHECK(ES5506)) + break; + es5506_write_rom(p->es550x[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x91: // X1-010 ROM Image + if (! CHIP_CHECK(X1_010)) + break; + x1_010_write_rom(p->x1_010[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x92: // C352 ROM Image + if (! CHIP_CHECK(C352)) + break; + c352_write_rom(p->c352[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + case 0x93: // GA20 ROM Image + if (! CHIP_CHECK(GA20)) + break; + iremga20_write_rom(p->ga20[CurChip], ROMSize, DataStart, DataLen, ROMData); + break; + // case 0x8C: // OKIM6376 ROM Image + // if (! CHIP_CHECK(OKIM6376)) + // break; + // break; + } + break; + case 0xC0: // RAM Write + if (! (TempByt & 0x20)) + { + DataStart = ReadLE16(&VGMPnt[0x07]); + DataLen = TempLng - 0x02; + ROMData = &VGMPnt[0x09]; + } + else + { + DataStart = ReadLE32(&VGMPnt[0x07]); + DataLen = TempLng - 0x04; + ROMData = &VGMPnt[0x0B]; + } + switch(TempByt) + { + case 0xC0: // RF5C68 RAM Database + if (! CHIP_CHECK(RF5C68)) + break; + rf5c68_write_ram(p->rf5c68, DataStart, DataLen, ROMData); + break; + case 0xC1: // RF5C164 RAM Database + if (! CHIP_CHECK(RF5C164)) + break; + rf5c164_write_ram(p->rf5c164, DataStart, DataLen, ROMData); + break; + case 0xC2: // NES APU RAM + if (! CHIP_CHECK(NES)) + break; + nes_write_ram(p->nesapu[CurChip], DataStart, DataLen, ROMData); + break; + case 0xE0: // SCSP RAM + if (! CHIP_CHECK(SCSP)) + break; + scsp_write_ram(p->scsp[CurChip], DataStart, DataLen, ROMData); + break; + case 0xE1: // ES5503 RAM + if (! CHIP_CHECK(ES5503)) + break; + es5503_write_ram(p->es5503[CurChip], DataStart, DataLen, ROMData); + break; + } + break; + } + p->VGMPos += 0x07 + TempLng; + break; + case 0xE0: // Seek to PCM Data Bank Pos + p->PCMBank[0x00].DataPos = ReadLE32(&VGMPnt[0x01]); + p->VGMPos += 0x05; + break; + case 0x4F: // GG Stereo + if (CHIP_CHECK(SN76496)) + { + chip_reg_write(p, 0x00, CurChip, 0x01, 0x00, VGMPnt[0x01]); + } + p->VGMPos += 0x02; + break; + case 0x54: // YM2151 write + if (CHIP_CHECK(YM2151)) + { + chip_reg_write(p, 0x03, CurChip, 0x01, VGMPnt[0x01], VGMPnt[0x02]); + } + //p->VGMSmplPos += 80; + p->VGMPos += 0x03; + break; + case 0xC0: // Sega PCM memory write + TempSht = ReadLE16(&VGMPnt[0x01]); + CurChip = (TempSht & 0x8000) >> 15; + if (CHIP_CHECK(SegaPCM)) + { + sega_pcm_w(p->segapcm[CurChip], TempSht & 0x7FFF, VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xB0: // RF5C68 register write + if (CHIP_CHECK(RF5C68)) + { + chip_reg_write(p, 0x05, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xC1: // RF5C68 memory write + if (CHIP_CHECK(RF5C68)) + { + TempSht = ReadLE16(&VGMPnt[0x01]); + rf5c68_mem_w(p->rf5c68, TempSht, VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0x55: // YM2203 + if (CHIP_CHECK(YM2203)) + { + chip_reg_write(p, 0x06, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x56: // YM2608 write port 0 + case 0x57: // YM2608 write port 1 + if (CHIP_CHECK(YM2608)) + { + chip_reg_write(p, 0x07, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x58: // YM2610 write port 0 + case 0x59: // YM2610 write port 1 + if (CHIP_CHECK(YM2610)) + { + chip_reg_write(p, 0x08, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x5A: // YM3812 write + if (CHIP_CHECK(YM3812)) + { + chip_reg_write(p, 0x09, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x5B: // YM3526 write + if (CHIP_CHECK(YM3526)) + { + chip_reg_write(p, 0x0A, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x5C: // Y8950 write + if (CHIP_CHECK(Y8950)) + { + chip_reg_write(p, 0x0B, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x5E: // YMF262 write port 0 + case 0x5F: // YMF262 write port 1 + if (CHIP_CHECK(YMF262)) + { + chip_reg_write(p, 0x0C, CurChip, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x5D: // YMZ280B write + if (CHIP_CHECK(YMZ280B)) + { + chip_reg_write(p, 0x0F, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xD0: // YMF278B write + if (CHIP_CHECK(YMF278B)) + { + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + chip_reg_write(p, 0x0D, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xD1: // YMF271 write + if (CHIP_CHECK(YMF271)) + { + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + chip_reg_write(p, 0x0E, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xB1: // RF5C164 register write + if (CHIP_CHECK(RF5C164)) + { + chip_reg_write(p, 0x10, CurChip, 0x00, VGMPnt[0x01], VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xC2: // RF5C164 memory write + if (CHIP_CHECK(RF5C164)) + { + TempSht = ReadLE16(&VGMPnt[0x01]); + rf5c164_mem_w(p->rf5c164, TempSht, VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xB2: // PWM channel write + if (CHIP_CHECK(PWM)) + { + chip_reg_write(p, 0x11, CurChip, (VGMPnt[0x01] & 0xF0) >> 4, + VGMPnt[0x01] & 0x0F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x68: // PCM RAM write + CurChip = (VGMPnt[0x02] & 0x80) >> 7; + TempByt = VGMPnt[0x02] & 0x7F; + + DataStart = ReadLE24(&VGMPnt[0x03]); + TempLng = ReadLE24(&VGMPnt[0x06]); + DataLen = ReadLE24(&VGMPnt[0x09]); + if (! DataLen) + DataLen += 0x01000000; + ROMData = GetPointerFromPCMBank(p, TempByt, DataStart); + if (ROMData == NULL) + { + p->VGMPos += 0x0C; + break; + } + + switch(TempByt) + { + case 0x01: + if (! CHIP_CHECK(RF5C68)) + break; + rf5c68_write_ram(p->rf5c68, TempLng, DataLen, ROMData); + break; + case 0x02: + if (! CHIP_CHECK(RF5C164)) + break; + rf5c164_write_ram(p->rf5c164, TempLng, DataLen, ROMData); + break; + case 0x06: + if (! CHIP_CHECK(SCSP)) + break; + scsp_write_ram(p->scsp[CurChip], TempLng, DataLen, ROMData); + break; + case 0x07: + if (! CHIP_CHECK(NES)) + break; + p->Last95Drum = DataStart / DataLen - 1; + p->Last95Max = p->PCMBank[TempByt].DataSize / DataLen; + nes_write_ram(p->nesapu[CurChip], TempLng, DataLen, ROMData); + break; + } + p->VGMPos += 0x0C; + break; + case 0xA0: // AY8910 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(AY8910)) + { + chip_reg_write(p, 0x12, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xB3: // GameBoy DMG write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(GameBoy)) + { + chip_reg_write(p, 0x13, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xB4: // NES APU write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(NES)) + { + chip_reg_write(p, 0x14, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xB5: // MultiPCM write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(MultiPCM)) + { + chip_reg_write(p, 0x15, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xC3: // MultiPCM memory write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(MultiPCM)) + { + TempSht = ReadLE16(&VGMPnt[0x02]); + multipcm_bank_write(p->multipcm[CurChip], VGMPnt[0x01] & 0x7F, TempSht); + } + p->VGMPos += 0x04; + break; + case 0xB6: // UPD7759 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(UPD7759)) + { + chip_reg_write(p, 0x16, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xB7: // OKIM6258 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(OKIM6258)) + { + chip_reg_write(p, 0x17, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xB8: // OKIM6295 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(OKIM6295)) + { + chip_reg_write(p, 0x18, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xD2: // SCC1 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(K051649)) + { + chip_reg_write(p, 0x19, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xD3: // K054539 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(K054539)) + { + chip_reg_write(p, 0x1A, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xB9: // HuC6280 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(HuC6280)) + { + chip_reg_write(p, 0x1B, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xD4: // C140 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(C140)) + { + chip_reg_write(p, 0x1C, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xBA: // K053260 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(K053260)) + { + chip_reg_write(p, 0x1D, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xBB: // Pokey write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(Pokey)) + { + chip_reg_write(p, 0x1E, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xC4: // QSound write + if (CHIP_CHECK(QSound)) + { + chip_reg_write(p, 0x1F, CurChip, VGMPnt[0x01], VGMPnt[0x02], VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xC5: // YMF292/SCSP write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(SCSP)) + { + chip_reg_write(p, 0x20, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xBC: // WonderSwan write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(WSwan)) + { + chip_reg_write(p, 0x21, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xC6: // WonderSwan memory write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(WSwan)) + { + TempSht = ReadBE16(&VGMPnt[0x01]) & 0x7FFF; + ws_write_ram(p->wswan[CurChip], TempSht, VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xC7: // VSU write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(VSU)) + { + chip_reg_write(p, 0x22, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xBD: // SAA1099 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(SAA1099)) + { + chip_reg_write(p, 0x23, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xD5: // ES5503 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(ES5503)) + { + chip_reg_write(p, 0x24, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xBE: // ES5506 write (8-bit data) + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(ES5506)) + { + chip_reg_write(p, 0x25, CurChip, VGMPnt[0x01] & 0x7F, 0x00, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0xD6: // ES5506 write (16-bit data) + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(ES5506)) + { + chip_reg_write(p, 0x25, CurChip, 0x80 | (VGMPnt[0x01] & 0x7F), + VGMPnt[0x02], VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; + case 0xC8: // X1-010 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(X1_010)) + { + chip_reg_write(p, 0x26, CurChip, VGMPnt[0x01] & 0x7F, VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; +#if 0 // for ctr's WIP rips + case 0xC9: // C352 write + CurChip = 0x00; + if (CHIP_CHECK(C352)) + { + if (VGMPnt[0x01] == 0x03 && VGMPnt[0x02] == 0xFF && VGMPnt[0x03] == 0xFF) + c352_w(p->c352[CurChip], 0x202, 0x0020); + else + chip_reg_write(p, 0x27, CurChip, VGMPnt[0x01], VGMPnt[0x02], + VGMPnt[0x03]); + } + p->VGMPos += 0x04; + break; +#endif + case 0xE1: // C352 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(C352)) + { + TempSht = ((VGMPnt[0x01] & 0x7F) << 8) | (VGMPnt[0x02] << 0); + c352_w(p->c352[CurChip], TempSht, (VGMPnt[0x03] << 8) | VGMPnt[0x04]); + } + p->VGMPos += 0x05; + break; + case 0xBF: // GA20 write + CurChip = (VGMPnt[0x01] & 0x80) >> 7; + if (CHIP_CHECK(GA20)) + { + chip_reg_write(p, 0x28, CurChip, 0x00, VGMPnt[0x01] & 0x7F, VGMPnt[0x02]); + } + p->VGMPos += 0x03; + break; + case 0x90: // DAC Ctrl: Setup Chip + CurChip = VGMPnt[0x01]; + if (CurChip == 0xFF) + { + p->VGMPos += 0x05; + break; + } + if (! p->DacCtrl[CurChip].Enable) + { + device_start_daccontrol(&p->daccontrol[CurChip], p, p->SampleRate); + device_reset_daccontrol(p->daccontrol[CurChip]); + p->DacCtrl[CurChip].Enable = true; + p->DacCtrlUsg[p->DacCtrlUsed] = CurChip; + p->DacCtrlUsed ++; + } + TempByt = VGMPnt[0x02]; // Chip Type + TempSht = ReadBE16(&VGMPnt[0x03]); + daccontrol_setup_chip(p->daccontrol[CurChip], TempByt & 0x7F, (TempByt & 0x80) >> 7, TempSht); + p->VGMPos += 0x05; + break; + case 0x91: // DAC Ctrl: Set Data + CurChip = VGMPnt[0x01]; + if (CurChip == 0xFF || ! p->DacCtrl[CurChip].Enable) + { + p->VGMPos += 0x05; + break; + } + p->DacCtrl[CurChip].Bank = VGMPnt[0x02]; + if (p->DacCtrl[CurChip].Bank >= PCM_BANK_COUNT) + p->DacCtrl[CurChip].Bank = 0x00; + + TempPCM = &p->PCMBank[p->DacCtrl[CurChip].Bank]; + p->Last95Max = TempPCM->BankCount; + daccontrol_set_data(p->daccontrol[CurChip], TempPCM->Data, TempPCM->DataSize, + VGMPnt[0x03], VGMPnt[0x04]); + p->VGMPos += 0x05; + break; + case 0x92: // DAC Ctrl: Set Freq + CurChip = VGMPnt[0x01]; + if (CurChip == 0xFF || ! p->DacCtrl[CurChip].Enable) + { + p->VGMPos += 0x06; + break; + } + TempLng = ReadLE32(&VGMPnt[0x02]); + p->Last95Freq = TempLng; + daccontrol_set_frequency(p->daccontrol[CurChip], TempLng); + p->VGMPos += 0x06; + break; + case 0x93: // DAC Ctrl: Play from Start Pos + CurChip = VGMPnt[0x01]; + if (CurChip == 0xFF || ! p->DacCtrl[CurChip].Enable || + ! p->PCMBank[p->DacCtrl[CurChip].Bank].BankCount) + { + p->VGMPos += 0x0B; + break; + } + DataStart = ReadLE32(&VGMPnt[0x02]); + p->Last95Drum = 0xFFFF; + TempByt = VGMPnt[0x06]; + DataLen = ReadLE32(&VGMPnt[0x07]); + daccontrol_start(p->daccontrol[CurChip], DataStart, TempByt, DataLen); + p->VGMPos += 0x0B; + break; + case 0x94: // DAC Ctrl: Stop immediately + CurChip = VGMPnt[0x01]; + if (! p->DacCtrl[CurChip].Enable) + { + p->VGMPos += 0x02; + break; + } + p->Last95Drum = 0xFFFF; + if (CurChip < 0xFF) + { + daccontrol_stop(p->daccontrol[CurChip]); + } + else + { + for (CurChip = 0x00; CurChip < 0xFF; CurChip ++) + daccontrol_stop(p->daccontrol[CurChip]); + } + p->VGMPos += 0x02; + break; + case 0x95: // DAC Ctrl: Play Block (small) + CurChip = VGMPnt[0x01]; + if (CurChip == 0xFF || ! p->DacCtrl[CurChip].Enable || + ! p->PCMBank[p->DacCtrl[CurChip].Bank].BankCount) + { + p->VGMPos += 0x05; + break; + } + TempPCM = &p->PCMBank[p->DacCtrl[CurChip].Bank]; + TempSht = ReadLE16(&VGMPnt[0x02]); + p->Last95Drum = TempSht; + p->Last95Max = TempPCM->BankCount; + if (TempSht >= TempPCM->BankCount) + TempSht = 0x00; + TempBnk = &TempPCM->Bank[TempSht]; + + TempByt = DCTRL_LMODE_BYTES | + (VGMPnt[0x04] & 0x10) | // Reverse Mode + ((VGMPnt[0x04] & 0x01) << 7); // Looping + daccontrol_start(p->daccontrol[CurChip], TempBnk->DataStart, TempByt, TempBnk->DataSize); + p->VGMPos += 0x05; + break; + default: + + switch(Command & 0xF0) + { + case 0x00: + case 0x10: + case 0x20: + p->VGMPos += 0x01; + break; + case 0x30: + p->VGMPos += 0x02; + break; + case 0x40: + case 0x50: + case 0xA0: + case 0xB0: + p->VGMPos += 0x03; + break; + case 0xC0: + case 0xD0: + p->VGMPos += 0x04; + break; + case 0xE0: + case 0xF0: + p->VGMPos += 0x05; + break; + default: + p->VGMEnd = true; + p->EndPlay = true; + break; + } + break; + } + } + + if (p->VGMPos >= p->VGMHead.lngEOFOffset) + p->VGMEnd = true; + + if (p->VGMEnd) + break; + } + + return; +} + + +static void GeneralChipLists(VGM_PLAYER* p) +{ + // Generate Chip List for playback loop + UINT16 CurBufIdx; + CA_LIST* CLstOld; + CA_LIST* CLst; + CA_LIST* CurLst; + UINT8 CurChip; + UINT8 CurCSet; + CAUD_ATTR* CAA; + + p->ChipListAll = NULL; + //ChipListPause = NULL; + //ChipListOpt = NULL; + + // generate list of all chips that are used in the current VGM + CurBufIdx = 0x00; + CLstOld = NULL; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) + { + for (CurCSet = 0x00; CurCSet < 0x02; CurCSet ++) + { + CAA = (CAUD_ATTR*)&p->ChipAudio[CurCSet] + CurChip; + if (CAA->ChipType != 0xFF) + { + CLst = &p->ChipListBuffer[CurBufIdx]; + CurBufIdx ++; + if (CLstOld == NULL) + p->ChipListAll = CLst; + else + CLstOld->next = CLst; + + CLst->CAud = CAA; + CLst->COpts = (CHIP_OPTS*)&p->ChipOpts[CurCSet] + CurChip; + CLstOld = CLst; + } + } + } + if (CLstOld != NULL) + CLstOld->next = NULL; + + /*// Go through the chip list and copy all chips to a new list, except for a few + // selected ones. + CLstOld = NULL; + CurLst = ChipListAll; + while(CurLst != NULL) + { + // don't emulate the RF5Cxx chips when paused+emulated + if (CurLst->CAud->ChipType != 0x05 && CurLst->CAud->ChipType != 0x10) + { + CLst = &p->ChipListBuffer[CurBufIdx]; + CurBufIdx ++; + if (CLstOld == NULL) + p->ChipListPause = CLst; + else + CLstOld->next = CLst; + + *CLst = *CurLst; + CLstOld = CLst; + } + CurLst = CurLst->next; + } + if (CLstOld != NULL) + CLstOld->next = NULL;*/ + + return; +} + +static void SetupResampler(VGM_PLAYER* p, CAUD_ATTR* CAA) +{ + if (! CAA->SmpRate) + { + CAA->Resampler = 0x00; + return; + } + + CAA->TargetSmpRate = p->SampleRate; + + CAA->Resampler = resampler_create(); + + return; +} + +static void ChangeChipSampleRate(void* DataPtr, UINT32 NewSmplRate) +{ + CAUD_ATTR* CAA = (CAUD_ATTR*)DataPtr; + + if (CAA->SmpRate == NewSmplRate) + return; + + CAA->SmpRate = NewSmplRate; + + return; +} + + +INLINE INT16 Limit2Short(INT32 Value) +{ + INT32 NewValue; + + NewValue = Value; + if (NewValue < -0x8000) + NewValue = -0x8000; + else if (NewValue > 0x7FFF) + NewValue = 0x7FFF; + + return (INT16)NewValue; +} + +INLINE INT32 LimitScaleAdd(INT32 Target, INT32 Value, UINT16 Scale) +{ + INT64 NewValue; + + NewValue = (INT64)Value; + NewValue *= (INT64)Scale; + NewValue += (INT64)Target; + + if (NewValue < -0x80000000LL) + NewValue = -0x80000000LL; + else if (NewValue > 0x7FFFFFFFLL) + NewValue = 0x7FFFFFFFLL; + + return (INT32)NewValue; +} + +static void null_update(void *param, stream_sample_t **outputs, int samples) +{ + memset(outputs[0x00], 0x00, sizeof(stream_sample_t) * samples); + memset(outputs[0x01], 0x00, sizeof(stream_sample_t) * samples); + + return; +} + +static void dual_opl2_stereo(void *param, stream_sample_t **outputs, int samples) +{ + struct dual_opl2_info * info = (struct dual_opl2_info *) param; + + ym3812_stream_update(info->chip, outputs, samples); + + // Dual-OPL with Stereo + if (info->ChipID & 0x01) + memset(outputs[0x00], 0x00, sizeof(stream_sample_t) * samples); // Mute Left Chanel + else + memset(outputs[0x01], 0x00, sizeof(stream_sample_t) * samples); // Mute Right Chanel + + return; +} + +static void ResampleChipStream(VGM_PLAYER* p, CA_LIST* CLst, WAVE_32BS* RetSample, UINT32 Length) +{ + CAUD_ATTR* CAA; + INT32* CurBufL; + INT32* CurBufR; + INT32 SmpCnt; // must be signed, else I'm getting calculation errors + INT32 CurSmpl; + UINT32 SampleRate; + UINT32 OutPos; + sample_t ls, rs; + + CAA = CLst->CAud; + if (!CAA->Resampler) + return; + + CurBufL = p->StreamBufs[0x00]; + CurBufR = p->StreamBufs[0x01]; + + SampleRate = p->SampleRate; + + OutPos = 0; + + // This Do-While-Loop gets and resamples the chip output of one or more chips. + // It's a loop to support the AY8910 paired with the YM2203/YM2608/YM2610. + do + { + for (OutPos = 0; OutPos < Length; OutPos++) + { + if (CAA->LastSmpRate != CAA->SmpRate) + { + resampler_set_rate(CAA->Resampler, (double)CAA->SmpRate / (double)CAA->TargetSmpRate); + CAA->LastSmpRate = CAA->SmpRate; + } + + SmpCnt = resampler_get_min_fill(CAA->Resampler) / 2; + + if (SmpCnt) + { + CAA->StreamUpdate(CAA->StreamUpdateParam, p->StreamBufs, SmpCnt); + for (CurSmpl = 0; CurSmpl < SmpCnt; CurSmpl++) + resampler_write_pair(CAA->Resampler, CurBufL[CurSmpl], CurBufR[CurSmpl]); + } + + resampler_read_pair(CAA->Resampler, &ls, &rs); + + RetSample[OutPos].Left = LimitScaleAdd(RetSample[OutPos].Left, ls, CAA->Volume); + RetSample[OutPos].Right = LimitScaleAdd(RetSample[OutPos].Right, rs, CAA->Volume); + } + + CAA = CAA->Paired; + } while(CAA != NULL); + + return; +} + +static INT32 RecalcFadeVolume(VGM_PLAYER* p) +{ + float TempSng; + + if (p->FadePlay) + { + if (! p->FadeStart) + p->FadeStart = p->PlayingTime; + + TempSng = (p->PlayingTime - p->FadeStart) / (float)p->SampleRate; + p->MasterVol = 1.0f - TempSng / (p->FadeTime * 0.001f); + if (p->MasterVol < 0.0f) + { + p->MasterVol = 0.0f; + //EndPlay = true; + p->VGMEnd = true; + } + p->FinalVol = p->VolumeLevelM * p->MasterVol * p->MasterVol; + } + + return (INT32)(0x100 * p->FinalVol + 0.5f); +} + +UINT32 FillBuffer(void *_p, WAVE_16BS* Buffer, UINT32 BufferSize) +{ + UINT32 CurSmpl; + WAVE_32BS TempBuf; + INT32 CurMstVol; + UINT32 RecalcStep; + CA_LIST* CurCLst; + + VGM_PLAYER* p = (VGM_PLAYER *)_p; + + //memset(Buffer, 0x00, sizeof(WAVE_16BS) * BufferSize); + + RecalcStep = p->FadePlay ? p->SampleRate / 44100 : 0; + CurMstVol = RecalcFadeVolume(p); + + if (Buffer == NULL) + { + //for (CurSmpl = 0x00; CurSmpl < BufferSize; CurSmpl ++) + // InterpretFile(1); + InterpretFile(p, BufferSize); + + if (p->FadePlay && ! p->FadeStart) + { + p->FadeStart = p->PlayingTime; + RecalcStep = p->FadePlay ? p->SampleRate / 100 : 0; + } + //if (RecalcStep && ! (CurSmpl % RecalcStep)) + if (RecalcStep) + CurMstVol = RecalcFadeVolume(p); + + if (p->VGMEnd) + { + p->EndPlay = true; + } + + return BufferSize; + } + + for (CurSmpl = 0x00; CurSmpl < BufferSize; CurSmpl ++) + { + InterpretFile(p, 1); + + // Sample Structures + // 00 - SN76496 + // 01 - YM2413 + // 02 - YM2612 + // 03 - YM2151 + // 04 - SegaPCM + // 05 - RF5C68 + // 06 - YM2203 + // 07 - YM2608 + // 08 - YM2610/YM2610B + // 09 - YM3812 + // 0A - YM3526 + // 0B - Y8950 + // 0C - YMF262 + // 0D - YMF278B + // 0E - YMF271 + // 0F - YMZ280B + // 10 - RF5C164 + // 11 - PWM + // 12 - AY8910 + // 13 - GameBoy + // 14 - NES APU + // 15 - MultiPCM + // 16 - UPD7759 + // 17 - OKIM6258 + // 18 - OKIM6295 + // 19 - K051649 + // 1A - K054539 + // 1B - HuC6280 + // 1C - C140 + // 1D - K053260 + // 1E - Pokey + // 1F - QSound + // 20 - YMF292/SCSP + // 21 - WonderSwan + // 22 - VSU + // 23 - SAA1099 + // 24 - ES5503 + // 25 - ES5506 + // 26 - X1-010 + // 27 - C352 + // 28 - GA20 + TempBuf.Left = 0x00; + TempBuf.Right = 0x00; + CurCLst = p->ChipListAll; + while(CurCLst != NULL) + { + if (! CurCLst->COpts->Disabled) + { + ResampleChipStream(p, CurCLst, &TempBuf, 1); + } + CurCLst = CurCLst->next; + } + + // ChipData << 9 [ChipVol] >> 5 << 8 [MstVol] >> 11 -> 9-5+8-11 = <<1 + TempBuf.Left = ((TempBuf.Left >> 5) * CurMstVol) >> 11; + TempBuf.Right = ((TempBuf.Right >> 5) * CurMstVol) >> 11; + if (p->SurroundSound) + TempBuf.Right *= -1; + Buffer[CurSmpl].Left = Limit2Short(TempBuf.Left); + Buffer[CurSmpl].Right = Limit2Short(TempBuf.Right); + + if (p->FadePlay && ! p->FadeStart) + { + p->FadeStart = p->PlayingTime; + RecalcStep = p->FadePlay ? p->SampleRate / 100 : 0; + } + if (RecalcStep && ! (CurSmpl % RecalcStep)) + CurMstVol = RecalcFadeVolume(p); + + if (p->VGMEnd) + { + if (! p->EndPlay) + { + p->EndPlay = true; + break; + } + } + } + + return CurSmpl; +} diff --git a/Frameworks/GME/vgmplay/VGMPlay.dsp b/Frameworks/GME/vgmplay/VGMPlay.dsp new file mode 100644 index 000000000..dda2e9cef --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay.dsp @@ -0,0 +1,1096 @@ +# Microsoft Developer Studio Project File - Name="VGMPlay" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** NICHT BEARBEITEN ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=VGMPlay - Win32 Debug +!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE +!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl +!MESSAGE +!MESSAGE NMAKE /f "VGMPlay.mak". +!MESSAGE +!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben +!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: +!MESSAGE +!MESSAGE NMAKE /f "VGMPlay.mak" CFG="VGMPlay - Win32 Debug" +!MESSAGE +!MESSAGE Für die Konfiguration stehen zur Auswahl: +!MESSAGE +!MESSAGE "VGMPlay - Win32 Release" (basierend auf "Win32 (x86) Console Application") +!MESSAGE "VGMPlay - Win32 Debug" (basierend auf "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /Ox /Ot /Og /Oi /Ob2 /I "zlib" /D "NDEBUG" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "ENABLE_ALL_CORES" /D "CONSOLE_MODE" /D "ADDITIONAL_FORMATS" /D "SET_CONSOLE_TITLE" /FD /c +# SUBTRACT CPP /Oa /Ow /YX /Yc /Yu +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 msvcrt.lib kernel32.lib user32.lib advapi32.lib winmm.lib zdll.lib /nologo /subsystem:console /machine:I386 /nodefaultlib /libpath:"zlib" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=..\vgm2txt\HiddenMsg.exe Release\VGMPlay.exe +# End Special Build Tool + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "zlib" /D "_DEBUG" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "ENABLE_ALL_CORES" /D "CONSOLE_MODE" /D "ADDITIONAL_FORMATS" /D "SET_CONSOLE_TITLE" /FR /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 msvcrtd.lib kernel32.lib user32.lib advapi32.lib winmm.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib /libpath:"zlib" +# SUBTRACT LINK32 /profile /map + +!ENDIF + +# Begin Target + +# Name "VGMPlay - Win32 Release" +# Name "VGMPlay - Win32 Debug" +# Begin Group "Quellcodedateien" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\pt_ioctl.c +# End Source File +# Begin Source File + +SOURCE=.\Stream.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\VGMPlay.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\VGMPlay_AddFmts.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\VGMPlayUI.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# End Group +# Begin Group "Header-Dateien" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\PortTalk_IOCTL.h +# End Source File +# Begin Source File + +SOURCE=.\Stream.h +# End Source File +# Begin Source File + +SOURCE=".\XMasFiles\SWJ-SQRC01_1C.h" +# End Source File +# Begin Source File + +SOURCE=.\VGMFile.h +# End Source File +# Begin Source File + +SOURCE=.\VGMPlay.h +# End Source File +# Begin Source File + +SOURCE=.\VGMPlay_Intf.h +# End Source File +# Begin Source File + +SOURCE=.\XMasFiles\XMasBonus.h +# End Source File +# End Group +# Begin Group "Ressourcendateien" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "SoundCore" + +# PROP Default_Filter "c;h;cpp" +# Begin Group "FM OPL Chips" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\2413intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\2413intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\2413tone.h +# End Source File +# Begin Source File + +SOURCE=.\chips\262intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\262intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\281btone.h +# End Source File +# Begin Source File + +SOURCE=.\chips\3526intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /W1 /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +# ADD CPP /W1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\3526intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\3812intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /W1 /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +# ADD CPP /W1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\3812intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\8950intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /W1 /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +# ADD CPP /W1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\8950intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\adlibemu.h +# End Source File +# Begin Source File + +SOURCE=.\chips\adlibemu_opl2.c +# End Source File +# Begin Source File + +SOURCE=.\chips\adlibemu_opl3.c +# End Source File +# Begin Source File + +SOURCE=.\chips\emu2413.c +# End Source File +# Begin Source File + +SOURCE=.\chips\emu2413.h +# End Source File +# Begin Source File + +SOURCE=.\chips\emutypes.h +# End Source File +# Begin Source File + +SOURCE=.\chips\fmopl.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\fmopl.h +# End Source File +# Begin Source File + +SOURCE=.\chips\opl.c +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\chips\opl.h +# End Source File +# Begin Source File + +SOURCE=.\chips\vrc7tone.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2413.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2413.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ymf262.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\ymf262.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ymf278b.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\ymf278b.h +# End Source File +# End Group +# Begin Group "FM OPN Chips" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\2203intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\2203intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\2608intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\2608intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\2610intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\2610intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\2612intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\2612intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\fm.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\fm.h +# End Source File +# Begin Source File + +SOURCE=.\chips\fm2612.c +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2612.c +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2612.h +# End Source File +# End Group +# Begin Group "FM OPx Chips" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\2151intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\2151intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\scsp.c +# End Source File +# Begin Source File + +SOURCE=.\chips\scsp.h +# End Source File +# Begin Source File + +SOURCE=.\chips\scspdsp.c +# End Source File +# Begin Source File + +SOURCE=.\chips\scspdsp.h +# End Source File +# Begin Source File + +SOURCE=.\chips\scsplfo.c +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2151.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2151.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ymf271.c +# ADD CPP /W1 +# End Source File +# Begin Source File + +SOURCE=.\chips\ymf271.h +# End Source File +# End Group +# Begin Group "PCM Chips" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\c140.c +# End Source File +# Begin Source File + +SOURCE=.\chips\c140.h +# End Source File +# Begin Source File + +SOURCE=.\chips\c352.c +# End Source File +# Begin Source File + +SOURCE=.\chips\c352.h +# End Source File +# Begin Source File + +SOURCE=.\chips\es5503.c +# End Source File +# Begin Source File + +SOURCE=.\chips\es5503.h +# End Source File +# Begin Source File + +SOURCE=.\chips\es5506.c +# End Source File +# Begin Source File + +SOURCE=.\chips\es5506.h +# End Source File +# Begin Source File + +SOURCE=.\chips\iremga20.c +# End Source File +# Begin Source File + +SOURCE=.\chips\iremga20.h +# End Source File +# Begin Source File + +SOURCE=.\chips\k053260.c +# End Source File +# Begin Source File + +SOURCE=.\chips\k053260.h +# End Source File +# Begin Source File + +SOURCE=.\chips\k054539.c +# End Source File +# Begin Source File + +SOURCE=.\chips\k054539.h +# End Source File +# Begin Source File + +SOURCE=.\chips\multipcm.c +# End Source File +# Begin Source File + +SOURCE=.\chips\multipcm.h +# End Source File +# Begin Source File + +SOURCE=.\chips\okim6258.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\okim6258.h +# End Source File +# Begin Source File + +SOURCE=.\chips\okim6295.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\okim6295.h +# End Source File +# Begin Source File + +SOURCE=.\chips\pwm.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\pwm.h +# End Source File +# Begin Source File + +SOURCE=.\chips\qsound.c +# End Source File +# Begin Source File + +SOURCE=.\chips\qsound.h +# End Source File +# Begin Source File + +SOURCE=.\chips\rf5c68.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\rf5c68.h +# End Source File +# Begin Source File + +SOURCE=.\chips\scd_pcm.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\scd_pcm.h +# End Source File +# Begin Source File + +SOURCE=.\chips\segapcm.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\segapcm.h +# End Source File +# Begin Source File + +SOURCE=.\chips\upd7759.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\upd7759.h +# End Source File +# Begin Source File + +SOURCE=.\chips\x1_010.c +# End Source File +# Begin Source File + +SOURCE=.\chips\x1_010.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ymdeltat.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\ymdeltat.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ymz280b.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /W1 /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +# ADD CPP /W1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\ymz280b.h +# End Source File +# End Group +# Begin Group "OPL Mapper" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\ay8910_opl.c +# End Source File +# Begin Source File + +SOURCE=.\chips\sn76496_opl.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2413_opl.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /W1 /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2413hd.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /W1 /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\ym2413hd.h +# End Source File +# End Group +# Begin Group "PSG Chips" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\ay8910.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\ay8910.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ay_intf.c +# End Source File +# Begin Source File + +SOURCE=.\chips\ay_intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\c6280.c +# End Source File +# Begin Source File + +SOURCE=.\chips\c6280.h +# End Source File +# Begin Source File + +SOURCE=.\chips\c6280intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\c6280intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\emu2149.c +# End Source File +# Begin Source File + +SOURCE=.\chips\emu2149.h +# End Source File +# Begin Source File + +SOURCE=.\chips\gb.c +# End Source File +# Begin Source File + +SOURCE=.\chips\gb.h +# End Source File +# Begin Source File + +SOURCE=.\chips\k051649.c +# End Source File +# Begin Source File + +SOURCE=.\chips\k051649.h +# End Source File +# Begin Source File + +SOURCE=.\chips\Ootake_PSG.c +# End Source File +# Begin Source File + +SOURCE=.\chips\Ootake_PSG.h +# End Source File +# Begin Source File + +SOURCE=.\chips\pokey.c +# End Source File +# Begin Source File + +SOURCE=.\chips\pokey.h +# End Source File +# Begin Source File + +SOURCE=.\chips\saa1099.c +# End Source File +# Begin Source File + +SOURCE=.\chips\saa1099.h +# End Source File +# Begin Source File + +SOURCE=.\chips\sn76489.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\sn76489.h +# End Source File +# Begin Source File + +SOURCE=.\chips\sn76496.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\sn76496.h +# End Source File +# Begin Source File + +SOURCE=.\chips\sn764intf.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\sn764intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\vsu.c +# End Source File +# Begin Source File + +SOURCE=.\chips\vsu.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ws_audio.c +# End Source File +# Begin Source File + +SOURCE=.\chips\ws_audio.h +# End Source File +# Begin Source File + +SOURCE=.\chips\ws_initialIo.h +# End Source File +# End Group +# Begin Group "NES Chips" + +# PROP Default_Filter "c;h" +# Begin Source File + +SOURCE=.\chips\nes_apu.c +# End Source File +# Begin Source File + +SOURCE=.\chips\nes_apu.h +# End Source File +# Begin Source File + +SOURCE=.\chips\nes_defs.h +# End Source File +# Begin Source File + +SOURCE=.\chips\nes_intf.c +# End Source File +# Begin Source File + +SOURCE=.\chips\nes_intf.h +# End Source File +# Begin Source File + +SOURCE=.\chips\np_nes_apu.c +# End Source File +# Begin Source File + +SOURCE=.\chips\np_nes_apu.h +# End Source File +# Begin Source File + +SOURCE=.\chips\np_nes_dmc.c +# End Source File +# Begin Source File + +SOURCE=.\chips\np_nes_dmc.h +# End Source File +# Begin Source File + +SOURCE=.\chips\np_nes_fds.c +# End Source File +# Begin Source File + +SOURCE=.\chips\np_nes_fds.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\chips\ChipIncl.h +# End Source File +# Begin Source File + +SOURCE=.\ChipMapper.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\ChipMapper.h +# End Source File +# Begin Source File + +SOURCE=.\chips\dac_control.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\dac_control.h +# End Source File +# Begin Source File + +SOURCE=.\chips\mamedef.h +# End Source File +# Begin Source File + +SOURCE=.\chips\panning.c + +!IF "$(CFG)" == "VGMPlay - Win32 Release" + +# ADD CPP /Oa + +!ELSEIF "$(CFG)" == "VGMPlay - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\chips\panning.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/Frameworks/GME/vgmplay/VGMPlay.dsw b/Frameworks/GME/vgmplay/VGMPlay.dsw new file mode 100644 index 000000000..e78cc3483 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN! + +############################################################################### + +Project: "VGMPlay"=".\VGMPlay.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Frameworks/GME/vgmplay/VGMPlay.h b/Frameworks/GME/vgmplay/VGMPlay.h new file mode 100644 index 000000000..da1add077 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay.h @@ -0,0 +1,295 @@ +// Header File for structures and constants used within VGMPlay.c + +#include "chips/mamedef.h" + +#include "VGMFile.h" + +#include "VGMPlay_Intf.h" + +#define VGMPLAY_VER_STR "0.40.6" +//#define APLHA +//#define BETA +#define VGM_VER_STR "1.71b" +#define VGM_VER_NUM 0x170 + +#define CHIP_COUNT 0x29 +typedef struct chip_options +{ + bool Disabled; + UINT8 EmuCore; + UINT8 ChnCnt; + // Special Flags: + // YM2612: Bit 0 - DAC Highpass Enable, Bit 1 - SSG-EG Enable + // YM-OPN: Bit 0 - Disable AY8910-Part + UINT16 SpecialFlags; + + // Channel Mute Mask - 1 Channel is represented by 1 bit + UINT32 ChnMute1; + // Mask 2 - used by YMF287B for OPL4 Wavetable Synth and by YM2608/YM2610 for PCM + UINT32 ChnMute2; + // Mask 3 - used for the AY-part of some OPN-chips + UINT32 ChnMute3; + + INT16* Panning; +} CHIP_OPTS; +typedef struct chips_options +{ + CHIP_OPTS SN76496; + CHIP_OPTS YM2413; + CHIP_OPTS YM2612; + CHIP_OPTS YM2151; + CHIP_OPTS SegaPCM; + CHIP_OPTS RF5C68; + CHIP_OPTS YM2203; + CHIP_OPTS YM2608; + CHIP_OPTS YM2610; + CHIP_OPTS YM3812; + CHIP_OPTS YM3526; + CHIP_OPTS Y8950; + CHIP_OPTS YMF262; + CHIP_OPTS YMF278B; + CHIP_OPTS YMF271; + CHIP_OPTS YMZ280B; + CHIP_OPTS RF5C164; + CHIP_OPTS PWM; + CHIP_OPTS AY8910; + CHIP_OPTS GameBoy; + CHIP_OPTS NES; + CHIP_OPTS MultiPCM; + CHIP_OPTS UPD7759; + CHIP_OPTS OKIM6258; + CHIP_OPTS OKIM6295; + CHIP_OPTS K051649; + CHIP_OPTS K054539; + CHIP_OPTS HuC6280; + CHIP_OPTS C140; + CHIP_OPTS K053260; + CHIP_OPTS Pokey; + CHIP_OPTS QSound; + CHIP_OPTS SCSP; + CHIP_OPTS WSwan; + CHIP_OPTS VSU; + CHIP_OPTS SAA1099; + CHIP_OPTS ES5503; + CHIP_OPTS ES5506; + CHIP_OPTS X1_010; + CHIP_OPTS C352; + CHIP_OPTS GA20; +// CHIP_OPTS OKIM6376; +} CHIPS_OPTION; + +typedef void (*strm_func)(void *, stream_sample_t **outputs, int samples); + +typedef struct chip_audio_attributes CAUD_ATTR; +struct chip_audio_attributes +{ + UINT32 TargetSmpRate; + UINT32 SmpRate; + UINT32 LastSmpRate; + UINT16 Volume; + UINT8 ChipType; + UINT8 ChipID; // 0 - 1st chip, 1 - 2nd chip, etc. + void* Resampler; + strm_func StreamUpdate; + void* StreamUpdateParam; + CAUD_ATTR* Paired; +}; + +typedef struct chip_audio_struct +{ + CAUD_ATTR SN76496; + CAUD_ATTR YM2413; + CAUD_ATTR YM2612; + CAUD_ATTR YM2151; + CAUD_ATTR SegaPCM; + CAUD_ATTR RF5C68; + CAUD_ATTR YM2203; + CAUD_ATTR YM2608; + CAUD_ATTR YM2610; + CAUD_ATTR YM3812; + CAUD_ATTR YM3526; + CAUD_ATTR Y8950; + CAUD_ATTR YMF262; + CAUD_ATTR YMF278B; + CAUD_ATTR YMF271; + CAUD_ATTR YMZ280B; + CAUD_ATTR RF5C164; + CAUD_ATTR PWM; + CAUD_ATTR AY8910; + CAUD_ATTR GameBoy; + CAUD_ATTR NES; + CAUD_ATTR MultiPCM; + CAUD_ATTR UPD7759; + CAUD_ATTR OKIM6258; + CAUD_ATTR OKIM6295; + CAUD_ATTR K051649; + CAUD_ATTR K054539; + CAUD_ATTR HuC6280; + CAUD_ATTR C140; + CAUD_ATTR K053260; + CAUD_ATTR Pokey; + CAUD_ATTR QSound; + CAUD_ATTR SCSP; + CAUD_ATTR WSwan; + CAUD_ATTR VSU; + CAUD_ATTR SAA1099; + CAUD_ATTR ES5503; + CAUD_ATTR ES5506; + CAUD_ATTR X1_010; + CAUD_ATTR C352; + CAUD_ATTR GA20; + // CAUD_ATTR OKIM6376; +} CHIP_AUDIO; + +typedef struct chip_aud_list CA_LIST; +struct chip_aud_list +{ + CAUD_ATTR* CAud; + CHIP_OPTS* COpts; + CA_LIST* next; +}; + +typedef struct daccontrol_data +{ + bool Enable; + UINT8 Bank; +} DACCTRL_DATA; + +typedef struct pcmbank_table +{ + UINT8 ComprType; + UINT8 CmpSubType; + UINT8 BitDec; + UINT8 BitCmp; + UINT16 EntryCount; + void* Entries; +} PCMBANK_TBL; + +typedef struct vgm_player +{ + // Options Variables + UINT32 SampleRate; // Note: also used by some sound cores to determinate the chip sample rate + + UINT32 VGMMaxLoop; + UINT32 VGMPbRate; // in Hz, ignored if this value or VGM's lngRate Header value is 0 +#ifdef ADDITIONAL_FORMATS + UINT32 CMFMaxLoop; +#endif + UINT32 FadeTime; + + float VolumeLevel; + bool SurroundSound; + bool FadeRAWLog; + //bool FullBufFill; // Fill Buffer until it's full + + bool DoubleSSGVol; + + UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both + UINT8 CHIP_SAMPLING_MODE; + INT32 CHIP_SAMPLE_RATE; + + CHIPS_OPTION ChipOpts[0x02]; + + stream_sample_t* DUMMYBUF[0x02]; + + char* AppPaths[8]; + + UINT8 FileMode; + VGM_HEADER VGMHead; + VGM_HDR_EXTRA VGMHeadX; + VGM_EXTRA VGMH_Extra; + UINT32 VGMDataLen; + UINT8* VGMData; + GD3_TAG VGMTag; + +#define PCM_BANK_COUNT 0x40 + VGM_PCM_BANK PCMBank[PCM_BANK_COUNT]; + PCMBANK_TBL PCMTbl; + UINT8 DacCtrlUsed; + UINT8 DacCtrlUsg[0xFF]; + DACCTRL_DATA DacCtrl[0xFF]; + + CHIP_AUDIO ChipAudio[0x02]; + CAUD_ATTR CA_Paired[0x02][0x03]; + float MasterVol; + + CA_LIST ChipListBuffer[0x200]; + CA_LIST* ChipListAll; // all chips needed for playback (in general) + //CA_LIST* ChipListOpt; // ChipListAll minus muted chips + +#define SMPL_BUFSIZE 0x100 + INT32* StreamBufs[0x02]; + + UINT32 VGMPos; + INT32 VGMSmplPos; + INT32 VGMSmplPlayed; + INT32 VGMSampleRate; + UINT32 VGMPbRateMul; + UINT32 VGMPbRateDiv; + UINT32 VGMSmplRateMul; + UINT32 VGMSmplRateDiv; + bool VGMEnd; + bool EndPlay; + bool FadePlay; + bool ForceVGMExec; + UINT8 PlayingMode; + UINT32 PlayingTime; + UINT32 FadeStart; + UINT32 VGMMaxLoopM; + UINT32 VGMCurLoop; + float VolumeLevelM; + float FinalVol; + bool ResetPBTimer; + + UINT8 IsVGMInit; + UINT16 Last95Drum; // for optvgm debugging + UINT16 Last95Max; // for optvgm debugging + UINT32 Last95Freq; // for optvgm debugging + + bool ErrorHappened; + + // the chips' states + void * sn764xx[2]; + void * ym2413[2]; + void * ym2612[2]; + void * ym2151[2]; + void * segapcm[2]; + void * rf5c68; + void * ym2203[2]; + void * ym2608[2]; + void * ym2610[2]; + void * ym3812[2]; + void * ym3812_dual_data[2]; + void * ym3526[2]; + void * y8950[2]; + void * ymf262[2]; + void * ymf278b[2]; + void * ymf271[2]; + void * ymz280b[2]; + void * rf5c164; + void * pwm; + void * ay8910[2]; + void * gbdmg[2]; + void * nesapu[2]; + void * multipcm[2]; + void * upd7759[2]; + void * okim6258[2]; + void * okim6295[2]; + void * k051649[2]; + void * k054539[2]; + void * huc6280[2]; + void * c140[2]; + void * k053260[2]; + void * pokey[2]; + void * qsound[2]; + void * scsp[2]; + void * wswan[2]; + void * vsu[2]; + void * saa1099[2]; + void * es5503[2]; + void * es550x[2]; + void * x1_010[2]; + void * c352[2]; + void * ga20[2]; + void * daccontrol[255]; +} VGM_PLAYER; diff --git a/Frameworks/GME/vgmplay/VGMPlay.ini b/Frameworks/GME/vgmplay/VGMPlay.ini new file mode 100644 index 000000000..1a71a8cb5 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay.ini @@ -0,0 +1,344 @@ +; VGMPlay Configuration File +; -------------------------- +; +; Default Values are usually 0 (False for boolean ones) +; Boolean Values are: +; False / 0 +; True / 1 + +[General] +; Default Sample Rate: 44100 +SampleRate = 48000 +; If you set PlaybackRate to 50, some songs will play slower, like on a PAL console. +; If you set it to 60, some songs may play faster, like PAL games on a NTSC console. +PlaybackRate = 0 + +; double the volume of the YM2xxx SSG, if it gets overridden by a VGM's header. +; This option will be removed once all VGMs on vgmrips are fixed. +DoubleSSGVol = True + +; Display Japanese GD3 Tag if available +; Most western Windows systems won't be able to display Japanese characters in the normal console, +; so use at own risk. (Linux doesn't have this problem.) +PreferJapTag = False + +; Default Fade Time: 5000 +FadeTime = 8000 +; In-Playlist Fade Time: for all looping tracks in a playlist (except the last one) +; Setting this to 0 simulate a Song-Medley without gaps. +; Default Fade Time in Playlist: 2000 +FadeTimePL = 0 +JinglePause = 1000 + +; Fade RAW logs from emulators (VGMs without Creator-Tag) so that they don't +; end abruptly at the full volume level but at 33% +;FadeRAWLogs = True +; Default Volume: 1.0 (of course) +Volume = 1.0 + +; Log Sound to Wave: 0 - no logging, 1 - log only, 2 - play and log +LogSound = 0 + +; Maximum Loops before fading +; Default: 0x02 (0x01 for CMF) +MaxLoops = 0x02 +MaxLoopsCMF = 0x01 + +; Resampling Mode: +; 0 - always high quality resampler (default) +; 1 - HQ resampler for upsampling, LQ resampler for downsampling (recommend for slow machines) +; 2 - always low quality resampler (very fast) +ResamplingMode = 0 +; Chip Sample Mode: +; 0 - Native (default) +; 1 - use highest sampling rate (native or the one below) +; 2 - use always the sampling rate below (CPU friendly) +; 3 - use native sample rate for FM chips and highest sampling rate for all others +ChipSmplMode = 3 +; Default Chip Sample Rate: 0 (results in value of Playback SampleRate) +ChipSmplRate = 0 + +; Force Audio Buffer Number (1 Buffer = 10 ms, Minimum is 4, Maximum is 200) +; higher values result in greater delays while seeking (and pausing with EmulatePause On) +; set this to 50 or 100 if the sound is choppy +; 0 results in 10 for Windows 98/ME/2000/XP/7, +; 50 for Windows 95 and 20 for Windows Vista +AudioBuffers = 0 +; "Surround" Sound - inverts the waveform of the right channel to create a pseudo surround effect +; use only with headphones!! +SurroundSound = False +; Emulate during Pause: continue to generate sound while playback is paused +EmulatePause = False +; Shows the last data block played with DAC Stream Command 95. Useful for debugging. +; 0 - don't show +; 1 - show data block ID only +; 2 - show data block ID + frequency +; 3 - show data block ID + frequency in KHz +ShowStreamCmds = 3 + +; --- FM Hardware Section Start --- +; Hardware FM Port (in hex, usually 220 or 388) +FMPort = 0 +; Force FM Mode, even if only the SN76496 is used, also enables Mixed Mode (Hardware FM + Software Emulator) +FMForce = False +; Makes some FM-Voices fading on track-end instead of instant silencing them +FMSoftStop = True +; Overrides Volume setting, if FM hardware is used +; Possible values: +; = 0 - don't override (default) +; > 0 - override Volume setting and VGM Volume Modifier +; < 0 - multiply with volume +FMVolume = 0.0 +; --- FM Hardware Section End --- + + +; Chip Options +; ------------ +; - Disabled = False/True +; disable the emulation of the current chip +; - EmulatorType = 0 / 1 / ... +; 0 is recommend/default, 1+ are alternative emulation cores +; - MuteMask = 0 +; mute channels by setting the muting bitmask +; - MuteCh? = False/True +; mute channel ? +; - Mutexxx = False/True +; mute channel with the name xxx (e.g. DAC, DT, BD, ...) + +[SN76496] +Disabled = False +; EmulatorType: 0 - MAME, 1 - Maxim +EmulatorType = 0x00 +; Channels: 4 (0-3) + +[YM2413] +Disabled = False +; FMPort = 0: +; EmulatorType: 0 - EMU2413, 1 - MAME +; FMPort != 0: +; EmulatorType: 0 - Valley Bell Custom, 1 - Meka +EmulatorType = 0x00 +; Channels: 14 (0-8, BD, SD, TOM, TC, HH) + +[YM2612] +Disabled = False +; EmulatorType: 0 - MAME (Genesis Plus GX), 1 - Gens +EmulatorType = 0x00 +; MAME: if on, the chip updates its left/right channel alternatively, creating a nice pseudo-stereo effect +; Note: If you emulate at a set sample rate, this option halves it. +PseudoStereo = False +; Gens: DAC Highpass-Filter (sometimes sounds good, but sometimes it generates a terrible noise) +DACHighpass = False +; Gens: SSG-EG Enable (very buggy) +SSG-EG = False +; Channels: 7 (0-5, DAC) + +[YM2151] +Disabled = False +; Channels: 8 (0-7) + +[SegaPCM] +Disabled = False +; Channels: 16 (0-15) + +[RF5C68] +Disabled = False +; Channels: 8 (0-7) + +[YM2203] +Disabled = False +; AY/YM2149 EmulatorType: 0 - EMU2149, 1 - MAME +EmulatorType = 0x00 +; disable the AY8910-part to speed up loading +DisableAY = False +; Channels: 3 (0-2) + +[YM2608] +Disabled = False +EmulatorType = 0x00 +DisableAY = False +; Channels: 6 FM (0-5) + 6 ADPCM (0-5) + 1 Delta-T +; Use MuteMask_FM, MuteMask_PCM (Delta-T is Ch6), MuteFMCh, MutePCMCh and MuteDT + +[YM2610] +Disabled = False +EmulatorType = 0x00 +DisableAY = False +; Channels: 6 FM (0-5) + 6 ADPCM (0-5) + 1 Delta-T +; Use MuteMask_FM, MuteMask_PCM (Delta-T is Ch6), MuteFMCh, MutePCMCh and MuteDT + +[YM3812] +Disabled = False +; EmulatorType: 0 - DOSBox (AdLibEmu), 1 - MAME +EmulatorType = 0x00 +; Channels: 14 (0-8, BD, SD, TOM, TC, HH) + +[YM3526] +Disabled = False +; Channels: 14 (0-8, BD, SD, TOM, TC, HH) + +[Y8950] +Disabled = False +; Channels: 15 (0-8, BD, SD, TOM, TC, HH, DT) + +[YMF262] +Disabled = False +; EmulatorType: 0 - DOSBox (AdLibEmu), 1 - MAME +EmulatorType = 0x00 +; Channels: 23 (0-17, BD, SD, TOM, TC, HH) + +[YMF278B] +Disabled = False +; Channels: 23 FM (0-17, BD, SD, TOM, TC, HH) + 24 WaveTable (0-23) +; Use MuteMask_FM, MuteMask_WT, MuteFMCh and MuteWTCh + +[YMF271] +Disabled = False +; Channels: 12 (0-11) + +[YMZ280B] +Disabled = False +; Channels: 8 (0-7) + +[RF5C164] +Disabled = False +; Channels: 8 (0-7) + +[PWM] +Disabled = False +; Channels: none (it just has left and right) + +[AY8910] +Disabled = False +; EmulatorType: 0 - EMU2149, 1 - MAME +EmulatorType = 0x00 +; Channels: 3 (0-2) + +[GameBoy] +Disabled = False +; double the volume of the Wave Channel (sounds better, but may be less accurate and seems to sound distorted sometimes, like nezplay++) +BoostWaveChn = True +; don't double volume of the Noise Channel (like MESS and nezplay++, False is like VisualBoy Advance and sounds better for some games) +LowerNoiseChn = True +; disable the accuracy hacks (expect some slightly off-tune notes and a very distorted Wave Channel) +Inaccurate = False +; Channels: 4 (0-3) + +[NES APU] +Disabled = False +; EmulatorType: 0 - NSFPlay, 1 - MAME +EmulatorType = 0x00 +; Channels: 6 (0-5 = Square 1, Square 2, Triangle, Noise, DPCM, FDS) + +; Options (NSFPlay cores only) +; ------- +; APU/DMC Options (2 bits, default: 0x03) +; 0x01 - OPT_UNMUTE_ON_RESET (enable all channels by default after reset) +; 0x02 - OPT_NONLINEAR_MIXER +SharedOpts = 0x03 +; APU Options (2 bits, default: 0x01) +; 0x01 - OPT_PHASE_REFRESH +; 0x02 - OPT_DUTY_SWAP +APUOpts = 0x01 +; DMC Options (6 bits, default: 0x3B) +; 0x01 - OPT_ENABLE_4011 +; 0x02 - OPT_ENABLE_PNOISE +; 0x04 - OPT_DPCM_ANTI_CLICK (nullify register 4011 writes, keeps DPCM limits correctly) +; 0x08 - OPT_RANDOMIZE_NOISE +; 0x10 - OPT_TRI_MUTE (stops Triangle wave if set to freq = 0, processes it at a very high rate else) +; 0x20 - OPT_TRI_NULL (VB custom, always makes Triangle return to null-level when stopping) +DMCOpts = 0x3B +; FDS Options (1 bit, default: 0x00) +; 0x01 - OPT_4085_RESET (reset modulation phase on 4085 writes) +FDSOpts = 0x00 + +[MultiPCM] +Disabled = False +; Channels: 28 (0-27) + +[uPD7759] +Disabled = False +; Channels: none (actually 1) + +[OKIM6258] +Disabled = False +; enables internal 10-bit processing (original MESS behaviour) +; The comments in the code say something about 10-bit and 12-bit DAC, but that's not what the code does. +Enable10Bit = False +; Remove the DC offset by resetting the ADPCM signal everytime a Play command is issued. +; Note: This causes lots of clicks with polyphonic ADPCM drivers, so the option is False by default. +RemoveDCOfs = True +; Channels: none (actually 1) + +[OKIM6295] +Disabled = False +; Channels: 4 (0-3) + +[K051649] +; also known as SCC1 +Disabled = False +; Channels: 5 (0-4) + +[K054539] +Disabled = False +; Channels: 8 (0-7) + +[HuC6280] +Disabled = False +; EmulatorType: 0 - Ootake, 1 - MAME (sounds brighter, lacks LFO) +EmulatorType = 0x00 +; Channels: 6 (0-5) + +[C140] +Disabled = False +; Channels: 24 (0-23) + +[K053260] +Disabled = False +; Channels: 4 (0-3) + +[Pokey] +Disabled = False +; Channels: 4 (0-3) + +[QSound] +Disabled = False +; Channels: 16 (0-15) + +[SCSP] +Disabled = False +; Skip all DSP calculations, huge speedup (the DSP doesn't work correctly right now anyway) +BypassDSP = True +; Channels: 32 (0-31) + +[WSwan] +Disabled = False +; Channels: 4 (0-3) + +[VSU] +Disabled = False +; Channels: 6 (0-5) + +[SAA1099] +Disabled = False +; Channels: 6 (0-5) + +[ES5503] +Disabled = False +; Channels: 32 (0-31) + +[ES5506] +Disabled = False +; Channels: 32 (0-31) + +[X1-010] +Disabled = False +; Channels: 16 (0-15) + +[C352] +Disabled = False +; Channels: 32 (0-31) + +[GA20] +Disabled = False +; Channels: 4 (0-3) diff --git a/Frameworks/GME/vgmplay/VGMPlay.txt b/Frameworks/GME/vgmplay/VGMPlay.txt new file mode 100644 index 000000000..c6a127dd1 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay.txt @@ -0,0 +1,159 @@ +VGM Player - Readme +========== + +General +======= +Usage: Drop a file on the executable or open the program and type the filename. + +Supported files types are: +- Video Game Music Files (*.vgm, *.vgz) +- Creative Music Files (*.cmf) +- DosBox RAW OPL Log Files (*.dro) +- Playlist files (*.m3u) + +Supported chips +--------------- +- SN76496** (Sega PSG) and T6W28** (NeoGeo Pocket custom) +- YM2413* (OPLL) +- YM2612 (OPN2) +- YM2151 (OPM) +- SegaPCM +- RF5C68 +- YM2203 (OPN) +- YM2608 (OPNA) +- YM2610/B (OPNB) +- YM3812* (OPL2) +- YM3526* (OPL) +- Y8950* (MSX AUDIO) +- YMF262* (OP3) +- YMF278B (OPL4) ! +- YMF271 (OPLX) +- YMZ280B +- RF5C164 (Sega MegaCD PCM) +- PWM (from Sega 32x) +- AY8910 (MSX PSG) +- GameBoy DMG +- NES APU (incl. FDS) +- MultiPCM +- UPD7759 +- OKI6258 (Sharp X68000 ADPCM) +- OKI6295 +- K051649 +- K054539 +- HuC6280 (PC Engine) +- Namco C140 +- K053260 +- Pokey (Atari) +- QSound +- SCSP (Saturn Custom Sound Processor, YMF292-F) +- WonderSwan +- Virtual Boy VSU +- SAA1099 +- ESS5503 +- ESS5505/6 +- Seta X1-010 +- Namco C352 +- Irem GA20 + + +* This chip can be emulated via OPL Hardware (like Soundblaster sound cards). +** OPL hardware emulation is available, but software emulation is prefered. Hardware emulation is used if another chip activates HW emulation or FMForce is True. +! You need a sample ROM, called yrw801.rom, to make playback work. Place it in the directory where VGMPlay.exe lies. + +OPL hardware emulation can be enabled by setting the "FMPort"-entry in the ini-file. +If you want to use FM Hardware under Windows NT/2000/XP/... you have to install PortTalk. +PortTalk Website: http://www.beyondlogic.org/porttalk/porttalk.htm +Under Linux the program must have root rights to use Hardware FM. + +It's possible to write Wave-Files by editing the "LogSound"-line in the ini-file. +Batch conversions are possible by opening a playlist. +FM Hardware can't be logged to Wave files. + +Keys +---- +You can use the following keys during playback: +Space - Pause +Cursor Left/Right - Seek 5 seconds backward/forward +Ctrl + Cursor Left/Right - Seek 1 minute backward/forward +ESC/Q - Quit the program +F - Fade out +R - Restart current Track +PageUp/B - Previous Track +PageDown/N - Next Track + +Note: All keys also work during silent sound logging. + + +System Reqiurements +=================== +It depends on the files you want to play. + +Minimum Reqiurements +-------------------- +Software Emulation: + +166 MHz should be enough for SN76496 + YM2612. +For chips with 12+ FM channels you need probably need at least 700 MHz. +16 MB RAM (plus a size of the VGM you want to play, so for a 100 MB vgm, you need ~110 MB RAM) + +That allows you to play VGMs with up to 2x FM, 2x PSG or FM + PSG + PCM. +It's NOT enough for YMF271 emulation. +The YMF262 is equal to 2x FM. The YMF278B is Wavetable + YMF262 (with the latter one disabled if unused). + +Hardware FM Playback: + +486 with 33 MHz? - I dunno +On my PII 233 MHz FM Playback takes 3.5% CPU at a maximum. + + +Bugs +==== +PauseEmulation is disabled under Linux unless FM Hardware is used. + +Under Linux you have to double-tap ESC to quit the program (or just press Q). I haven't yet found a way around it. + +Sometimes MAME's sound cores tend to sound strange. I already fixed some, but it's not my fault. + +Ubuntu refuses to run (or maybe compile) VGMPlay correctly. It either crashes upon opening a vgm file or doesn't open the sound device. (and the ini-file, too. VGMs are opened for some reason.) +Using Wine with the Windows-version of VGMPlay should work. + +It runs fine on openSUSE. (I compile and test it regularly with 12.3 64-bit. Older version were successfully tested with 11.1 32-bit and 11.4 64-bit) +It also ran fine on the Debian system at university. + + +Comments +======== +If you want to set some options, open the ini-file. It's well commented. + +The T6W28 doesn't use MAME's T6W28 core. Instead I modified the SN76496 core to emulate the T6W28 with 2 SN76496 chips. + +The SN76496 OPL emulation is okay, but it's impossible to get the noise sound right. + +EMU2413 Emulator was added, because sometimes the one of MAME sounds strange. +I added the Gens YM2612 core for the same reason (before I fixed MAME's YM2612 core). + +I haven't yet found a player that supports all three version of dro files. +P.S.: AdPlug now seems to support them. + +Some may be wondering, how someone can have the idea to implement OPL Hardware support. Here a more or less small story: +I like CMF MIDI files, but I was unable to listen to them while doing other things on my computer in Windows 95. It was impossible to listen to them unter Windows 2000. When I found AdPlug, the CMF support was bad and I started to make my VGM Player play CMF files. Debugging wasn't nice - a Pentium2 233MHz isn't stong enough for a Software OPL Emulator in Debug Mode. The OPL2-Port option in AdPlug was interesting (I have a Soundblaster 16) and showed me the power of PortTalk. +My MIDI-FM Player that got OPL-Hardware-Support earlier than my VGM Player, because playing usual MIDI files requires a lot more debugging than playing simple VGM files. When it was working in my MIDI player, I implemented OPL-Hardware-Support into VGMPlay. Of course the first working chips were OPL/OPL2/OPL3. I remembered that Meka had OPL-Support, too and wrote my OPLL->OPL-Mapper. It sounded quite good, but I was unable to get the PSG and the YM2413 of Space Harrier Theme in sync. So a PSG->OPL-Mapper followed. Later I ported Meka's OPL Mapper, because I liked the bass of some Phantasy Star tunes. + + +Credits +======= +This program was written by Valley Bell. + +- almost all software emulators are from MAME (http://mamedev.org) +- EMU2413 and Gens YM2612 were ported from Maxim's in_vgm +- the YMF278B core was ported from openMSX +- zlib compression by Jean-loup Gailly and Mark Adler is used +- all custom OPL Mappers were written using MAME software emulators and the OPL2/3 programming guides by Jeffrey S. Lee and Vladimir Arnost +- one YM2413 OPL Mapper was ported from MEKA. +- the RF5C164 and PWM cores were ported from Gens/GS +- the MAME YM2612 core was fixed with the help of Blargg's MAME YM2612 fix and Genesis Plus GX' YM2612 core +- AdLibEmu (OPL2 and OPL3 core) was ported from DOSBox +- The default HuC6280 core is from Ootake. +- EMU2149, the alternative NES APU core and the NES FDS core were ported from rainwarrior's NSFPlay. +- the WonderSwan core was ported from in_wsr +- Virtual Boy VSU core was ported from vbjin and originates from mednafen diff --git a/Frameworks/GME/vgmplay/VGMPlayUI.c b/Frameworks/GME/vgmplay/VGMPlayUI.c new file mode 100644 index 000000000..1aad2d786 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlayUI.c @@ -0,0 +1,2672 @@ +// TODO: Check codepage stuff (SetConsoleCP) - it looks like I don't need printc anymore +// VGMPlayUI.c: C Source File for the Console User Interface + +// Note: In order to make MS VC6 NOT crash when using fprintf with stdout, stderr, etc. +// if linked to msvcrt.lib, the following project setting is important: +// C/C++ -> Code Generation -> Runtime libraries: Multithreaded DLL + +#include +#include +#include +#include +#include +#include // for setlocale +#include "stdbool.h" +#include + +#ifdef WIN32 +#include +#include +#else +#include // for PATH_MAX +#include +#include // for STDIN_FILENO and usleep() +#include // for struct timeval in _kbhit() + +#define Sleep(msec) usleep(msec * 1000) +#define _vsnwprintf vswprintf +#endif + +#define printerr(x) fprintf(stderr, x) + +#include "chips/mamedef.h" + +#include "Stream.h" +#include "VGMPlay.h" +#include "VGMPlay_Intf.h" + +#ifdef XMAS_EXTRA +#include "XMasFiles/XMasBonus.h" +#endif +#ifdef WS_DEMO +#include "XMasFiles/SWJ-SQRC01_1C.h" +#endif + +#ifndef WIN32 +void WaveOutLinuxCallBack(void); +#endif + +#ifdef WIN32 +#define DIR_CHR '\\' +#define DIR_STR "\\" +#define QMARK_CHR '\"' +#else +#define DIR_CHR '/' +#define DIR_STR "/" +#define QMARK_CHR '\'' + +#ifndef SHARE_PREFIX +#define SHARE_PREFIX "/usr/local" +#endif + +#endif + +#define APP_NAME "VGM Player" +#define APP_NAME_L L"VGM Player" + + +int main(int argc, char* argv[]); +static void RemoveNewLines(char* String); +static void RemoveQuotationMarks(char* String); +static char* GetLastDirSeparator(const char* FilePath); +static bool IsAbsolutePath(const char* FilePath); +static char* GetFileExtention(const char* FilePath); +static void StandardizeDirSeparators(char* FilePath); +#ifdef WIN32 +static void WinNT_Check(void); +#endif +static char* GetAppFileName(void); +static void cls(void); +#ifndef WIN32 +static void changemode(bool); +static int _kbhit(void); +static int _getch(void); +#endif +static INT8 stricmp_u(const char *string1, const char *string2); +static INT8 strnicmp_u(const char *string1, const char *string2, size_t count); +static void ReadOptions(const char* AppName); +static bool GetBoolFromStr(const char* TextStr); +#if defined(XMAS_EXTRA) || defined(WS_DEMO) +static bool XMas_Extra(char* FileName, bool Mode); +#endif +#ifndef WIN32 +static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr); +#endif +static bool OpenPlayListFile(const char* FileName); +static bool OpenMusicFile(const char* FileName); +extern bool OpenVGMFile(const char* FileName); +extern bool OpenOtherFile(const char* FileName); + +//#ifdef WIN32 +//static void printc(const char* format, ...); +//#else +#define printc printf +//#endif +static void wprintc(const wchar_t* format, ...); +static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock); +static const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag); +static void ShowVGMTag(void); + +static void PlayVGM_UI(void); +INLINE INT8 sign(double Value); +INLINE long int Round(double Value); +INLINE double RoundSpecial(double Value, double RoundTo); +static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate); + + +// Options Variables +extern UINT32 SampleRate; // Note: also used by some sound cores to + // determinate the chip sample rate + +extern UINT32 VGMPbRate; +extern UINT32 VGMMaxLoop; +extern UINT32 CMFMaxLoop; +UINT32 FadeTimeN; // normal fade time +UINT32 FadeTimePL; // in-playlist fade time +extern UINT32 FadeTime; +UINT32 PauseTimeJ; // Pause Time for Jingles +UINT32 PauseTimeL; // Pause Time for Looping Songs +extern UINT32 PauseTime; +static UINT8 Show95Cmds; + +extern float VolumeLevel; +extern bool SurroundSound; +extern bool FadeRAWLog; +static UINT8 LogToWave; +//extern bool FullBufFill; +extern bool PauseEmulate; +extern bool DoubleSSGVol; +static UINT16 ForceAudioBuf; + +extern UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both +extern UINT8 CHIP_SAMPLING_MODE; +extern INT32 CHIP_SAMPLE_RATE; + +extern UINT16 FMPort; +extern bool UseFM; +extern bool FMForce; +//extern bool FMAccurate; +extern bool FMBreakFade; +extern float FMVol; + +extern CHIPS_OPTION ChipOpts[0x02]; + + +extern bool ThreadPauseEnable; +extern volatile bool ThreadPauseConfrm; +extern bool ThreadNoWait; // don't reset the timer +extern UINT16 AUDIOBUFFERU; +extern UINT32 SMPL_P_BUFFER; +extern char SoundLogFile[MAX_PATH]; + +extern UINT8 OPL_MODE; +extern UINT8 OPL_CHIPS; +//extern bool WINNT_MODE; +UINT8 NEED_LARGE_AUDIOBUFS; + +extern char* AppPaths[8]; +static char AppPathBuffer[MAX_PATH * 2]; + +static char PLFileBase[MAX_PATH]; +static char PLFileName[MAX_PATH]; +static UINT32 PLFileCount; +static char** PlayListFile; +static UINT32 CurPLFile; +static UINT8 NextPLCmd; +static UINT8 PLMode; // set to 1 to show Playlist text +static bool FirstInit; +extern bool AutoStopSkip; + +static char VgmFileName[MAX_PATH]; +static UINT8 FileMode; +extern VGM_HEADER VGMHead; +extern UINT32 VGMDataLen; +extern UINT8* VGMData; +extern GD3_TAG VGMTag; +static PreferJapTag; + +extern volatile bool PauseThread; +static bool StreamStarted; + +extern float MasterVol; + +extern UINT32 VGMPos; +extern INT32 VGMSmplPos; +extern INT32 VGMSmplPlayed; +extern INT32 VGMSampleRate; +extern UINT32 BlocksSent; +extern UINT32 BlocksPlayed; +static bool IsRAWLog; +extern bool EndPlay; +extern bool PausePlay; +extern bool FadePlay; +extern bool ForceVGMExec; +extern UINT8 PlayingMode; + +extern UINT32 PlayingTime; + +extern UINT32 FadeStart; +extern UINT32 VGMMaxLoopM; +extern UINT32 VGMCurLoop; +extern float VolumeLevelM; +bool ErrorHappened; // used by VGMPlay.c and VGMPlay_AddFmts.c +extern float FinalVol; +extern bool ResetPBTimer; + +#ifndef WIN32 +static struct termios oldterm; +static bool termmode; +#endif + +UINT8 CmdList[0x100]; + +//extern UINT8 DISABLE_YMZ_FIX; +extern UINT8 IsVGMInit; +extern UINT16 Last95Drum; // for optvgm debugging +extern UINT16 Last95Max; // for optvgm debugging +extern UINT32 Last95Freq; // for optvgm debugging + +static bool PrintMSHours; + +int main(int argc, char* argv[]) +{ + int argbase; + int ErrRet; + char* AppName; +#if defined(XMAS_EXTRA) || defined(WS_DEMO) + bool XMasEnable; +#endif + char* AppPathPtr; + const char* StrPtr; + const char* FileExt; + UINT8 CurPath; + UINT32 ChrPos; + INT32 OldCP; + + // set locale to "current system locale" + // (makes Unicode characters (like umlauts) work under Linux and fixes some + // Unicode -> ANSI conversions) + setlocale(LC_CTYPE, ""); + +#ifndef WIN32 + tcgetattr(STDIN_FILENO, &oldterm); + termmode = false; +#endif + + if (argc > 1) + { + if (! stricmp_u(argv[1], "-v") || ! stricmp_u(argv[1], "--version")) + { + printf("VGMPlay %s" +#if defined(APLHA) + " alpha" +#elif defined(BETA) + " beta" +#endif + ", supports VGM %s\n", VGMPLAY_VER_STR, VGM_VER_STR); + return 0; + } + } + +#ifdef SET_CONSOLE_TITLE +#ifdef WIN32 + SetConsoleTitle(APP_NAME); // Set Windows Console Title +#else + printf("\x1B]0;%s\x07", APP_NAME); // Set xterm/rxvt Terminal Title +#endif +#endif + + printf(APP_NAME); +#ifdef XMAS_EXTRA + printf(" - XMas Release"); +#endif + printf("\n----------\n"); + + //if (argv[0x00] == NULL) + // printf("Argument \"Application-Name\" is NULL!\n"); + + // Warning! It's dangerous to use Argument 0! + // AppName may be "vgmplay" instead of "vgmplay.exe" + + VGMPlay_Init(); + + // Note: Paths are checked from last to first. + CurPath = 0x00; + AppPathPtr = AppPathBuffer; +#ifndef WIN32 + // Path 1: global share directory + AppPaths[CurPath] = SHARE_PREFIX "/share/vgmplay/"; + CurPath ++; +#endif + + // Path 2: exe's directory + AppName = GetAppFileName(); // "C:\VGMPlay\VGMPlay.exe" + // Note: GetAppFileName always returns native directory separators. + StrPtr = strrchr(AppName, DIR_CHR); + if (StrPtr != NULL) + { + ChrPos = StrPtr + 1 - AppName; + strncpy(AppPathPtr, AppName, ChrPos); + AppPathPtr[ChrPos] = 0x00; // "C:\VGMPlay\" + AppPaths[CurPath] = AppPathPtr; + CurPath ++; + AppPathPtr += ChrPos + 1; + } + +#ifndef WIN32 + // Path 3: home directory + StrPtr = getenv("XDG_CONFIG_HOME"); + if (StrPtr != NULL && StrPtr[0] == '\0') + { + strcpy(AppPathPtr, StrPtr); + } + else + { + StrPtr = getenv("HOME"); + if (StrPtr != NULL) + strcpy(AppPathPtr, StrPtr); + else + strcpy(AppPathPtr, ""); + strcat(AppPathPtr, "/.config"); + } + strcat(AppPathPtr, "/vgmplay/"); + AppPaths[CurPath] = AppPathPtr; + CurPath ++; + AppPathPtr += strlen(AppPathPtr) + 1; +#endif + + // Path 4: working directory ("\0") + AppPathPtr[0] = '\0'; + AppPaths[CurPath] = AppPathPtr; + CurPath ++; + +#if 0 // set to 1 to print all selected search paths + CurPath = 0; + while(AppPaths[CurPath] != NULL) + { + printf("Path %u: %s\n", CurPath + 1, AppPaths[CurPath]); + CurPath ++; + } +#endif + + ReadOptions(AppName); + VGMPlay_Init2(); + + ErrRet = 0; + argbase = 0x01; + if (argc >= argbase + 0x01) + { + if (! strnicmp_u(argv[argbase], "-LogSound:", 10)) + { + LogToWave = (UINT8)strtoul(argv[argbase] + 10, NULL, 0); + argbase ++; + } + } + + printf("\nFile Name:\t"); + if (argc <= argbase) + { +#ifdef WIN32 + OldCP = GetConsoleCP(); + + // Set the Console Input Codepage to ANSI. + // The Output Codepage must be left at OEM, else the displayed characters are wrong. + ChrPos = GetACP(); + ErrRet = SetConsoleCP(ChrPos); // set input codepage + //ErrRet = SetConsoleOutputCP(ChrPos); // set output codepage (would be a bad idea) + + StrPtr = fgets(VgmFileName, MAX_PATH, stdin); + if (StrPtr == NULL) + VgmFileName[0] = '\0'; + + // Playing with the console font resets the Console Codepage to OEM, so I have to + // convert the file name in this case. + if (GetConsoleCP() == GetOEMCP()) + OemToChar(VgmFileName, VgmFileName); // OEM -> ANSI conversion + + // This fixes the display of non-ANSI characters. + ErrRet = SetConsoleCP(OldCP); + + // This codepage stuff drives me insane. + // Debug and Release build behave differently - WHAT?? + // + // There a list of behaviours. + // Debug and Release were tested by dropping a file on it and via Visual Studio. + // + // Input CP 850, Output CP 850 + // Debug build: Dynamite D³x + // Release build: Dynamite Düx + // Input CP 1252, Output CP 850 + // Debug build: Dynamite D³x + // Release build: Dynamite D³x + // Input CP 850, Output CP 1252 + // Debug build: Dynamite D³x [tag display wrong] + // Release build: Dynamite Düx [tag display wrong] + // Input CP 1252, Output CP 1252 + // Debug build: Dynamite D³x [tag display wrong] + // Release build: Dynamite D³x [tag display wrong] +#else + StrPtr = fgets(VgmFileName, MAX_PATH, stdin); + if (StrPtr == NULL) + VgmFileName[0] = '\0'; +#endif + + RemoveNewLines(VgmFileName); + RemoveQuotationMarks(VgmFileName); + } + else + { + // The argument should already use the ANSI codepage. + strcpy(VgmFileName, argv[argbase]); + printc("%s\n", VgmFileName); + } + if (! strlen(VgmFileName)) + goto ExitProgram; + StandardizeDirSeparators(VgmFileName); + +#if defined(XMAS_EXTRA) || defined(WS_DEMO) + XMasEnable = XMas_Extra(VgmFileName, 0x00); +#endif + +#if 0 + { // Print hex characters of file name (for vgm-player script debugging) + const char* CurChr; + +#ifdef WIN32 + printf("Input CP: %d, Output CP: %d\n", GetConsoleCP(), GetConsoleOutputCP()); +#endif + printf("VgmFileName: "); + + CurChr = VgmFileName; + while(*CurChr != '\0') + { + printf("%02X ", (UINT8)*CurChr); + CurChr ++; + } + printf("%02X\n", (UINT8)*CurChr); + _getch(); + } +#endif +#if 0 + { // strip spaces and \n (fixed bugs with vgm-player script with un-7z) + char* CurChr; + + // trim \n and spaces off + CurChr = strchr(VgmFileName, '\n'); + if (CurChr != NULL) + *CurChr = '\0'; + CurChr = VgmFileName + strlen(VgmFileName) - 1; + while(CurChr > VgmFileName && *CurChr == ' ') + *(CurChr --) = '\0'; + } +#endif + + FirstInit = true; + StreamStarted = false; + FileExt = GetFileExtention(VgmFileName); + if (FileExt == NULL || stricmp_u(FileExt, "m3u")) + PLMode = 0x00; + else + PLMode = 0x01; + + if (! PLMode) + { + PLFileCount = 0x00; + CurPLFile = 0x00; + // no Play List File + if (! OpenMusicFile(VgmFileName)) + { + printerr("Error opening the file!\n"); + if (argv[0][1] == ':') + _getch(); + ErrRet = 1; + goto ExitProgram; + } + printf("\n"); + + ErrorHappened = false; + FadeTime = FadeTimeN; + PauseTime = PauseTimeL; + PrintMSHours = (VGMHead.lngTotalSamples >= 158760000); // 44100 smpl * 60 sec * 60 min + ShowVGMTag(); + NextPLCmd = 0x80; + PlayVGM_UI(); + + CloseVGMFile(); + } + else + { + strcpy(PLFileName, VgmFileName); + if (! OpenPlayListFile(PLFileName)) + { + printerr("Error opening the playlist!\n"); + if (argv[0][1] == ':') + _getch(); + ErrRet = 1; + goto ExitProgram; + } + + for (CurPLFile = 0x00; CurPLFile < PLFileCount; CurPLFile ++) + { + if (PLMode) + { + cls(); + printf(APP_NAME); + printf("\n----------\n"); + printc("\nPlaylist File:\t%s\n", PLFileName); + printf("Playlist Entry:\t%u / %u\n", CurPLFile + 1, PLFileCount); + printc("File Name:\t%s\n", PlayListFile[CurPLFile]); + } + + if (IsAbsolutePath(PlayListFile[CurPLFile])) + { + strcpy(VgmFileName, PlayListFile[CurPLFile]); + } + else + { + strcpy(VgmFileName, PLFileBase); + strcat(VgmFileName, PlayListFile[CurPLFile]); + } + + if (! OpenMusicFile(VgmFileName)) + { + printf("Error opening the file!\n"); + _getch(); + while(_kbhit()) + _getch(); + continue; + } + printf("\n"); + + ErrorHappened = false; + if (CurPLFile < PLFileCount - 1) + FadeTime = FadeTimePL; + else + FadeTime = FadeTimeN; + PauseTime = VGMHead.lngLoopOffset ? PauseTimeL : PauseTimeJ; + PrintMSHours = (VGMHead.lngTotalSamples >= 158760000); + ShowVGMTag(); + NextPLCmd = 0x00; + PlayVGM_UI(); + + CloseVGMFile(); + + if (ErrorHappened) + { + if (_kbhit()) + _getch(); + _getch(); + ErrorHappened = false; + } + if (NextPLCmd == 0xFF) + break; + else if (NextPLCmd == 0x01) + CurPLFile -= 0x02; // Jump to last File (-2 + 1 = -1) + } + } + + if (ErrorHappened && argv[0][1] == ':') + { + if (_kbhit()) + _getch(); + _getch(); + } + +#ifdef _DEBUG + printf("Press any key ..."); + _getch(); +#endif + +ExitProgram: +#if defined(XMAS_EXTRA) || defined(WS_DEMO) + if (XMasEnable) + XMas_Extra(VgmFileName, 0x01); +#endif +#ifndef WIN32 + changemode(false); +#ifdef SET_CONSOLE_TITLE +// printf("\x1B]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\x07", APP_NAME); // Reset xterm/rxvt Terminal Title +#endif +#endif + VGMPlay_Deinit(); + free(AppName); + + return ErrRet; +} + +static void RemoveNewLines(char* String) +{ + char* StrPtr; + + StrPtr = String + strlen(String) - 1; + while(StrPtr >= String && (UINT8)*StrPtr < 0x20) + { + *StrPtr = '\0'; + StrPtr --; + } + + return; +} + +static void RemoveQuotationMarks(char* String) +{ + UINT32 StrLen; + char* EndQMark; + + if (String[0x00] != QMARK_CHR) + return; + + StrLen = strlen(String); + memmove(String, String + 0x01, StrLen); // Remove first char + EndQMark = strrchr(String, QMARK_CHR); + if (EndQMark != NULL) + *EndQMark = 0x00; // Remove last Quot.-Mark + + return; +} + +static char* GetLastDirSeparator(const char* FilePath) +{ + char* SepPos1; + char* SepPos2; + + SepPos1 = strrchr(FilePath, '/'); + SepPos2 = strrchr(FilePath, '\\'); + if (SepPos1 < SepPos2) + return SepPos2; + else + return SepPos1; +} + +static bool IsAbsolutePath(const char* FilePath) +{ +#ifdef WIN32 + if (FilePath[0] == '\0') + return false; // empty string + if (FilePath[1] == ':') + return true; // Device Path: C:\path + if (! strncmp(FilePath, "\\\\", 2)) + return true; // Network Path: \\computername\path +#else + if (FilePath[0] == '/') + return true; // absolute UNIX path +#endif + return false; +} + +static char* GetFileExtention(const char* FilePath) +{ + char* DirSepPos; + char* ExtDotPos; + + DirSepPos = GetLastDirSeparator(FilePath); + if (DirSepPos == NULL) + DirSepPos = (char*)FilePath; + ExtDotPos = strrchr(DirSepPos, '.'); + if (ExtDotPos == NULL) + return NULL; + else + return ExtDotPos + 1; +} + +static void StandardizeDirSeparators(char* FilePath) +{ + char* CurChr; + + CurChr = FilePath; + while(*CurChr != '\0') + { + if (*CurChr == '\\' || *CurChr == '/') + *CurChr = DIR_CHR; + CurChr ++; + } + + return; +} + +#ifdef WIN32 +static void WinNT_Check(void) +{ + OSVERSIONINFO VerInf; + + VerInf.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&VerInf); + //WINNT_MODE = (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT); + + /* Following Systems need larger Audio Buffers: + - Windows 95 (500+ ms) + - Windows Vista (200+ ms) + Tested Systems: + - Windows 95B + - Windows 98 SE + - Windows 2000 + - Windows XP (32-bit) + - Windows Vista (32-bit) + - Windows 7 (64-bit) + */ + + NEED_LARGE_AUDIOBUFS = 0; + if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + { + if (VerInf.dwMajorVersion == 4 && VerInf.dwMinorVersion == 0) + NEED_LARGE_AUDIOBUFS = 50; // Windows 95 + } + else if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if (VerInf.dwMajorVersion == 6 && VerInf.dwMinorVersion == 0) + NEED_LARGE_AUDIOBUFS = 20; // Windows Vista + } + + return; +} +#endif + +static char* GetAppFileName(void) +{ + char* AppPath; + int RetVal; + + AppPath = (char*)malloc(MAX_PATH * sizeof(char)); +#ifdef WIN32 + RetVal = GetModuleFileName(NULL, AppPath, MAX_PATH); + if (! RetVal) + AppPath[0] = '\0'; +#else + RetVal = readlink("/proc/self/exe", AppPath, MAX_PATH); + if (RetVal == -1) + AppPath[0] = '\0'; +#endif + + return AppPath; +} + +static void cls(void) +{ +#ifdef WIN32 + // CLS-Function from the MSDN Help + HANDLE hConsole; + COORD coordScreen = {0, 0}; + BOOL bSuccess; + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD dwConSize; + + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + + // get the number of character cells in the current buffer + bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi); + + // fill the entire screen with blanks + dwConSize = csbi.dwSize.X * csbi.dwSize.Y; + bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen, + &cCharsWritten); + + // get the current text attribute + //bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi); + + // now set the buffer's attributes accordingly + //bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, + // &cCharsWritten); + + // put the cursor at (0, 0) + bSuccess = SetConsoleCursorPosition(hConsole, coordScreen); +#else + system("clear"); +#endif + + return; +} + +#ifndef WIN32 + +static void changemode(bool dir) +{ + static struct termios newterm; + + if (termmode == dir) + return; + + if (dir) + { + newterm = oldterm; + newterm.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newterm); + } + else + { + tcsetattr(STDIN_FILENO, TCSANOW, &oldterm); + } + termmode = dir; + + return; +} + +static int _kbhit(void) +{ + struct timeval tv; + fd_set rdfs; + int kbret; + bool needchg; + + needchg = (! termmode); + if (needchg) + changemode(true); + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO(&rdfs); + FD_SET(STDIN_FILENO, &rdfs); + + select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv); + kbret = FD_ISSET(STDIN_FILENO, &rdfs); + if (needchg) + changemode(false); + + return kbret; +} + +static int _getch(void) +{ + int ch; + bool needchg; + + needchg = (! termmode); + if (needchg) + changemode(true); + ch = getchar(); + if (needchg) + changemode(false); + + return ch; +} +#endif + +static INT8 stricmp_u(const char *string1, const char *string2) +{ + // my own stricmp, because VC++6 doesn't find _stricmp when compiling without + // standard libraries + const char* StrPnt1; + const char* StrPnt2; + char StrChr1; + char StrChr2; + + StrPnt1 = string1; + StrPnt2 = string2; + while(true) + { + StrChr1 = toupper(*StrPnt1); + StrChr2 = toupper(*StrPnt2); + + if (StrChr1 < StrChr2) + return -1; + else if (StrChr1 > StrChr2) + return +1; + if (StrChr1 == 0x00) + return 0; + + StrPnt1 ++; + StrPnt2 ++; + } + + return 0; +} + +static INT8 strnicmp_u(const char *string1, const char *string2, size_t count) +{ + // my own strnicmp, because GCC doesn't seem to have _strnicmp + const char* StrPnt1; + const char* StrPnt2; + char StrChr1; + char StrChr2; + size_t CurChr; + + StrPnt1 = string1; + StrPnt2 = string2; + CurChr = 0x00; + while(CurChr < count) + { + StrChr1 = toupper(*StrPnt1); + StrChr2 = toupper(*StrPnt2); + + if (StrChr1 < StrChr2) + return -1; + else if (StrChr1 > StrChr2) + return +1; + if (StrChr1 == 0x00) + return 0; + + StrPnt1 ++; + StrPnt2 ++; + CurChr ++; + } + + return 0; +} + +static void ReadOptions(const char* AppName) +{ + const UINT8 CHN_COUNT[CHIP_COUNT] = + { 0x04, 0x09, 0x06, 0x08, 0x10, 0x08, 0x03, 0x00, + 0x00, 0x09, 0x09, 0x09, 0x12, 0x00, 0x0C, 0x08, + 0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00, + 0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10, + 0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20, + 0x04 + }; + const UINT8 CHN_MASK_CNT[CHIP_COUNT] = + { 0x04, 0x0E, 0x07, 0x08, 0x10, 0x08, 0x03, 0x06, + 0x06, 0x0E, 0x0E, 0x0E, 0x17, 0x18, 0x0C, 0x08, + 0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00, + 0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10, + 0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20, + 0x04 + }; + char* FileName; + FILE* hFile; + char TempStr[0x40]; + UINT32 StrLen; + UINT32 TempLng; + char* LStr; + char* RStr; + UINT8 IniSection; + UINT8 CurChip; + CHIP_OPTS* TempCOpt; + CHIP_OPTS* TempCOpt2; + UINT8 CurChn; + char* TempPnt; + bool TempFlag; + + // most defaults are set by VGMPlay_Init() + FadeTimeN = FadeTime; + PauseTimeJ = PauseTime; + PauseTimeL = 0; + Show95Cmds = 0x00; + LogToWave = 0x00; + ForceAudioBuf = 0x00; + PreferJapTag = false; + + if (AppName == NULL) + { + printerr("Argument \"Application-Path\" is NULL!\nSkip loading INI.\n"); + return; + } + + // AppName: "C:\VGMPlay\VGMPlay.exe" + RStr = strrchr(AppName, DIR_CHR); + if (RStr != NULL) + RStr ++; + else + RStr = (char*)AppName; + FileName = (char*)malloc(strlen(RStr) + 0x05); // ".ini" + 00 + strcpy(FileName, RStr); + // FileName: "VGMPlay.exe" + + RStr = GetFileExtention(FileName); + if (RStr == NULL) + { + RStr = FileName + strlen(FileName); + *RStr = '.'; + RStr ++; + } + strcpy(RStr, "ini"); + // FileName: "VGMPlay.ini" + + LStr = FileName; + FileName = FindFile(LStr); + free(LStr); + if (FileName == NULL) + { + // on Linux platforms, it searches for "vgmplay.ini" first and + // file names are case sensitive + FileName = FindFile("VGMPlay.ini"); + } + if (FileName == NULL) + { + printerr("Failed to load INI.\n"); + return; + } + hFile = fopen(FileName, "rt"); + free(FileName); + if (hFile == NULL) + { + printerr("Failed to load INI.\n"); + return; + } + + IniSection = 0x00; + while(! feof(hFile)) + { + LStr = fgets(TempStr, 0x40, hFile); + if (LStr == NULL) + break; + if (TempStr[0x00] == ';') // Comment line + continue; + + StrLen = strlen(TempStr) - 0x01; + //if (TempStr[StrLen] == '\n') + // TempStr[StrLen] = 0x00; + while(TempStr[StrLen] < 0x20) + { + TempStr[StrLen] = 0x00; + if (! StrLen) + break; + StrLen --; + } + if (! StrLen) + continue; + StrLen ++; + + LStr = &TempStr[0x00]; + while(*LStr == ' ') + LStr ++; + if (LStr[0x00] == ';') // Comment line + continue; + + if (LStr[0x00] == '[') + RStr = strchr(TempStr, ']'); + else + RStr = strchr(TempStr, '='); + if (RStr == NULL) + continue; + + if (LStr[0x00] == '[') + { + // Line pattern: [Group] + LStr ++; + RStr = strchr(TempStr, ']'); + if (RStr != NULL) + RStr[0x00] = 0x00; + + if (! stricmp_u(LStr, "General")) + { + IniSection = 0x00; + } + else + { + IniSection = 0xFF; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) + { + if (! stricmp_u(LStr, GetChipName(CurChip))) + { + IniSection = 0x80 | CurChip; + break; + } + } + if (IniSection == 0xFF) + continue; + } + } + else + { + // Line pattern: Option = Value + TempLng = RStr - TempStr; + TempStr[TempLng] = 0x00; + + // Prepare Strings (trim the spaces) + RStr = &TempStr[TempLng - 0x01]; + while(*RStr == ' ') + *(RStr --) = 0x00; + + RStr = &TempStr[StrLen - 0x01]; + while(*RStr == ' ') + *(RStr --) = 0x00; + RStr = &TempStr[TempLng + 0x01]; + while(*RStr == ' ') + RStr ++; + + switch(IniSection) + { + case 0x00: // General Sction + if (! stricmp_u(LStr, "SampleRate")) + { + SampleRate = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "PlaybackRate")) + { + VGMPbRate = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "DoubleSSGVol")) + { + DoubleSSGVol = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "PreferJapTag")) + { + PreferJapTag = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "FadeTime")) + { + FadeTimeN = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "FadeTimePL")) + { + FadeTimePL = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "JinglePause")) + { + PauseTimeJ = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "FadeRAWLogs")) + { + FadeRAWLog = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "Volume")) + { + VolumeLevel = (float)strtod(RStr, NULL); + } + else if (! stricmp_u(LStr, "LogSound")) + { + //LogToWave = GetBoolFromStr(RStr); + LogToWave = (UINT8)strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "MaxLoops")) + { + VGMMaxLoop = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "MaxLoopsCMF")) + { + CMFMaxLoop = strtoul(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "ResamplingMode")) + { + ResampleMode = (UINT8)strtol(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "ChipSmplMode")) + { + CHIP_SAMPLING_MODE = (UINT8)strtol(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "ChipSmplRate")) + { + CHIP_SAMPLE_RATE = strtol(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "AudioBuffers")) + { + ForceAudioBuf = (UINT16)strtol(RStr, NULL, 0); + if (ForceAudioBuf < 0x04) + ForceAudioBuf = 0x00; + } + else if (! stricmp_u(LStr, "SurroundSound")) + { + SurroundSound = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "EmulatePause")) + { + PauseEmulate = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "ShowStreamCmds")) + { + Show95Cmds = (UINT8)strtol(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "FMPort")) + { + FMPort = (UINT16)strtoul(RStr, NULL, 16); + } + else if (! stricmp_u(LStr, "FMForce")) + { + FMForce = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "FMVolume")) + { + FMVol = (float)strtod(RStr, NULL); + } + /*else if (! stricmp_u(LStr, "AccurateFM")) + { + FMAccurate = GetBoolFromStr(RStr); + }*/ + else if (! stricmp_u(LStr, "FMSoftStop")) + { + FMBreakFade = GetBoolFromStr(RStr); + } + break; + case 0x80: // SN76496 + case 0x81: // YM2413 + case 0x82: // YM2612 + case 0x83: // YM2151 + case 0x84: // SegaPCM + case 0x85: // RF5C68 + case 0x86: // YM2203 + case 0x87: // YM2608 + case 0x88: // YM2610 + case 0x89: // YM3812 + case 0x8A: // YM3526 + case 0x8B: // Y8950 + case 0x8C: // YMF262 + case 0x8D: // YMF278B + case 0x8E: // YMF271 + case 0x8F: // YMZ280B + case 0x90: // RF5C164 + case 0x91: // PWM + case 0x92: // AY8910 + case 0x93: // GameBoy + case 0x94: // NES + case 0x95: // MultiPCM + case 0x96: // UPD7759 + case 0x97: // OKIM6258 + case 0x98: // OKIM6295 + case 0x99: // K051649 + case 0x9A: // K054539 + case 0x9B: // HuC6280 + case 0x9C: // C140 + case 0x9D: // K053260 + case 0x9E: // Pokey + case 0x9F: // QSound + case 0xA0: // SCSP + case 0xA1: // WonderSwan + case 0xA2: // VSU + case 0xA3: // SAA1099 + case 0xA4: // ES5503 + case 0xA5: // ES5506 + case 0xA6: // X1_010 + case 0xA7: // C352 + case 0xA8: // GA20 + CurChip = IniSection & 0x7F; + TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00] + CurChip; + + if (! stricmp_u(LStr, "Disabled")) + { + TempCOpt->Disabled = GetBoolFromStr(RStr); + } + else if (! stricmp_u(LStr, "EmulatorType")) + { + TempCOpt->EmuCore = (UINT8)strtol(RStr, NULL, 0); + } + else if (! stricmp_u(LStr, "MuteMask")) + { + if (! CHN_COUNT[CurChip]) + break; // must use MuteMaskFM and MuteMask??? + TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0); + if (CHN_MASK_CNT[CurChip] < 0x20) + TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1; + } + else if (! strnicmp_u(LStr, "MuteCh", 0x06)) + { + if (! CHN_COUNT[CurChip]) + break; // must use MuteFM and Mute??? + CurChn = (UINT8)strtol(LStr + 0x06, &TempPnt, 0); + if (TempPnt == NULL || *TempPnt) + break; + if (CurChn >= CHN_COUNT[CurChip]) + break; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute1 &= ~(0x01 << CurChn); + TempCOpt->ChnMute1 |= TempFlag << CurChn; + } + else + { + switch(CurChip) + { + //case 0x00: // SN76496 + case 0x02: // YM2612 + if (! stricmp_u(LStr, "MuteDAC")) + { + CurChn = 0x06; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute1 &= ~(0x01 << CurChn); + TempCOpt->ChnMute1 |= TempFlag << CurChn; + } + else if (! stricmp_u(LStr, "DACHighpass")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 0); + TempCOpt->SpecialFlags |= TempFlag << 0; + } + else if (! stricmp_u(LStr, "SSG-EG")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 1); + TempCOpt->SpecialFlags |= TempFlag << 1; + } + else if (! stricmp_u(LStr, "PseudoStereo")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 2); + TempCOpt->SpecialFlags |= TempFlag << 2; + } + break; + //case 0x03: // YM2151 + //case 0x04: // SegaPCM + //case 0x05: // RF5C68 + case 0x06: // YM2203 + if (! stricmp_u(LStr, "DisableAY")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 0); + TempCOpt->SpecialFlags |= TempFlag << 0; + } + break; + case 0x07: // YM2608 + case 0x08: // YM2610 + if (! stricmp_u(LStr, "DisableAY")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 0); + TempCOpt->SpecialFlags |= TempFlag << 0; + } + else if (! stricmp_u(LStr, "MuteMask_FM")) + { + TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0); + TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1; + } + else if (! stricmp_u(LStr, "MuteMask_PCM")) + { + TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0); + TempCOpt->ChnMute2 &= (1 << (CHN_MASK_CNT[CurChip] + 1)) - 1; + } + else if (! strnicmp_u(LStr, "MuteFMCh", 0x08)) + { + CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); + if (TempPnt == NULL || *TempPnt) + break; + if (CurChn >= CHN_COUNT[CurChip]) + break; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute1 &= ~(0x01 << CurChn); + TempCOpt->ChnMute1 |= TempFlag << CurChn; + } + else if (! strnicmp_u(LStr, "MutePCMCh", 0x08)) + { + CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); + if (TempPnt == NULL || *TempPnt) + break; + if (CurChn >= CHN_COUNT[CurChip]) + break; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute2 &= ~(0x01 << CurChn); + TempCOpt->ChnMute2 |= TempFlag << CurChn; + } + else if (! stricmp_u(LStr, "MuteDT")) + { + CurChn = 0x06; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute2 &= ~(0x01 << CurChn); + TempCOpt->ChnMute2 |= TempFlag << CurChn; + } + break; + case 0x01: // YM2413 + case 0x09: // YM3812 + case 0x0A: // YM3526 + case 0x0B: // Y8950 + case 0x0C: // YMF262 + CurChn = 0xFF; + if (! stricmp_u(LStr, "MuteBD")) + CurChn = 0x00; + else if (! stricmp_u(LStr, "MuteSD")) + CurChn = 0x01; + else if (! stricmp_u(LStr, "MuteTOM")) + CurChn = 0x02; + else if (! stricmp_u(LStr, "MuteTC")) + CurChn = 0x03; + else if (! stricmp_u(LStr, "MuteHH")) + CurChn = 0x04; + else if (CurChip == 0x0B && ! stricmp_u(LStr, "MuteDT")) + CurChn = 0x05; + if (CurChn != 0xFF) + { + if (CurChip < 0x0C) + CurChn += 9; + else + CurChn += 18; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute1 &= ~(0x01 << CurChn); + TempCOpt->ChnMute1 |= TempFlag << CurChn; + } + break; + case 0x0D: // YMF278B + if (! stricmp_u(LStr, "MuteMask_FM")) + { + TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0); + TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip - 0x01]) - 1; + } + else if (! stricmp_u(LStr, "MuteMask_WT")) + { + TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0); + TempCOpt->ChnMute2 &= (1 << CHN_MASK_CNT[CurChip]) - 1; + } + else if (! strnicmp_u(LStr, "MuteFMCh", 0x08)) + { + CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); + if (TempPnt == NULL || *TempPnt) + break; + if (CurChn >= CHN_COUNT[CurChip - 0x01]) + break; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute1 &= ~(0x01 << CurChn); + TempCOpt->ChnMute1 |= TempFlag << CurChn; + } + else if (! strnicmp_u(LStr, "MuteFM", 0x06)) + { + CurChn = 0xFF; + if (! stricmp_u(LStr + 6, "BD")) + CurChn = 0x00; + else if (! stricmp_u(LStr + 6, "SD")) + CurChn = 0x01; + else if (! stricmp_u(LStr + 6, "TOM")) + CurChn = 0x02; + else if (! stricmp_u(LStr + 6, "TC")) + CurChn = 0x03; + else if (! stricmp_u(LStr + 6, "HH")) + CurChn = 0x04; + if (CurChn != 0xFF) + { + CurChn += 18; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute1 &= ~(0x01 << CurChn); + TempCOpt->ChnMute1 |= TempFlag << CurChn; + } + } + else if (! strnicmp_u(LStr, "MuteWTCh", 0x08)) + { + CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0); + if (TempPnt == NULL || *TempPnt) + break; + if (CurChn >= CHN_MASK_CNT[CurChip]) + break; + TempFlag = GetBoolFromStr(RStr); + TempCOpt->ChnMute2 &= ~(0x01 << CurChn); + TempCOpt->ChnMute2 |= TempFlag << CurChn; + } + break; + //case 0x0E: // YMF271 + //case 0x0F: // YMZ280B + /*if (! stricmp_u(LStr, "DisableFix")) + { + DISABLE_YMZ_FIX = GetBoolFromStr(RStr); + } + break;*/ + //case 0x10: // RF5C164 + //case 0x11: // PWM + //case 0x12: // AY8910 + case 0x13: // GameBoy + if (! stricmp_u(LStr, "BoostWaveChn")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 0); + TempCOpt->SpecialFlags |= TempFlag << 0; + } + else if (! stricmp_u(LStr, "LowerNoiseChn")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 1); + TempCOpt->SpecialFlags |= TempFlag << 1; + } + else if (! stricmp_u(LStr, "Inaccurate")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 2); + TempCOpt->SpecialFlags |= TempFlag << 2; + } + break; + case 0x14: // NES + if (! stricmp_u(LStr, "SharedOpts")) + { + // 2 bits + TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03; + TempCOpt->SpecialFlags &= ~(0x03 << 0) & 0x7FFF; + TempCOpt->SpecialFlags |= TempLng << 0; + } + else if (! stricmp_u(LStr, "APUOpts")) + { + // 2 bits + TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03; + TempCOpt->SpecialFlags &= ~(0x03 << 2) & 0x7FFF; + TempCOpt->SpecialFlags |= TempLng << 2; + } + else if (! stricmp_u(LStr, "DMCOpts")) + { + // 8 bits (6 bits used) + TempLng = (UINT32)strtol(RStr, NULL, 0) & 0xFF; + TempCOpt->SpecialFlags &= ~(0xFF << 4) & 0x7FFF; + TempCOpt->SpecialFlags |= TempLng << 4; + } + else if (! stricmp_u(LStr, "FDSOpts")) + { + // 1 bit + TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x01; + TempCOpt->SpecialFlags &= ~(0x01 << 12) & 0x7FFF; + TempCOpt->SpecialFlags |= TempLng << 12; + } + break; + case 0x17: // OKIM6258 + if (! stricmp_u(LStr, "Enable10Bit")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 0); + TempCOpt->SpecialFlags |= TempFlag << 0; + } + else if (! stricmp_u(LStr, "RemoveDCOfs")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 1); + TempCOpt->SpecialFlags |= TempFlag << 1; + } + break; + case 0x20: // SCSP + if (! stricmp_u(LStr, "BypassDSP")) + { + TempFlag = GetBoolFromStr(RStr); + TempCOpt->SpecialFlags &= ~(0x01 << 0); + TempCOpt->SpecialFlags |= TempFlag << 0; + } + break; + } + } + break; + case 0xFF: // Dummy Section + break; + } + } + } + + TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00]; + TempCOpt2 = (CHIP_OPTS*)&ChipOpts[0x01]; + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++, TempCOpt2 ++) + { + TempCOpt2->Disabled = TempCOpt->Disabled; + TempCOpt2->EmuCore = TempCOpt->EmuCore; + TempCOpt2->SpecialFlags = TempCOpt->SpecialFlags; + TempCOpt2->ChnMute1 = TempCOpt->ChnMute1; + TempCOpt2->ChnMute2 = TempCOpt->ChnMute2; + TempCOpt2->ChnMute3 = TempCOpt->ChnMute3; + } + + fclose(hFile); + +#ifdef WIN32 + WinNT_Check(); +#endif + if (CHIP_SAMPLE_RATE <= 0) + CHIP_SAMPLE_RATE = SampleRate; + + return; +} + +static bool GetBoolFromStr(const char* TextStr) +{ + if (! stricmp_u(TextStr, "True")) + return true; + else if (! stricmp_u(TextStr, "False")) + return false; + else + return strtol(TextStr, NULL, 0) ? true : false; +} + +#if defined(XMAS_EXTRA) || defined(WS_DEMO) +static bool XMas_Extra(char* FileName, bool Mode) +{ + char* FileTitle; + const UINT8* XMasData; + UINT32 XMasSize; + FILE* hFile; + + if (! Mode) + { // Prepare Mode + FileTitle = NULL; + XMasData = NULL; +#ifdef XMAS_EXTRA + if (! stricmp_u(FileName, "WEWISH") + { + FileTitle = "WEWISH.CMF"; + XMasSize = sizeof(WEWISH_CMF); + XMasData = WEWISH_CMF; + } + else if (! stricmp_u(FileName, "tim7") + { + FileTitle = "lem_tim7.vgz"; + XMasSize = sizeof(TIM7_VGZ); + XMasData = TIM7_VGZ; + } + else if (! stricmp_u(FileName, "jingleb") + { + FileTitle = "lxmas_jb.dro"; + XMasSize = sizeof(JB_DRO); + XMasData = JB_DRO; + } + else if (! stricmp_u(FileName, "rudolph") + { + FileTitle = "rudolph.dro"; + XMasSize = sizeof(RODOLPH_DRO); + XMasData = RODOLPH_DRO; + } + else if (! stricmp_u(FileName, "clyde")) + { + FileTitle = "clyde1_1.dro"; + XMasSize = sizeof(clyde1_1_dro); + XMasData = clyde1_1_dro; + } +#elif defined(WS_DEMO) + if (! stricmp_u(FileName, "wswan")) + { + FileTitle = "SWJ-SQRC01_1C.vgz"; + XMasSize = sizeof(FF1ws_1C); + XMasData = FF1ws_1C; + } +#endif + + if (XMasData) + { +#ifdef WIN32 + GetEnvironmentVariable("Temp", FileName, MAX_PATH); +#else + strcpy(FileName, "/tmp"); +#endif + strcat(FileName, DIR_STR); + if (FileTitle == NULL) + FileTitle = "XMas.dat"; + strcat(FileName, FileTitle); + + hFile = fopen(FileName, "wb"); + if (hFile == NULL) + { + FileName[0x00] = 0x00; + printerr("Critical XMas-Error!\n"); + return false; + } + fwrite(XMasData, 0x01, XMasSize, hFile); + fclose(hFile); + } + else + { + FileName = NULL; + return false; + } + } + else + { // Unprepare Mode + if (! remove(FileName)) + return false; + // btw: it's intentional that the user can grab the file from the temp-folder + } + + return true; +} +#endif + +#ifndef WIN32 +static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr) +{ + const UINT16 CONV_TBL[0x20] = + { 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87 + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, // 88-8F + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97 + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178}; // 98-9F + UINT32 StrLen; + UINT16 UnicodeChr; + char* DstPtr; + const char* SrcPtr; + + SrcPtr = SrcStr; + StrLen = 0x00; + while(*SrcPtr != '\0') + { + if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0) + UnicodeChr = *SrcPtr; + else + UnicodeChr = CONV_TBL[*SrcPtr - 0x80]; + if (UnicodeChr < 0x0080) + StrLen ++; + else if (UnicodeChr < 0x0800) + StrLen += 2; + else + StrLen += 3; + SrcPtr ++; + } + + *DstStr = (char*)malloc((StrLen + 0x01) * sizeof(char)); + SrcPtr = SrcStr; + DstPtr = *DstStr; + while(*SrcPtr != '\0') + { + if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0) + UnicodeChr = (unsigned char)*SrcPtr; + else + UnicodeChr = CONV_TBL[*SrcPtr - 0x80]; + if (UnicodeChr < 0x0080) + { + *DstPtr = UnicodeChr & 0xFF; + DstPtr ++; + } + else if (UnicodeChr < 0x0800) + { + DstPtr[0x00] = 0xC0 | ((UnicodeChr >> 6) & 0x1F); + DstPtr[0x01] = 0x80 | ((UnicodeChr >> 0) & 0x3F); + DstPtr += 0x02; + } + else + { + DstPtr[0x00] = 0xE0 | ((UnicodeChr >> 12) & 0x0F); + DstPtr[0x01] = 0x80 | ((UnicodeChr >> 6) & 0x3F); + DstPtr[0x02] = 0x80 | ((UnicodeChr >> 0) & 0x3F); + DstPtr += 0x03; + } + SrcPtr ++; + } + *DstPtr = '\0'; + + return; +} +#endif + +static bool OpenPlayListFile(const char* FileName) +{ + const char M3UV2_HEAD[] = "#EXTM3U"; + const char M3UV2_META[] = "#EXTINF:"; + const UINT8 UTF8_SIG[] = {0xEF, 0xBB, 0xBF}; + UINT32 METASTR_LEN; + size_t RetVal; + + FILE* hFile; + UINT32 LineNo; + bool IsV2Fmt; + UINT32 PLAlloc; + char TempStr[0x1000]; // 4096 chars should be enough + char* RetStr; + bool IsUTF8; + + hFile = fopen(FileName, "rt"); + if (hFile == NULL) + return false; + + RetVal = fread(TempStr, 0x01, 0x03, hFile); + if (RetVal >= 0x03) + IsUTF8 = ! memcmp(TempStr, UTF8_SIG, 0x03); + else + IsUTF8 = false; + + rewind(hFile); + + PLAlloc = 0x0100; + PLFileCount = 0x00; + LineNo = 0x00; + IsV2Fmt = false; + METASTR_LEN = strlen(M3UV2_META); + PlayListFile = (char**)malloc(PLAlloc * sizeof(char*)); + while(! feof(hFile)) + { + RetStr = fgets(TempStr, 0x1000, hFile); + if (RetStr == NULL) + break; + //RetStr = strchr(TempStr, 0x0D); + //if (RetStr) + // *RetStr = 0x00; // remove NewLine-Character + RetStr = TempStr + strlen(TempStr) - 0x01; + while(RetStr >= TempStr && *RetStr < 0x20) + { + *RetStr = 0x00; // remove NewLine-Characters + RetStr --; + } + if (! strlen(TempStr)) + continue; + + if (! LineNo) + { + if (! strcmp(TempStr, M3UV2_HEAD)) + { + IsV2Fmt = true; + LineNo ++; + continue; + } + } + if (IsV2Fmt) + { + if (! strncmp(TempStr, M3UV2_META, METASTR_LEN)) + { + // Ignore Metadata of m3u Version 2 + LineNo ++; + continue; + } + } + + if (PLFileCount >= PLAlloc) + { + PLAlloc += 0x0100; + PlayListFile = (char**)realloc(PlayListFile, PLAlloc * sizeof(char*)); + } + + // TODO: + // - supprt UTF-8 m3us under Windows + // - force IsUTF8 via Commandline +#ifdef WIN32 + // Windows uses the 1252 Codepage by default + PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char)); + strcpy(PlayListFile[PLFileCount], TempStr); +#else + if (! IsUTF8) + { + // Most recent Linux versions use UTF-8, so I need to convert all strings. + ConvertCP1252toUTF8(&PlayListFile[PLFileCount], TempStr); + } + else + { + PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char)); + strcpy(PlayListFile[PLFileCount], TempStr); + } +#endif + StandardizeDirSeparators(PlayListFile[PLFileCount]); + PLFileCount ++; + LineNo ++; + } + + fclose(hFile); + + RetStr = GetLastDirSeparator(FileName); + if (RetStr != NULL) + { + RetStr ++; + strncpy(PLFileBase, FileName, RetStr - FileName); + PLFileBase[RetStr - FileName] = '\0'; + StandardizeDirSeparators(PLFileBase); + } + else + { + strcpy(PLFileBase, ""); + } + + return true; +} + +static bool OpenMusicFile(const char* FileName) +{ + if (OpenVGMFile(FileName)) + return true; + else if (OpenOtherFile(FileName)) + return true; + + return false; +} + +/*#ifdef WIN32 +// "printc" initially meant "print correct, though "print console" would also make sense ;) +static void printc(const char* format, ...) +{ + int RetVal; + UINT32 BufSize; + char* printbuf; + va_list arg_list; + + BufSize = 0x00; + printbuf = NULL; + do + { + BufSize += 0x100; + printbuf = (char*)realloc(printbuf, BufSize); + va_start(arg_list, format); + RetVal = _vsnprintf(printbuf, BufSize - 0x01, format, arg_list); + va_end(arg_list); + } while(RetVal == -1 && BufSize < 0x1000); + + CharToOem(printbuf, printbuf); + + printf("%s", printbuf); + + free(printbuf); + + return; +} +#endif*/ + +static void wprintc(const wchar_t* format, ...) +{ + va_list arg_list; + int RetVal; + UINT32 BufSize; + wchar_t* printbuf; +#ifdef WIN32 + UINT32 StrLen; + char* oembuf; + DWORD CPMode; +#endif + + BufSize = 0x00; + printbuf = NULL; + do + { + BufSize += 0x100; + printbuf = (wchar_t*)realloc(printbuf, BufSize * sizeof(wchar_t)); + + // Note: On Linux every vprintf call needs its own set of va_start/va_end commands. + // Under Windows (with VC6) one only one block for all calls works, too. + va_start(arg_list, format); + RetVal = _vsnwprintf(printbuf, BufSize - 0x01, format, arg_list); + va_end(arg_list); + } while(RetVal == -1 && BufSize < 0x1000); +#ifdef WIN32 + StrLen = wcslen(printbuf); + + // This is the only way to print Unicode stuff to the Windows console. + // No, wprintf doesn't work. + RetVal = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), printbuf, StrLen, &CPMode, NULL); + if (! RetVal) // call failed (e.g. with ERROR_CALL_NOT_IMPLEMENTED on Win95) + { + // fallback to printf with OEM codepage + oembuf = (char*)malloc(BufSize); + /*if (GetConsoleOutputCP() == GetOEMCP()) + CPMode = CP_OEMCP; + else + CPMode = CP_ACP;*/ + CPMode = GetConsoleOutputCP(); + WideCharToMultiByte(CPMode, 0x00, printbuf, StrLen + 1, oembuf, BufSize, NULL, NULL); + + printf("%s", oembuf); + free(oembuf); + } +#else + printf("%ls", printbuf); +#endif + + free(printbuf); + + return; +} + +static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock) +{ + if (! Clock) + return; + + if (ChipID == 0x00 && (Clock & 0x80000000)) + Clock &= ~0x40000000; + if (Clock & 0x80000000) + { + Clock &= ~0x80000000; + ChipID |= 0x80; + } + + if (Clock & 0x40000000) + printf("2x"); + printf("%s, ", GetAccurateChipName(ChipID, SubType)); + + return; +} + +static const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag) +{ + const wchar_t* RetTag; + + if (EngTag == NULL || ! wcslen(EngTag)) + { + RetTag = JapTag; + } + else if (JapTag == NULL || ! wcslen(JapTag)) + { + RetTag = EngTag; + } + else + { + if (! PreferJapTag) + RetTag = EngTag; + else + RetTag = JapTag; + } + + if (RetTag == NULL) + return L""; + else + return RetTag; +} + +static void ShowVGMTag(void) +{ + const wchar_t* TitleTag; + const wchar_t* GameTag; + const wchar_t* AuthorTag; + const wchar_t* SystemTag; + UINT8 CurChip; + UINT32 ChpClk; + UINT8 ChpType; + INT16 VolMod; +#ifdef SET_CONSOLE_TITLE + wchar_t TitleStr[0x80]; + UINT32 StrLen; +#endif + + TitleTag = GetTagStrEJ(VGMTag.strTrackNameE, VGMTag.strTrackNameJ); + GameTag = GetTagStrEJ(VGMTag.strGameNameE, VGMTag.strGameNameJ); + AuthorTag = GetTagStrEJ(VGMTag.strAuthorNameE, VGMTag.strAuthorNameJ); + SystemTag = GetTagStrEJ(VGMTag.strSystemNameE, VGMTag.strSystemNameJ); + +#ifdef SET_CONSOLE_TITLE + // --- Show "Song (Game) - VGM Player" as Console Title --- + if (! wcslen(TitleTag)) + { + char* TempPtr1; + char* TempPtr2; + + TempPtr1 = strrchr(VgmFileName, '\\'); + TempPtr2 = strrchr(VgmFileName, '/'); + if (TempPtr1 < TempPtr2) + TempPtr1 = TempPtr2; + if (TempPtr1 == NULL) + TempPtr1 = VgmFileName; + else + TempPtr1 ++; + //strncpy(TitleStr, TempPtr1, 0x70); + mbstowcs(TitleStr, TempPtr1, 0x7F); + TitleStr[0x70] = '\0'; + } + else + { +#if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(__MINGW32__) + swprintf(TitleStr, L"%.*ls", 0x70, TitleTag); +#else + swprintf(TitleStr, 0x80, L"%.*ls", 0x70, TitleTag); +#endif + } + StrLen = wcslen(TitleStr); + + if (wcslen(GameTag) && StrLen < 0x6C) + { +#if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(__MINGW32__) + swprintf(TitleStr + StrLen, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag); +#else + swprintf(TitleStr + StrLen, 0x80, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag); +#endif + StrLen = wcslen(TitleStr); + } + + wcscat(TitleStr, L" - " APP_NAME_L); +#ifdef WIN32 + SetConsoleTitleW(TitleStr); // Set Windows Console Title +#else + printf("\x1B]0;%ls\x07", TitleStr); // Set xterm/rxvt Terminal Title +#endif +#endif + + // --- Display Tag Data --- + if (VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP) + VolMod = VGMHead.bytVolumeModifier; + else if (VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01)) + VolMod = VOLUME_MODIF_WRAP - 0x100; + else + VolMod = VGMHead.bytVolumeModifier - 0x100; + + wprintc(L"Track Title:\t%ls\n", TitleTag); + wprintc(L"Game Name:\t%ls\n", GameTag); + wprintc(L"System:\t\t%ls\n", SystemTag); + wprintc(L"Composer:\t%ls\n", AuthorTag); + wprintc(L"Release:\t%ls\n", VGMTag.strReleaseDate); + printf("Version:\t%X.%02X\t", VGMHead.lngVersion >> 8, VGMHead.lngVersion & 0xFF); + printf(" Gain:%5.2f\t", pow(2.0, VolMod / (double)0x20)); + printf("Loop: "); + if (VGMHead.lngLoopOffset) + { + UINT32 PbRateMul; + UINT32 PbRateDiv; + UINT32 PbSamples; + + // calculate samples for correct display with changed playback rate + if (! VGMPbRate || ! VGMHead.lngRate) + { + PbRateMul = 1; + PbRateDiv = 1; + } + else + { + PbRateMul = VGMHead.lngRate; + PbRateDiv = VGMPbRate; + } + PbSamples = (UINT32)((UINT64)VGMHead.lngLoopSamples * PbRateMul / PbRateDiv); + + printf("Yes ("); + PrintMinSec(PbSamples, VGMSampleRate); + printf(")\n"); + } + else + { + printf("No\n"); + } + wprintc(L"VGM by:\t\t%ls\n", VGMTag.strCreator); + wprintc(L"Notes:\t\t%ls\n", VGMTag.strNotes); + printf("\n"); + + printf("Used chips:\t"); + for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++) + { + ChpClk = GetChipClock(&VGMHead, CurChip, &ChpType); + if (ChpClk && GetChipClock(&VGMHead, 0x80 | CurChip, NULL)) + ChpClk |= 0x40000000; + PrintChipStr(CurChip, ChpType, ChpClk); + } + printf("\b\b \n"); + printf("\n"); + + return; +} + + +#define LOG_SAMPLES (SampleRate / 5) +static void PlayVGM_UI(void) +{ + INT32 VGMPbSmplCount; + INT32 PlaySmpl; + UINT8 KeyCode; + UINT32 VGMPlaySt; + UINT32 VGMPlayEnd; + char WavFileName[MAX_PATH]; + char* TempStr; + WAVE_16BS* TempBuf; + UINT8 RetVal; + UINT32 TempLng; + bool PosPrint; + bool LastUninit; + bool QuitPlay; + UINT32 PlayTimeEnd; + + printf("Initializing ...\r"); + + PlayVGM(); + + /*switch(LogToWave) + { + case 0x00: + break; + case 0x01: + // Currently there's no record for Hardware FM + PlayingMode = 0x00; // Impossible to log at full speed AND use FMPort + break; + case 0x02: + if (PlayingMode == 0x01) + LogToWave = 0x00; // Output and log sound (FM isn't logged) + break; + }*/ + switch(PlayingMode) + { + case 0x00: + AUDIOBUFFERU = 10; + break; + case 0x01: + AUDIOBUFFERU = 0; // no AudioBuffers needed + break; + case 0x02: + AUDIOBUFFERU = 5; // try to sync Hardware/Software Emulator as well as possible + break; + } + if (AUDIOBUFFERU < NEED_LARGE_AUDIOBUFS) + AUDIOBUFFERU = NEED_LARGE_AUDIOBUFS; + if (ForceAudioBuf && AUDIOBUFFERU) + AUDIOBUFFERU = ForceAudioBuf; + + switch(FileMode) + { + case 0x00: // VGM + // RAW Log: no loop, no Creator, System Name set + IsRAWLog = (! VGMHead.lngLoopOffset && ! wcslen(VGMTag.strCreator) && + (wcslen(VGMTag.strSystemNameE) || wcslen(VGMTag.strSystemNameJ))); + break; + case 0x01: // CMF + IsRAWLog = false; + break; + case 0x02: // DRO + IsRAWLog = true; + break; + } + if (! VGMHead.lngTotalSamples) + IsRAWLog = false; + +#ifndef WIN32 + changemode(true); +#endif + + switch(PlayingMode) + { + case 0x00: + case 0x02: + if (LogToWave) + { + strcpy(WavFileName, VgmFileName); + TempStr = GetFileExtention(WavFileName); + if (TempStr == NULL) + TempStr = WavFileName + strlen(WavFileName); + else + TempStr --; + strcpy(TempStr, ".wav"); + + strcpy(SoundLogFile, WavFileName); + } + //FullBufFill = ! LogToWave; + + switch(LogToWave) + { + case 0x00: + case 0x02: + SoundLogging(LogToWave ? true : false); + if (FirstInit || ! StreamStarted) + { + // support smooth transistions between songs + RetVal = StartStream(0x00); + if (RetVal) + { + printf("Error openning Sound Device!\n"); + return; + } + StreamStarted = true; + } + PauseStream(PausePlay); + break; + case 0x01: + TempBuf = (WAVE_16BS*)malloc(SAMPLESIZE * LOG_SAMPLES); + if (TempBuf == NULL) + { + printf("Allocation Error!\n"); + return; + } + + StartStream(0xFF); + RetVal = SaveFile(0x00000000, NULL); + if (RetVal) + { + printf("Can't open %s!\n", SoundLogFile); + return; + } + break; + } + break; + case 0x01: + // PlayVGM() does it all + //FullBufFill = true; + break; + } + FirstInit = false; + + VGMPlaySt = VGMPos; + if (VGMHead.lngGD3Offset) + VGMPlayEnd = VGMHead.lngGD3Offset; + else + VGMPlayEnd = VGMHead.lngEOFOffset; + VGMPlayEnd -= VGMPlaySt; + if (! FileMode) + VGMPlayEnd --; // EOF Command doesn't count + PosPrint = true; + + PlayTimeEnd = 0; + QuitPlay = false; + while(! QuitPlay) + { + if (! PausePlay || PosPrint) + { + PosPrint = false; + + VGMPbSmplCount = SampleVGM2Playback(VGMHead.lngTotalSamples); + PlaySmpl = VGMPos - VGMPlaySt; +#ifdef WIN32 + printf("Playing %01.2f%%\t", 100.0 * PlaySmpl / VGMPlayEnd); +#else + // \t doesn't display correctly under Linux + // but \b causes flickering under Windows + printf("Playing %01.2f%% \b\b\b\t", 100.0 * PlaySmpl / VGMPlayEnd); +#endif + if (LogToWave != 0x01) + { + PlaySmpl = (BlocksSent - BlocksPlayed) * SMPL_P_BUFFER; + PlaySmpl = VGMSmplPlayed - PlaySmpl; + } + else + { + PlaySmpl = VGMSmplPlayed; + } + if (! VGMCurLoop) + { + if (PlaySmpl < 0) + PlaySmpl = 0; + } + else + { + while(PlaySmpl < SampleVGM2Playback(VGMHead.lngTotalSamples - + VGMHead.lngLoopSamples)) + PlaySmpl += SampleVGM2Playback(VGMHead.lngLoopSamples); + } + //if (PlaySmpl > VGMPbSmplCount) + // PlaySmpl = VGMPbSmplCount; + PrintMinSec(PlaySmpl, SampleRate); + printf(" / "); + PrintMinSec(VGMPbSmplCount, SampleRate); + printf(" seconds"); + if (Show95Cmds && Last95Max != 0xFFFF) + { + if (Show95Cmds == 0x01) + printf(" %02hX / %02hX", 1 + Last95Drum, Last95Max); + else if (Show95Cmds == 0x02) + printf(" %02hX / %02hX at %5u Hz", 1 + Last95Drum, Last95Max, Last95Freq); + else if (Show95Cmds == 0x03) + printf(" %02hX / %02hX at %4.1f KHz", 1 + Last95Drum, Last95Max, + Last95Freq / 1000.0); + } + //printf(" %u / %u", multipcm_get_channels(0, NULL), 28); + printf("\r"); +#ifndef WIN32 + fflush(stdout); +#endif + + if (LogToWave == 0x01 && ! PausePlay) + { + TempLng = FillBuffer(TempBuf, LOG_SAMPLES); + if (TempLng) + SaveFile(TempLng, TempBuf); + if (EndPlay) + break; + } + else + { +#ifdef WIN32 + Sleep(50); +#endif + } + } + else + { +#ifdef WIN32 + Sleep(1); +#endif + } +#ifndef WIN32 + if (! PausePlay) + WaveOutLinuxCallBack(); + else + Sleep(100); +#endif + + if (EndPlay) + { + if (! PlayTimeEnd) + { + PlayTimeEnd = PlayingTime; + // quitting now terminates the program, so I need some special + // checks to make sure that the rest of the audio buffer is played + if (! PLFileCount || CurPLFile >= PLFileCount - 0x01) + { + if (FileMode == 0x01) + PlayTimeEnd += SampleRate << 1; // Add 2 secs + PlayTimeEnd += AUDIOBUFFERU * SMPL_P_BUFFER; + } + } + + if (PlayingTime >= PlayTimeEnd) + QuitPlay = true; + } + if (_kbhit()) + { + KeyCode = _getch(); + if (KeyCode < 0x80) + KeyCode = toupper(KeyCode); + switch(KeyCode) + { +#ifndef WIN32 + case 0x1B: // Special Key + KeyCode = _getch(); + if (KeyCode == 0x1B || KeyCode == 0x00) + { + // ESC Key pressed + QuitPlay = true; + NextPLCmd = 0xFF; + break; + } + switch(KeyCode) + { + case 0x5B: + // Cursor-Key Table + // Key KeyCode + // Up 41 + // Down 42 + // Left 44 + // Right 43 + // Cursor only: CursorKey + // Ctrl: 0x31 + 0x3B + 0x35 + CursorKey + // Alt: 0x31 + 0x3B + 0x33 + CursorKey + + // Page-Keys: PageKey + 0x7E + // PageUp 35 + // PageDown 36 + KeyCode = _getch(); // Get 2nd Key + // Convert Cursor Key Code from Linux to Windows + switch(KeyCode) + { + case 0x31: // Ctrl or Alt key + KeyCode = _getch(); + if (KeyCode == 0x3B) + { + KeyCode = _getch(); + if (KeyCode == 0x35) + { + KeyCode = _getch(); + switch(KeyCode) + { + case 0x41: + KeyCode = 0x8D; + break; + case 0x42: + KeyCode = 0x91; + break; + case 0x43: + KeyCode = 0x74; + break; + case 0x44: + KeyCode = 0x73; + break; + default: + KeyCode = 0x00; + break; + } + } + } + + if ((KeyCode & 0xF0) == 0x30) + KeyCode = 0x00; + break; + case 0x35: + KeyCode = 0x49; + _getch(); + break; + case 0x36: + KeyCode = 0x51; + _getch(); + break; + case 0x41: + KeyCode = 0x48; + break; + case 0x42: + KeyCode = 0x50; + break; + case 0x43: + KeyCode = 0x4D; + break; + case 0x44: + KeyCode = 0x4B; + break; + default: + KeyCode = 0x00; + break; + } + } + // At this point I have Windows-style keys. +#else //#ifdef WIN32 + case 0xE0: // Special Key + // Cursor-Key Table + // Shift + Cursor results in the usual value for the Cursor Key + // Alt + Cursor results in 0x00 + (0x50 + CursorKey) (0x00 instead of 0xE0) + // Key None Ctrl + // Up 48 8D + // Down 50 91 + // Left 4B 73 + // Right 4D 74 + KeyCode = _getch(); // Get 2nd Key +#endif + switch(KeyCode) + { + case 0x4B: // Cursor Left + PlaySmpl = -5; + break; + case 0x4D: // Cursor Right + PlaySmpl = 5; + break; + case 0x73: // Ctrl + Cursor Left + PlaySmpl = -60; + break; + case 0x74: // Ctrl + Cursor Right + PlaySmpl = 60; + break; + case 0x49: // Page Up + if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile) + { + NextPLCmd = 0x01; + QuitPlay = true; + } + PlaySmpl = 0; + break; + case 0x51: // Page Down + if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01) + { + NextPLCmd = 0x00; + QuitPlay = true; + } + PlaySmpl = 0; + break; + default: + PlaySmpl = 0; + break; + } + if (PlaySmpl) + { + SeekVGM(true, PlaySmpl * SampleRate); + PosPrint = true; + } + break; +#ifdef WIN32 + case 0x1B: // ESC +#endif + case 'Q': + QuitPlay = true; + NextPLCmd = 0xFF; + break; + case ' ': + PauseVGM(! PausePlay); + PosPrint = true; + break; + case 'F': // Fading + FadeTime = FadeTimeN; + FadePlay = true; + break; + case 'R': // Restart + RestartVGM(); + PosPrint = true; + break; + case 'B': // Previous file (Back) + if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile) + { + NextPLCmd = 0x01; + QuitPlay = true; + } + break; + case 'N': // Next file + if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01) + { + NextPLCmd = 0x00; + QuitPlay = true; + } + break; + } + } + + /*if (! PauseThread && FadePlay && (! FadeTime || MasterVol == 0.0f)) + { + QuitPlay = true; + }*/ + if (FadeRAWLog && IsRAWLog && ! PausePlay && ! FadePlay && FadeTimeN) + { + PlaySmpl = (INT32)VGMHead.lngTotalSamples - + FadeTimeN * VGMSampleRate / 1500; + if (VGMSmplPos >= PlaySmpl) + { + FadeTime = FadeTimeN; + FadePlay = true; // (FadeTime / 1500) ends at 33% + } + } + } + ThreadNoWait = false; + + // Last Uninit: ESC pressed, no playlist, last file in playlist + LastUninit = (NextPLCmd & 0x80) || ! PLFileCount || + (NextPLCmd == 0x00 && CurPLFile >= PLFileCount - 0x01); + switch(PlayingMode) + { + case 0x00: + switch(LogToWave) + { + case 0x00: + case 0x02: + if (LastUninit) + { + StopStream(); + StreamStarted = false; + } + else + { + if (ThreadPauseEnable) + { + ThreadPauseConfrm = false; + PauseThread = true; + while(! ThreadPauseConfrm) + Sleep(1); // Wait until the Thread is finished + } + else + { + PauseThread = true; + } + if (LogToWave) + SaveFile(0xFFFFFFFF, NULL); + } + break; + case 0x01: + SaveFile(0xFFFFFFFF, NULL); + break; + } + break; + case 0x01: + if (StreamStarted) + { + StopStream(); + StreamStarted = false; + } + break; + case 0x02: + if (LastUninit) + { + StopStream(); + StreamStarted = false; +#ifdef MIXER_MUTING +#ifdef WIN32 + mixerClose(hmixer); +#else + close(hmixer); +#endif +#endif + } + else + { + if (ThreadPauseEnable) + { + ThreadPauseConfrm = false; + PauseThread = true; + while(! ThreadPauseConfrm) + Sleep(1); // Wait until the Thread is finished + PauseStream(true); + } + else + { + PauseThread = true; + } + } + break; + } +#ifndef WIN32 + changemode(false); +#endif + + StopVGM(); + + printf("\nPlaying finished.\n"); + + return; +} + +INLINE INT8 sign(double Value) +{ + if (Value > 0.0) + return 1; + else if (Value < 0.0) + return -1; + else + return 0; +} + +INLINE long int Round(double Value) +{ + // Alternative: (fabs(Value) + 0.5) * sign(Value); + return (long int)(Value + 0.5 * sign(Value)); +} + +INLINE double RoundSpecial(double Value, double RoundTo) +{ + return (long int)(Value / RoundTo + 0.5 * sign(Value)) * RoundTo; +} + +static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate) +{ + float TimeSec; + UINT16 TimeMin; + UINT16 TimeHours; + + TimeSec = (float)RoundSpecial(SamplePos / (double)SmplRate, 0.01); + //TimeSec = SamplePos / (float)SmplRate; + TimeMin = (UINT16)TimeSec / 60; + TimeSec -= TimeMin * 60; + if (! PrintMSHours) + { + printf("%02hu:%05.2f", TimeMin, TimeSec); + } + else + { + TimeHours = TimeMin / 60; + TimeMin %= 60; + printf("%hu:%02hu:%05.2f", TimeHours, TimeMin, TimeSec); + } + + return; +} diff --git a/Frameworks/GME/vgmplay/VGMPlay_AddFmts.c b/Frameworks/GME/vgmplay/VGMPlay_AddFmts.c new file mode 100644 index 000000000..c71754ffb --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay_AddFmts.c @@ -0,0 +1,1155 @@ +// VGMPlay_AddFmts.c: C Source File for playback of additional non-VGM formats + +#ifdef ADDITIONAL_FORMATS + +#include +#include +#include +#include +#include "stdbool.h" +#include + +#ifdef WIN32 +//#include +void __stdcall Sleep(unsigned int dwMilliseconds); +#else +#define Sleep(msec) usleep(msec * 1000) +#endif + +#include + +#include "chips/mamedef.h" + +#include "VGMPlay.h" + +#include "ChipMapper.h" + +// Structures for DRO and CMF files +typedef struct _cmf_file_header +{ + UINT32 fccCMF; + UINT16 shtVersion; + UINT16 shtOffsetInsData; + UINT16 shtOffsetMusData; + UINT16 shtTickspQuarter; + UINT16 shtTickspSecond; + UINT16 shtOffsetTitle; + UINT16 shtOffsetAuthor; + UINT16 shtOffsetComments; + UINT8 bytChnUsed[0x10]; + UINT16 shtInstrumentCount; + UINT16 shtTempo; +} CMF_HEADER; +typedef struct _cmf_instrument_table +{ + UINT8 Character[0x02]; + UINT8 ScaleLevel[0x02]; + UINT8 AttackDelay[0x02]; + UINT8 SustnRelease[0x02]; + UINT8 WaveSelect[0x02]; + UINT8 FeedbConnect; + UINT8 Reserved[0x5]; +} CMF_INSTRUMENT; + +typedef struct _dro_file_header +{ + char cSignature[0x08]; + UINT16 iVersionMajor; + UINT16 iVersionMinor; +} DRO_HEADER; +typedef struct _dro_version_header_1 +{ + UINT32 iLengthMS; + UINT32 iLengthBytes; + UINT32 iHardwareType; +} DRO_VER_HEADER_1; +typedef struct _dro_version_header_2 +{ + UINT32 iLengthPairs; + UINT32 iLengthMS; + UINT8 iHardwareType; + UINT8 iFormat; + UINT8 iCompression; + UINT8 iShortDelayCode; + UINT8 iLongDelayCode; + UINT8 iCodemapLength; +} DRO_VER_HEADER_2; + +#define FCC_CMF 0x464D5443 // 'CTMF' +#define FCC_DRO1 0x41524244 // 'DBRA' +#define FCC_DRO2 0x4C504F57 // 'WOPL' + +extern UINT32 GetGZFileLength(const char* FileName); +//bool OpenOtherFile(const char* FileName) + +INLINE UINT16 ReadLE16(const UINT8* Data); +INLINE UINT32 ReadLE32(const UINT8* Data); +INLINE int gzgetLE32(gzFile hFile, UINT32* RetValue); + +static UINT32 GetMIDIDelay(UINT32* DelayLen); +static UINT16 MIDINote2FNum(UINT8 Note, INT8 Pitch); +static void SendMIDIVolume(UINT8 ChipID, UINT8 Channel, UINT8 Command, + UINT8 ChnIns, UINT8 Volume); +//void InterpretOther(UINT32 SampleCount); + +INLINE INT32 SampleVGM2Playback(INT32 SampleVal); +INLINE INT32 SamplePlayback2VGM(INT32 SampleVal); + + +extern UINT32 SampleRate; // Note: also used by some sound cores to determinate the chip sample rate + +extern UINT8 FileMode; +extern VGM_HEADER VGMHead; +extern UINT32 VGMDataLen; +extern UINT8* VGMData; +extern GD3_TAG VGMTag; + +CMF_HEADER CMFHead; +UINT16 CMFInsCount; +CMF_INSTRUMENT* CMFIns; + +DRO_HEADER DROHead; +DRO_VER_HEADER_2 DROInf; +UINT8* DROCodemap; + + +extern UINT32 VGMPos; +extern INT32 VGMSmplPos; +extern INT32 VGMSmplPlayed; +extern INT32 VGMSampleRate; +extern UINT32 BlocksSent; +extern UINT32 BlocksPlayed; +bool VGMEnd; +extern bool EndPlay; +extern bool PausePlay; +extern bool FadePlay; +extern bool ForceVGMExec; + +extern UINT32 VGMMaxLoop; +UINT32 CMFMaxLoop; +extern UINT32 VGMMaxLoopM; +extern UINT32 VGMCurLoop; + +extern UINT32 FadeTime; +extern UINT32 VGMMaxLoop; +extern bool ErrorHappened; + +extern UINT8 CmdList[0x100]; + +bool OpenOtherFile(const char* FileName) +{ + gzFile hFile; + UINT32 FileSize; + UINT32 fccHeader; + UINT32 CurPos; + UINT32 TempLng; + UINT16 FileVer; + const char* TempStr; + DRO_VER_HEADER_1 DRO_V1; + + FileSize = GetGZFileLength(FileName); + + FileMode = 0x00; + hFile = gzopen(FileName, "rb"); + if (hFile == NULL) + return false; + + gzseek(hFile, 0x00, SEEK_SET); + gzgetLE32(hFile, &fccHeader); + switch(fccHeader) + { + case FCC_VGM: + FileMode = 0xFF; // should already be opened + break; + case FCC_CMF: + FileMode = 0x01; + break; + case FCC_DRO1: + gzgetLE32(hFile, &fccHeader); + if (fccHeader == FCC_DRO2) + FileMode = 0x02; + else + FileMode = 0xFF; + break; + default: + FileMode = 0xFF; + break; + } + if (FileMode == 0xFF) + goto OpenErr; + + VGMTag.strTrackNameE = L""; + VGMTag.strTrackNameJ = L""; + VGMTag.strGameNameE = L""; + VGMTag.strGameNameJ = L""; + VGMTag.strSystemNameE = L""; + VGMTag.strSystemNameJ = L""; + VGMTag.strAuthorNameE = L""; + VGMTag.strAuthorNameJ = L""; + VGMTag.strReleaseDate = L""; + VGMTag.strCreator = L""; + VGMTag.strNotes = L""; + + switch(FileMode) + { + case 0x00: // VGM File + break; + case 0x01: // CMF File + case 0x02: // DosBox RAW OPL + VGMTag.strGameNameE = (wchar_t*)malloc(0x10 * sizeof(wchar_t*)); + wcscpy(VGMTag.strGameNameE, L" Player"); + VGMTag.strSystemNameE = L"PC / MS-DOS"; + break; + } + + VGMDataLen = FileSize; + + switch(FileMode) + { + case 0x00: // VGM File + // already done by OpenVGMFile + break; + case 0x01: // CMF File + // Read Data + VGMData = (UINT8*)malloc(VGMDataLen); + if (VGMData == NULL) + goto OpenErr; + gzseek(hFile, 0x00, SEEK_SET); + gzread(hFile, VGMData, VGMDataLen); + +#ifndef VGM_BIG_ENDIAN + memcpy(&CMFHead, &VGMData[0x00], sizeof(CMF_HEADER)); +#else + CMFHead.fccCMF = ReadLE32(&VGMData[0x00]); + CMFHead.shtVersion = ReadLE16(&VGMData[0x04]); + CMFHead.shtOffsetInsData = ReadLE16(&VGMData[0x06]); + CMFHead.shtOffsetMusData = ReadLE16(&VGMData[0x08]); + CMFHead.shtTickspQuarter = ReadLE16(&VGMData[0x0A]); + CMFHead.shtTickspSecond = ReadLE16(&VGMData[0x0C]); + CMFHead.shtOffsetTitle = ReadLE16(&VGMData[0x0E]); + CMFHead.shtOffsetAuthor = ReadLE16(&VGMData[0x10]); + CMFHead.shtOffsetComments = ReadLE16(&VGMData[0x12]); + memcpy(CMFHead.bytChnUsed, &VGMData[0x14], 0x10); + CMFHead.shtInstrumentCount = ReadLE16(&VGMData[0x24]); + CMFHead.shtTempo = ReadLE16(&VGMData[0x26]); +#endif + + if (CMFHead.shtVersion == 0x0100) + { + CMFHead.shtInstrumentCount &= 0x00FF; + CMFHead.shtTempo = (UINT16)(60.0 * + CMFHead.shtTickspQuarter / CMFHead.shtTickspSecond + 0.5); + } + + if (CMFHead.shtOffsetTitle) + { + TempStr = (char*)&VGMData[CMFHead.shtOffsetTitle]; + TempLng = strlen(TempStr) + 0x01; + VGMTag.strTrackNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t)); + mbstowcs(VGMTag.strTrackNameE, TempStr, TempLng); + } + VGMTag.strGameNameE[0x00] = 'C'; + VGMTag.strGameNameE[0x01] = 'M'; + VGMTag.strGameNameE[0x02] = 'F'; + if (CMFHead.shtOffsetAuthor) + { + TempStr = (char*)&VGMData[CMFHead.shtOffsetAuthor]; + TempLng = strlen(TempStr) + 0x01; + VGMTag.strAuthorNameE = (wchar_t*)malloc(TempLng * sizeof(wchar_t)); + mbstowcs(VGMTag.strAuthorNameE, TempStr, TempLng); + } + if (CMFHead.shtOffsetComments) + { + TempStr = (char*)&VGMData[CMFHead.shtOffsetComments]; + TempLng = strlen(TempStr) + 0x01; + VGMTag.strNotes = (wchar_t*)malloc(TempLng * sizeof(wchar_t)); + mbstowcs(VGMTag.strNotes, TempStr, TempLng); + } + + CMFInsCount = CMFHead.shtInstrumentCount; + TempLng = CMFInsCount * sizeof(CMF_INSTRUMENT); + CMFIns = (CMF_INSTRUMENT*)malloc(TempLng); + memcpy(CMFIns, &VGMData[CMFHead.shtOffsetInsData], TempLng); + + memset(&VGMHead, 0x00, sizeof(VGM_HEADER)); + VGMHead.lngEOFOffset = VGMDataLen; + VGMHead.lngVersion = CMFHead.shtVersion; + VGMHead.lngDataOffset = CMFHead.shtOffsetMusData; + VGMSampleRate = CMFHead.shtTickspSecond; + VGMHead.lngTotalSamples = 0; + VGMHead.lngHzYM3812 = 3579545 | 0x40000000; + + break; + case 0x02: // DosBox RAW OPL + // Read Data + VGMData = (UINT8*)malloc(VGMDataLen); + if (VGMData == NULL) + goto OpenErr; + gzseek(hFile, 0x00, SEEK_SET); + VGMDataLen = gzread(hFile, VGMData, VGMDataLen); + + VGMTag.strGameNameE[0x00] = 'D'; + VGMTag.strGameNameE[0x01] = 'R'; + VGMTag.strGameNameE[0x02] = 'O'; + + memset(&VGMHead, 0x00, sizeof(VGM_HEADER)); + CurPos = 0x00; +#ifndef VGM_BIG_ENDIAN + memcpy(&DROHead, &VGMData[CurPos], sizeof(DRO_HEADER)); +#else + memcpy(DROHead.cSignature, &VGMData[CurPos + 0x00], 0x08); + DROHead.iVersionMajor = ReadLE16( &VGMData[CurPos + 0x08]); + DROHead.iVersionMinor = ReadLE16( &VGMData[CurPos + 0x0A]); +#endif + CurPos += sizeof(DRO_HEADER); + + memcpy(&TempLng, &VGMData[0x08], sizeof(UINT32)); + if (TempLng & 0xFF00FF00) + { + // DosBox Version 0.61 + // this version didn't contain Version Bytes + CurPos = 0x08; + DROHead.iVersionMajor = 0x00; + DROHead.iVersionMinor = 0x00; + } + else if (! (TempLng & 0x0000FFFF)) + { + // DosBox Version 0.63 + // the order of the Version Bytes is swapped in this version + FileVer = DROHead.iVersionMinor; + if (FileVer == 0x01) + { + DROHead.iVersionMinor = DROHead.iVersionMajor; + DROHead.iVersionMajor = FileVer; + } + } + VGMHead.lngEOFOffset = VGMDataLen; + VGMHead.lngVersion = (DROHead.iVersionMajor << 8) | + ((DROHead.iVersionMinor & 0xFF) << 0); + VGMSampleRate = 1000; + + if (DROHead.iVersionMajor > 2) + DROHead.iVersionMajor = 2; + switch(DROHead.iVersionMajor) + { + case 0: // Version 0 (DosBox Version 0.61) + case 1: // Version 1 (DosBox Version 0.63) + switch(DROHead.iVersionMajor) + { + case 0: // Version 0 + DRO_V1.iLengthMS = ReadLE32(&VGMData[CurPos + 0x00]); + DRO_V1.iLengthBytes = ReadLE32(&VGMData[CurPos + 0x04]); + DRO_V1.iHardwareType = VGMData[CurPos + 0x08]; + CurPos += 0x09; + break; + case 1: // Version 1 + DRO_V1.iLengthMS = ReadLE32(&VGMData[CurPos + 0x00]); + DRO_V1.iLengthBytes = ReadLE32(&VGMData[CurPos + 0x04]); + DRO_V1.iHardwareType = ReadLE32(&VGMData[CurPos + 0x08]); + CurPos += 0x0C; + break; + } + + DROInf.iLengthPairs = DRO_V1.iLengthBytes >> 1; + DROInf.iLengthMS = DRO_V1.iLengthMS; + switch(DRO_V1.iHardwareType) + { + case 0x01: // Single OPL3 + DROInf.iHardwareType = 0x02; + break; + case 0x02: // Dual OPL2 + DROInf.iHardwareType = 0x01; + break; + default: + DROInf.iHardwareType = (UINT8)DRO_V1.iHardwareType; + break; + } + DROInf.iFormat = 0x00; + DROInf.iCompression = 0x00; + DROInf.iShortDelayCode = 0x00; + DROInf.iLongDelayCode = 0x01; + DROInf.iCodemapLength = 0x00; + + break; + case 2: // Version 2 (DosBox Version 0.73) + // sizeof(DRO_VER_HEADER_2) returns 0x10, but the exact size is 0x0E + //memcpy(&DROInf, &VGMData[CurPos], 0x0E); + DROInf.iLengthPairs = ReadLE32( &VGMData[CurPos + 0x00]); + DROInf.iLengthMS = ReadLE32( &VGMData[CurPos + 0x04]); + DROInf.iHardwareType = VGMData[CurPos + 0x08]; + DROInf.iFormat = VGMData[CurPos + 0x09]; + DROInf.iCompression = VGMData[CurPos + 0x0A]; + DROInf.iShortDelayCode = VGMData[CurPos + 0x0B]; + DROInf.iLongDelayCode = VGMData[CurPos + 0x0C]; + DROInf.iCodemapLength = VGMData[CurPos + 0x0D]; + CurPos += 0x0E; + + break; + } + + if (DROInf.iCodemapLength) + { + DROCodemap = (UINT8*)malloc(DROInf.iCodemapLength * sizeof(UINT8)); + memcpy(DROCodemap, &VGMData[CurPos], DROInf.iCodemapLength); + CurPos += DROInf.iCodemapLength; + } + else + { + DROCodemap = NULL; + } + + VGMHead.lngDataOffset = CurPos; + VGMHead.lngTotalSamples = DROInf.iLengthMS; + switch(DROInf.iHardwareType) + { + case 0x00: // Single OPL2 Chip + VGMHead.lngHzYM3812 = 3579545; + break; + case 0x01: // Dual OPL2 Chip + VGMHead.lngHzYM3812 = 3579545 | 0xC0000000; + break; + case 0x02: // Single OPL3 Chip + VGMHead.lngHzYMF262 = 14318180; + break; + default: + VGMHead.lngHzYM3812 = 3579545 | 0x40000000; + break; + } + + break; + } + + gzclose(hFile); + return true; + +OpenErr: + + gzclose(hFile); + return false; +} + +INLINE UINT16 ReadLE16(const UINT8* Data) +{ + // read 16-Bit Word (Little Endian/Intel Byte Order) +#ifndef VGM_BIG_ENDIAN + return *(UINT16*)Data; +#else + return (Data[0x01] << 8) | (Data[0x00] << 0); +#endif +} + +INLINE UINT32 ReadLE32(const UINT8* Data) +{ + // read 32-Bit Word (Little Endian/Intel Byte Order) +#ifndef VGM_BIG_ENDIAN + return *(UINT32*)Data; +#else + return (Data[0x03] << 24) | (Data[0x02] << 16) | + (Data[0x01] << 8) | (Data[0x00] << 0); +#endif +} + +INLINE int gzgetLE32(gzFile hFile, UINT32* RetValue) +{ +#ifndef VGM_BIG_ENDIAN + return gzread(hFile, RetValue, 0x04); +#else + int RetVal; + UINT8 Data[0x04]; + + RetVal = gzread(hFile, Data, 0x04); + *RetValue = (Data[0x03] << 24) | (Data[0x02] << 16) | + (Data[0x01] << 8) | (Data[0x00] << 0); + return RetVal; +#endif +} + +static UINT32 GetMIDIDelay(UINT32* DelayLen) +{ + UINT32 CurPos; + UINT32 DelayVal; + + CurPos = VGMPos; + DelayVal = 0x00; + do + { + DelayVal = (DelayVal << 7) | (VGMData[CurPos] & 0x7F); + } while(VGMData[CurPos ++] & 0x80); + + if (DelayLen != NULL) + *DelayLen = CurPos - VGMPos; + return DelayVal; +} + +static UINT16 MIDINote2FNum(UINT8 Note, INT8 Pitch) +{ + const double CHIP_RATE = 3579545.0 / 72.0; // ~49716 + double FreqVal; + INT8 BlockVal; + UINT16 KeyVal; + + FreqVal = 440.0 * pow(2, (Note - 69 + Pitch / 256.0) / 12.0); + BlockVal = (Note / 12) - 1; + if (BlockVal < 0x00) + BlockVal = 0x00; + else if (BlockVal > 0x07) + BlockVal = 0x07; + KeyVal = (UINT16)(FreqVal * pow(2, 20 - BlockVal) / CHIP_RATE + 0.5); + + return (BlockVal << 10) | KeyVal; // << (8+2) +} + +static void SendMIDIVolume(UINT8 ChipID, UINT8 Channel, UINT8 Command, + UINT8 ChnIns, UINT8 Volume) +{ + bool RhythmOn; + UINT8 TempByt; + //UINT16 TempSht; + UINT32 TempLng; + UINT8 OpBase; // Operator Base + CMF_INSTRUMENT* TempIns; + UINT8 OpMask; + INT8 OpVol; + INT8 NoteVol; + + RhythmOn = (Channel >> 7) & 0x01; + Channel &= 0x7F; + + // Refresh Total Level (Volume) + TempIns = CMFIns + ChnIns; + OpBase = (Channel / 0x03) * 0x08 + (Channel % 0x03); + + if (! RhythmOn) + { + TempLng = 0x01; + OpMask = 0x03; + } + else + { + //TempLng = 0x01; + switch(Command & 0x0F) + { + case 0x0B: // Bass Drum + OpMask = 0x00; + break; + case 0x0F: // Hi Hat + OpMask = 0x00; + break; + case 0x0C: // Snare Drum + OpMask = 0x01; + break; + case 0x0D: // Tom Tom + OpMask = 0x00; + break; + case 0x0E: // Cymbal + OpMask = 0x01; + break; + default: + OpMask = 0x00; + break; + } + TempLng = OpMask; + OpMask *= 0x03; + } + + // Verified with PLAY.EXE + OpVol = (Volume + 0x04) >> 3; + OpVol = 0x10 - (OpVol << 1); + if (OpVol < 0x00) + OpVol >>= 1; + NoteVol = (TempIns->ScaleLevel[TempLng] & 0x3F) + OpVol; + if (NoteVol < 0x00) + NoteVol = 0x00; + + TempByt = NoteVol | TempIns->ScaleLevel[TempLng] & 0xC0; + chip_reg_write(0x09, ChipID, 0x00, 0x40 | (OpBase + OpMask), TempByt); + + return; +} + +void InterpretOther(UINT32 SampleCount) +{ + static UINT8 LastCmd = 0x90; + static UINT8 DrumReg[0x02] = {0x00, 0x00}; + static UINT8 ChnIns[0x10]; + static UINT8 ChnNote[0x20]; + static INT8 ChnPitch[0x10]; + INT32 SmplPlayed; + UINT8 Command; + UINT8 Channel; + UINT8 TempByt; + UINT16 TempSht; + UINT32 TempLng; + UINT32 DataLen; + static UINT8 CurChip = 0x00; + UINT8 OpBase; // Operator Base + CMF_INSTRUMENT* TempIns; + bool RhythmOn; + bool NoteOn; + UINT8 OpMask; + + if (VGMEnd) + return; + if (PausePlay && ! ForceVGMExec) + return; + + switch(FileMode) + { + case 0x01: // CMF File Mode + if (! SampleCount) + { + memset(ChnIns, 0xFF, 0x10); + memset(ChnNote, 0xFF, 0x20); + memset(ChnPitch, 0x00, 0x10); + + TempLng = VGMPos; + SmplPlayed = VGMSmplPos; + VGMPos = VGMHead.lngDataOffset; + RhythmOn = false; + while(! RhythmOn) + { + VGMSmplPos += GetMIDIDelay(&DataLen); + VGMPos += DataLen; + + Command = VGMData[VGMPos]; + if (Command & 0x80) + VGMPos ++; + else + Command = LastCmd; + Channel = Command & 0x0F; + + switch(Command & 0xF0) + { + case 0xF0: // End Of File + switch(Command) + { + case 0xFF: + switch(VGMData[VGMPos + 0x00]) + { + case 0x2F: + VGMHead.lngTotalSamples = VGMSmplPos; + VGMHead.lngLoopSamples = VGMHead.lngTotalSamples; + RhythmOn = true; + break; + } + break; + default: + VGMPos += 0x01; + } + break; + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + VGMPos += 0x02; + break; + case 0xC0: + case 0xD0: + VGMPos += 0x01; + break; + } + if (Command < 0xF0) + LastCmd = Command; + } + + VGMPos = TempLng; + VGMSmplPos = SmplPlayed; + } + + SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + SampleCount); + while(true) + { + TempLng = GetMIDIDelay(&DataLen); + if (VGMSmplPos + (INT32)TempLng > SmplPlayed) + break; + VGMSmplPos += TempLng; + VGMPos += DataLen; + + Command = VGMData[VGMPos]; + if (Command & 0x80) + VGMPos ++; + else + Command = LastCmd; + Channel = Command & 0x0F; + + if (DrumReg[0x00] & 0x20) + { + if (Channel < 0x0B) + { + CurChip = Channel / 0x06; + Channel = Channel % 0x06; + } + else + { + Channel -= 0x0B; + CurChip = Channel / 0x03; + Channel = Channel % 0x03; + if (CurChip == 0x01 && Channel == 0x00) + Channel = 0x02; + Channel += 0x06; + // Map all drums to one chip + CurChip = 0x00; + } + } + else + { + CurChip = Channel / 0x09; + Channel = Channel % 0x09; + } + CurChip = 0x00; + + RhythmOn = (Channel >= 0x06) && (DrumReg[CurChip] & 0x20); + switch(Command & 0xF0) + { + case 0xF0: // End Of File + switch(Command) + { + case 0xFF: + switch(VGMData[VGMPos + 0x00]) + { + case 0x2F: + if (CMFMaxLoop != 0x01) + { + VGMPos = VGMHead.lngDataOffset; + VGMSmplPos -= VGMHead.lngLoopSamples; + VGMSmplPlayed -= SampleVGM2Playback(VGMHead.lngLoopSamples); + SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + SampleCount); + VGMCurLoop ++; + + if (CMFMaxLoop && VGMCurLoop >= CMFMaxLoop) + FadePlay = true; + if (FadePlay && ! FadeTime) + VGMEnd = true; + } + else + { + VGMEnd = true; + break; + } + break; + } + break; + default: + VGMPos += 0x01; + } + break; + case 0x80: + case 0x90: + TempSht = MIDINote2FNum(VGMData[VGMPos + 0x00], ChnPitch[Channel]); + if ((Command & 0xF0) == 0x80) + NoteOn = false; + else + NoteOn = VGMData[VGMPos + 0x01] ? true : false; + + if (! RhythmOn) // Set "Key On" + TempSht |= (UINT8)NoteOn << 13; // << (8+5) + + if (NoteOn) + { + for (CurChip = 0x00; CurChip < 0x02; CurChip ++) + { + if (ChnNote[(CurChip << 4) | Channel] == 0xFF) + { + ChnNote[(CurChip << 4) | Channel] = VGMData[VGMPos + 0x00]; + break; + } + } + if (CurChip >= 0x02) + { + CurChip = 0x00; + ChnNote[(CurChip << 4) | Channel] = VGMData[VGMPos + 0x00]; + } + } + else + { + for (CurChip = 0x00; CurChip < 0x02; CurChip ++) + { + if (ChnNote[(CurChip << 4) | Channel] != 0xFF) + { + ChnNote[(CurChip << 4) | Channel] = 0xFF; + break; + } + } + } + if (CurChip >= 0x02) + CurChip = 0xFF; + + if (CurChip != 0xFF) + { + if (NoteOn) + { + if (! RhythmOn) + SendMIDIVolume(CurChip, Channel | (RhythmOn << 7), Command, + ChnIns[Command & 0x0F], VGMData[VGMPos + 0x01]); + } + if (NoteOn || ! RhythmOn) + { + chip_reg_write(0x09, CurChip, 0x00, 0xA0 | Channel, TempSht & 0xFF); + chip_reg_write(0x09, CurChip, 0x00, 0xB0 | Channel, TempSht >> 8); + } + + if (RhythmOn) + { + TempByt = 0x0F - (Command & 0x0F); + DrumReg[CurChip] &= ~(0x01 << TempByt); + if (NoteOn) + chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[CurChip]); + DrumReg[CurChip] |= (UINT8)NoteOn << TempByt; + chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[CurChip]); + } + } + VGMPos += 0x02; + break; + case 0xB0: + NoteOn = false; + switch(VGMData[VGMPos + 0x00]) + { + case 0x66: // Marker + break; + case 0x67: // Rhythm Mode + if (! VGMData[VGMPos + 0x01]) + { // Set Rhythm Mode off + DrumReg[0x00] = 0xC0; + DrumReg[0x01] = 0xC0; + } + else + { // Set Rhythm Mode on + DrumReg[0x00] = 0xE0; + DrumReg[0x01] = 0xE0; + } + chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[0x00]); + chip_reg_write(0x09, CurChip, 0x00, 0xBD, DrumReg[0x01]); + break; + case 0x68: // Pitch Upward + ChnPitch[Channel] = +VGMData[VGMPos + 0x01]; + NoteOn = true; + break; + case 0x69: // Pitch Downward + ChnPitch[Channel] = -VGMData[VGMPos + 0x01]; + NoteOn = true; + break; + } + if (NoteOn) + { + for (CurChip = 0x00; CurChip < 0x02; CurChip ++) + { + TempByt = ChnNote[(CurChip << 4) | Channel]; + if (! RhythmOn && TempByt != 0xFF) + { + TempSht = MIDINote2FNum(TempByt, ChnPitch[Channel]); + TempSht |= 0x01 << 13; // << (8+5) + + chip_reg_write(0x09, CurChip, 0x00, 0xA0 | Channel, TempSht & 0xFF); + chip_reg_write(0x09, CurChip, 0x00, 0xB0 | Channel, TempSht >> 8); + } + } + } + VGMPos += 0x02; + break; + case 0xC0: + TempByt = VGMData[VGMPos + 0x00]; + if (TempByt >= CMFInsCount) + { + //VGMPos += 0x01; + //break; + TempByt %= CMFInsCount; + } + + TempIns = CMFIns + TempByt; + ChnIns[Command & 0x0F] = TempByt; + + OpBase = (Channel / 0x03) * 0x08 + (Channel % 0x03); + if (! RhythmOn) + { + OpMask = 0x03; + } + else + { + switch(Command & 0x0F) + { + case 0x0B: // Bass Drum + OpMask = 0x03; + break; + case 0x0F: // Hi Hat + OpMask = 0x01; + //Channel = 0x01; // PLAY.EXE really does this sometimes + //OpBase = 0x01; + break; + case 0x0C: // Snare Drum + OpMask = 0x02; + break; + case 0x0D: // Tom Tom + OpMask = 0x01; + break; + case 0x0E: // Cymbal + OpMask = 0x02; + break; + default: + OpMask = 0x03; + break; + } + } + + for (CurChip = 0x00; CurChip < 0x02; CurChip ++) + { + TempByt = 0x00; + if (OpMask & 0x01) + { + // Write Operator 1 + chip_reg_write(0x09, CurChip, 0x00, + 0x20 | (OpBase + 0x00), TempIns->Character[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0x40 | (OpBase + 0x00), TempIns->ScaleLevel[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0x60 | (OpBase + 0x00), TempIns->AttackDelay[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0x80 | (OpBase + 0x00), TempIns->SustnRelease[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0xE0 | (OpBase + 0x00), TempIns->WaveSelect[TempByt]); + TempByt ++; + } + if (OpMask & 0x02) + { + // Write Operator 2 + chip_reg_write(0x09, CurChip, 0x00, + 0x20 | (OpBase + 0x03), TempIns->Character[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0x40 | (OpBase + 0x03), TempIns->ScaleLevel[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0x60 | (OpBase + 0x03), TempIns->AttackDelay[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0x80 | (OpBase + 0x03), TempIns->SustnRelease[TempByt]); + chip_reg_write(0x09, CurChip, 0x00, + 0xE0 | (OpBase + 0x03), TempIns->WaveSelect[TempByt]); + TempByt ++; + } + + chip_reg_write(0x09, CurChip, 0x00, 0xC0 | Channel, TempIns->FeedbConnect); + } + + VGMPos += 0x01; + break; + case 0xA0: + case 0xE0: + VGMPos += 0x02; + break; + case 0xD0: + VGMPos += 0x01; + break; + } + if (Command < 0xF0) + LastCmd = Command; + + if (VGMEnd) + break; + } + break; + case 0x02: // DosBox RAW OPL File Mode + NoteOn = false; + if (! SampleCount) + { + // was done during Init (Emulation Core / Chip Mapper for real FM), + // but Chip Mapper's init-routine now works different + switch(DROInf.iHardwareType) + { + case 0x00: // OPL 2 + for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) + chip_reg_write(0x09, 0x00, 0x00, TempByt, 0x00); + chip_reg_write(0x09, 0x00, 0x00, 0x08, 0x00); + chip_reg_write(0x09, 0x00, 0x00, 0x01, 0x00); + break; + case 0x01: // Dual OPL 2 + for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) + chip_reg_write(0x09, 0x00, 0x00, TempByt, 0x00); + chip_reg_write(0x09, 0x00, 0x00, 0x08, 0x00); + chip_reg_write(0x09, 0x00, 0x00, 0x01, 0x00); + //Sleep(1); + for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) + chip_reg_write(0x09, 0x01, 0x00, TempByt, 0x00); + chip_reg_write(0x09, 0x01, 0x00, 0x08, 0x00); + chip_reg_write(0x09, 0x01, 0x00, 0x01, 0x00); + break; + case 0x02: // OPL 3 + for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) + chip_reg_write(0x0C, 0x00, 0x00, TempByt, 0x00); + chip_reg_write(0x0C, 0x00, 0x00, 0x08, 0x00); + chip_reg_write(0x0C, 0x00, 0x00, 0x01, 0x00); + //Sleep(1); + for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) + chip_reg_write(0x0C, 0x00, 0x01, TempByt, 0x00); + //chip_reg_write(0x0C, 0x00, 0x01, 0x05, 0x00); + chip_reg_write(0x0C, 0x00, 0x01, 0x04, 0x00); + break; + default: + for (TempByt = 0xFF; TempByt >= 0x20; TempByt --) + chip_reg_write(0x09, 0x00, 0x00, TempByt, 0x00); + break; + } + Sleep(1); + NoteOn = true && (DROHead.iVersionMajor < 2); + OpBase = 0x00; + } + + SmplPlayed = SamplePlayback2VGM(VGMSmplPlayed + SampleCount); + while(VGMSmplPos <= SmplPlayed) + { + Command = VGMData[VGMPos + 0x00]; + + if (Command == DROInf.iShortDelayCode) + Command = 0x00; + else if (Command == DROInf.iLongDelayCode) + Command = 0x01; + else + { + switch(DROHead.iVersionMajor) + { + case 0: + case 1: + if (Command <= 0x01) + Command = 0xFF; + break; + case 2: + Command = 0xFF; + break; + } + } + + // DRO v0/v1 only: "Delay-Command" fix + if (NoteOn) // "Delay"-Command during init-phase? + { + if (Command < OpBase) // out of operator range? + { + NoteOn = false; // it's a delay + } + else + { + OpBase = Command; // it's a command + Command = 0xFF; + } + } +DRO_CommandSwitch: + switch(Command) + { + case 0x00: // 1-Byte Delay + VGMSmplPos += 0x01 + VGMData[VGMPos + 0x01]; + VGMPos += 0x02; + break; + case 0x01: // 2-Byte Delay + switch(DROHead.iVersionMajor) + { + case 0: + case 1: + memcpy(&TempSht, &VGMData[VGMPos + 0x01], 0x02); + if ((TempSht & 0xFF00) == 0xBD00) + { + Command = 0xFF; + goto DRO_CommandSwitch; + } + VGMSmplPos += 0x01 + TempSht; + VGMPos += 0x03; + break; + case 2: + VGMSmplPos += (0x01 + VGMData[VGMPos + 0x01]) << 8; + VGMPos += 0x02; + break; + } + break; + case 0x02: // Use 1st OPL Chip + case 0x03: // Use 2nd OPL Chip + CurChip = Command & 0x01; + if (CurChip > 0x00 && DROInf.iHardwareType == 0x00) + { + //CurChip = 0x00; + if (! CmdList[0x02]) + { + printf("More chips used than defined in header!\n"); + CmdList[0x02] = true; + } + } + VGMPos += 0x01; + break; + case 0x04: // Escape + VGMPos += 0x01; + // No break (execute following Register) + default: + Command = VGMData[VGMPos + 0x00]; + if (DROCodemap) + { + CurChip = (Command & 0x80) ? 0x01 : 0x00; + Command &= 0x7F; + if (Command < DROInf.iCodemapLength) + Command = DROCodemap[Command]; + else + Command = Command; + switch(DROInf.iHardwareType) + { + case 0x00: + if (CurChip) + { + if (! CmdList[0x02]) + { + printf("More chips used than defined in header!\n"); + CmdList[0x02] = true; + } + //CurChip = 0x00; + //Command = 0x00; + } + break; + case 0x01: + case 0x02: + break; + } + } + switch(DROInf.iHardwareType) + { + case 0x00: // OPL 2 + if (CurChip > 0x00) + break; + chip_reg_write(0x09, 0x00, 0x00, Command, VGMData[VGMPos + 0x01]); + break; + case 0x01: + chip_reg_write(0x09, CurChip, 0x00, Command, VGMData[VGMPos + 0x01]); + break; + case 0x02: // OPL 3 + chip_reg_write(0x0C, 0x00, CurChip, Command, VGMData[VGMPos + 0x01]); + break; + default: + chip_reg_write(0x09, CurChip, 0x00, Command, VGMData[VGMPos + 0x01]); + break; + } + VGMPos += 0x02; + break; + } + + if (VGMPos >= VGMDataLen) + { + if (VGMHead.lngTotalSamples != (UINT32)VGMSmplPos) + { + printf("Warning! Header Samples: %u\t Counted Samples: %u\n", + VGMHead.lngTotalSamples, VGMSmplPos); + VGMHead.lngTotalSamples = VGMSmplPos; + ErrorHappened = true; + } + VGMEnd = true; + } + if (VGMEnd) + break; + } + break; + } + + return; +} + +INLINE INT32 SampleVGM2Playback(INT32 SampleVal) +{ + return (INT32)((INT64)SampleVal * SampleRate / VGMSampleRate); +} + +INLINE INT32 SamplePlayback2VGM(INT32 SampleVal) +{ + return (INT32)((INT64)SampleVal * VGMSampleRate / SampleRate); +} + +#endif diff --git a/Frameworks/GME/vgmplay/VGMPlay_Intf.h b/Frameworks/GME/vgmplay/VGMPlay_Intf.h new file mode 100644 index 000000000..033b4a138 --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay_Intf.h @@ -0,0 +1,75 @@ +// VGMPlay_Intf.h: VGMPlay Interface Header File +// + +//#define NO_WCHAR_FILENAMES +#ifndef WIN32 +// Linux uses UTF-8 Unicode and has no special wide-character file routines. +#define NO_WCHAR_FILENAMES +#endif + +typedef struct waveform_16bit_stereo +{ + INT16 Left; + INT16 Right; +} WAVE_16BS; + +typedef struct waveform_32bit_stereo +{ + INT32 Left; + INT32 Right; +} WAVE_32BS; + +typedef struct vgm_file VGM_FILE; +struct vgm_file +{ + int (*Read)(VGM_FILE*, void*, UINT32); + int (*Seek)(VGM_FILE*, UINT32); + UINT32 (*GetSize)(VGM_FILE*); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void * VGMPlay_Init(void); +void VGMPlay_Init2(void* vgmp); +void VGMPlay_Deinit(void* vgmp); + +UINT32 GetGZFileLength(const char* FileName); + +bool OpenVGMFile(void* vgmp, const char* FileName); +bool OpenVGMFile_Handle(void* vgmp, VGM_FILE*); +void CloseVGMFile(void* vgmp); + +void FreeGD3Tag(GD3_TAG* TagData); +UINT32 GetVGMFileInfo(const char* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); +UINT32 GetVGMFileInfo_Handle(VGM_FILE*, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); +UINT32 CalcSampleMSec(void* vgmp, UINT64 Value, UINT8 Mode); +UINT32 CalcSampleMSecExt(void* vgmp, UINT64 Value, UINT8 Mode, VGM_HEADER* FileHead); +const char* GetChipName(UINT8 ChipID); +const char* GetAccurateChipName(UINT8 ChipID, UINT8 SubType); +UINT32 GetChipClock(void* vgmp, UINT8 ChipID, UINT8* RetSubType); + +#ifndef NO_WCHAR_FILENAMES +UINT32 GetGZFileLengthW(const wchar_t* FileName); +bool OpenVGMFileW(void* vgmp, const wchar_t* FileName); +UINT32 GetVGMFileInfoW(const wchar_t* FileName, VGM_HEADER* RetVGMHead, GD3_TAG* RetGD3Tag); +#endif + +INT32 SampleVGM2Playback(void* vgmp, INT32 SampleVal); +INT32 SamplePlayback2VGM(void* vgmp, INT32 SampleVal); + +void PlayVGM(void* vgmp); +void StopVGM(void* vgmp); +void RestartVGM(void* vgmp); +void SeekVGM(void* vgmp, bool Relative, INT32 PlayBkSamples); +void RefreshMuting(void* vgmp); +void RefreshPanning(void* vgmp); +void RefreshPlaybackOptions(void* vgmp); + +UINT32 FillBuffer(void* vgmp, WAVE_16BS* Buffer, UINT32 BufferSize); + +#ifdef __cplusplus +} +#endif + diff --git a/Frameworks/GME/vgmplay/VGMPlay_Updates.txt b/Frameworks/GME/vgmplay/VGMPlay_Updates.txt new file mode 100644 index 000000000..7571e3fae --- /dev/null +++ b/Frameworks/GME/vgmplay/VGMPlay_Updates.txt @@ -0,0 +1,130 @@ +Update list since 0.4.0u3 (03.12.2012) +- replaced some larger swich statements with function pointers (small speed up) +- fixed Stereo flag in SN76496 OPL mapper +- optimized playback routine (huge speed up, >20% for single-chip VGMs) +- writing to a second chip if only the first one is defined doesn't cause crashes anymore +- shortened the Chip Reset, Chip Mute and Chip Panning routines +- added OKIM6295, OKIM6258 and UPD7759 to DAC Stream Control +- slight improvements on OPL Hardware Playback +- fixed bug where [VGMPlay Linux] was unable to show Unicode characters +- added multiple paths for VGMPlay.ini and OPL4 ROM +- [VGMPlay Linux] is now checking for "vgmplay.ini" as well as "VGMPlay.ini" +- cleaned the code a bit +- [VGMPlay] now frees its memory properly when quitting + +17.04.2013 and later: +- improved skipping code for OPL hardware (to hopefully reduce noise after seeking) +- made Makefile print short information instead of full gcc command lines +- added possibility to override volume (+gain) of VGMs when using OPL hardware +- [VGMPlay] made it use stderr for error messages +- added optional debug output for DAC stream control (current data block, frequency) +- fixed a bug with data blocks being re-read when restarting playback (wasted memory) +- [VGMPlay] fixed bug when alternating between OPL hardware playback and emulation without restarting VGMPlay (e.g. via playlist with OPL and non-OPL VGMs) +- fixed crash where the NES ROM could be written to invalid offsets +- added alternative AY8910 core (EMU2149) +- added NES DPCM address overflow behaviour, added alternative DPCM range (based on NSFPlay) +- [VGMPlay] prevented it from writing too quickly to the OPL chip on fast computers + +15.06.2013 and later: +- [VGMPlay] console title now changes to "Song (Game) - VGM Player" while playing +- [VGMPlay] made it fall back to Japanese tags if English ones are empty +- [VGMPlay] made Console title use Unicode (yes, that works - not like the in-Console text) +- [in_vgm] made ANSI GetFileInfo function fall back to Japanese tags if English ones are not present +- [in_vgm] fixed Unicode -> ANSI filename conversion bug (caused Unicode Tag function to fail with some non-ANSI filenames) +- [in_vgm] fixed File Info Dialog falling back to tags of the other language (it isn't supposed to do that) +- [VGMPlay] some Codepage fixes for Windows version, hopefully it now behaves correctly in all cases. + The Linux version should now be able to print non-ANSI Unicode characters in tags. +- [VGMPlay Windows] now writes Unicode to the console (via WinAPI), so with the non-raster-fonts Japanese characters should work now. (although I only saw square boxes in WinXP, copying and pasting into Notepad worked at least) +- [in_vgm] Fixed bug where "Chip Rate" == 0 didn't work and would get reset to 44100 when loading the plugin. +- fixed bug where YM2612 Database Block caused DAC Stream Control to malfunction or crash (The MAME devs were right - realloc IS evil.) +- [VGMPlay Linux] added support for libao + +09.09.2013 and later: +- added WonderSwan support +- added better SN-PSG name display +- made it work on Big Endian machines (Thanks to nextvolume for sending me a patch that helped to find the critical spots) +- cleaned code that loads VGMs a bit +- shortened the Chip Stop routine +- applied MAME fixes to YM2413/YMF262 (thread safe now), K051649 (added test register, changed sound generation code a bit), K054539 (replaced sound generation code, might improve reverb), YMZ280B (minor stuff), YMF271 (FM code replaced, sounds different now!) +- added NES APU database block type +- added NES APU to 0x68 command (RAM write from database) and fixed its dual chip support +- ported NES APU/DMC emulator from NSFPlay 2.2 +- ported NES FDS emulator from NSFPlay 2.3 +- fixed MAME NES emulator reset +- fixed MAME NES (and maybe others?) unmuting all channels when restarting a song or seeking back +- added EMU2149 to YM2203, YM2608 and YM2610, fixes wrong AY-PSG tones in some VGMs (bug is still present with MAME's AY core) +- [VGMPlay] modified makefile to make it easier to compile VGMPlay using MSYS/MinGW +- [in_vgm] fixed crash when trying to mute channels after opening Winamp, but before any song was played +- [in_vgm] added options from VGMPlay.ini to in_vgm.ini +- [in_vgm] fixed track length display bug after changing the playback rate +- [VGMPlay Linux] fixed warnings that are printed from ym2612.c +- added VRC7 mode for YM2413 (reducing features and using separate instrument set) +- [in_vgm] made OK button of File Dialog return another error code to Winamp to prevent a playlist bug + +Updates since 0.4.0u4 (02.11.2013) +- added code for muting channels to K053260 (looks like I forgot that one) and K054539 (lost when applying MAME fixes) +- made K054539 use a proper clock (with a fallback for older VGMs) +- added a small hack that allows mid-song sample rate changes for the OKIM6258 chip (I'll make a proper implementation for 0.41) +- [VGMPlay] fixed absolute paths in m3u files +- [VGMPlay] fixed path handling, makes paths that contain / and \ work in both Windows and Unix, fixes m3u Windows paths under Unix +- fixed pitch of YM2608 ADPCM Tom Tom and Rim Shot (reported by MaliceX) +- fixed YM2612 Timer A (there are still tiny rounding errors though), this fixed the CSM mode +- fixed probable YM2612 bug (CSM mode was able to prevent KeyOn/Off commands on channels other than FM3) +- fixed noise calculation in MAME's AY8910 (applied latest MAME update) +- fixed a few crashes related to corrupted VGM headers with invalid offsets and insane clock values (though not all) +- [VGMPlay] MuteWTCh# now works in VGMPlay.ini +- fixed clicks in YMF278B core (introduced when porting it from openMSX) +- [VGMPlay] fixed length display when VGMs play slower/faster after using the PlaybackRate option +- fixed bug where VGZs with additional 00s at the end won't play (because VGMPlay will read an unpacked size of 0 bytes) +- changed Endianess of SCSP RAM data to Big Endian (as it should be) +- applied MAME QSound fixes (key on register, chip initialization) +- removed last A from AY-3-8910A and variants +- added an option to force OKIM6258 into 12-bit mode (makes it louder sometimes, but an internal 12-bit calculation seems to be more correct) +- VGMs using the OKIM6258 or OKIM6295 are now half as loud (affects global volume only, not chip-relative volume) +- [VGMPlay] fixed crash that happened on Linux when trying to display a large Notes tag (thanks to vampi-the-frog for the report) +- OKIM6258 lowers its output signal faster if there's no incoming data (reduces clicks) + +Updates since 0.4.0u5 (13.04.2014) +- fixed panning in AdLibEmu with 4-op instruments (it used the bits from Channel+0 instead of Channel+3) +- fixed panning in AdLibEmu for rhythm channels +- [VGMPlay] fixed YM2608/YM2610 DeltaT-channel not being muteable via MuteMask_PCM +- [VGMPlay] fixed muting of YM278B's FM drums +- applied MAME YM2612 Timer fix (said to fix SFX in Streets of Rage 2, see MAME Testers 05049) +- fixed C140 sub chip type and C140/System 21 banking (using current MAME code) +- added code to handle "real" C140 clock +- [VGMPlay] fixed "make install" (the share/vgmplay/ folder was created without "execute" rights for users) +- improved OKIM6258 data cache (should reduce clicks a bit) +- YMF278B: fixed OPL3 register writes and OPL3 chip clock +- applied MAME K053260 DPCM decoding fix +- applied MAME OPLx Key Scale Level fix +- added 40h-byte-FIFO to UPD7759 chip when in Slave mode, based on how Sega Pico games work +- fixed T6W28 muting (Noise channel muting was controlled by chip #2, affected in_vgm only) + Thanks to the developers of foo_input_vgm for noticing this bug. +- added NMK112 banking to OKIM6295 emulation +- added callback function for chip sample rate change + -> replaces OKIM6258 hack + -> fixes OPN SSG with MAME AY emulation + -> makes OKIM6295 clock changes work +- added routines for wide-character file names (only used by in_vgm for now) +- fixed RAM write range check for RF5C68 and RF5C164 +- made OKIM6258 FIFO queue larger, added RemoveDCOfs option +- [VGMPlay] fixed tag display under Windows 95 +- fixed YM2151 internal sample clipping (works like YM2612 now, can reduce distortion) +- fixed SegaPCM volume register bitmask [thanks ctr for finding the bug] +- fix potential crash in MultiPCM chip when playing invalid instruments +- fixed buggy frequency writes in K051649 by backporting some MAME changes +- added VSU, SAA1099 support +- fixed high beep in NSFPlay NES core with triangle at frequency 0 and OPT_TRI_NULL option +- [VGMPlay] fixed YM2612 DAC not being muteable via MuteMask +- added ES5503, ES5506, Seta X1-010, Namco C352, Irem GA20 support +- [VGMPlay] fixed Loop Length (is now adjusted for changed playback rates) + +Updates since 0.4.0u6 (30.05.2015) +- fixed SAA1099 muting (muting channel 1 or 4 prevented the envelope generator from running, thus muting 0-2 or 3-5 respectively) +- made SAA1099 output bipolar +- [VGMPlay] fix compiling under Linux (broken since Wide-Charcter file name support) +- fixed ES5503 sample rate (divider was off by one) +- applied fix from VOGONS that makes the SAA1099 core actually use the chip clock +- fixed YMF278B's FM part being emulated at wrong sample rate +- fixed Irem GA20 channel muting +- fixed playing non-NMK banked OKIM6295 VGMs after NMK-banked ones diff --git a/Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C.h b/Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C.h new file mode 100644 index 000000000..972d3ed19 --- /dev/null +++ b/Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C.h @@ -0,0 +1,403 @@ +// SWJ-SQRC01_1C.vgz +UINT8 FF1ws_1C[0x1908] = +{ 0x1F, 0x8B, 0x08, 0x00, 0xD4, 0x7A, 0x2F, 0x52, 0x02, 0x00, 0xD4, 0x98, 0x57, 0xCC, 0x0E, 0x51, + 0x10, 0x86, 0xC7, 0xFA, 0x7D, 0x7A, 0x0D, 0x42, 0x5C, 0x10, 0x12, 0x2E, 0x08, 0xA2, 0x25, 0x7A, + 0xFB, 0x95, 0x04, 0x21, 0xD1, 0xE2, 0x42, 0x39, 0x21, 0x24, 0x12, 0xD1, 0xAD, 0x16, 0xBD, 0xF7, + 0xDE, 0x7B, 0xEF, 0xBD, 0x1B, 0xDC, 0x6D, 0x24, 0x88, 0x1E, 0xA2, 0x04, 0x97, 0x6E, 0x48, 0x5C, + 0x48, 0x44, 0x94, 0x67, 0x8F, 0xC3, 0xE2, 0x60, 0xD5, 0xE0, 0x4B, 0x26, 0xF1, 0xAD, 0x9D, 0x77, + 0xDE, 0x77, 0x66, 0xCE, 0x9C, 0xF9, 0xFE, 0x2E, 0xFD, 0x06, 0x94, 0xEB, 0xDA, 0x4B, 0x64, 0x70, + 0x0E, 0xF9, 0xF0, 0x69, 0xDA, 0x53, 0xE4, 0x71, 0xC9, 0x72, 0x72, 0xB5, 0x80, 0x48, 0xA5, 0x1E, + 0x65, 0xE4, 0xF3, 0xCF, 0x1A, 0xF9, 0x87, 0x3E, 0x0F, 0xAB, 0x7E, 0xF2, 0x75, 0x90, 0xE6, 0x11, + 0xCD, 0x27, 0x9A, 0x5F, 0x54, 0x44, 0x73, 0x88, 0x06, 0xA2, 0x39, 0x45, 0xB3, 0x44, 0x73, 0x89, + 0x66, 0x44, 0x73, 0xF3, 0x46, 0x01, 0xD1, 0x82, 0xA2, 0x79, 0x45, 0x8B, 0x34, 0xD1, 0xE2, 0xA2, + 0x45, 0x33, 0x83, 0xB4, 0x50, 0x29, 0x2D, 0x1C, 0x98, 0xD6, 0x41, 0x24, 0x13, 0xDE, 0x44, 0x32, + 0x11, 0x9B, 0x84, 0x4D, 0xC6, 0xA6, 0x60, 0x53, 0xB1, 0x69, 0xD8, 0x74, 0x6C, 0x86, 0x44, 0x32, + 0x13, 0x9B, 0x85, 0xCD, 0xC6, 0xE6, 0x60, 0x73, 0xB1, 0x79, 0xD8, 0x7C, 0x19, 0x1A, 0xC9, 0x02, + 0x5E, 0x5A, 0x88, 0x2D, 0xC2, 0x16, 0x63, 0x4B, 0xB0, 0xA5, 0xD8, 0x32, 0x6C, 0x39, 0xB6, 0x82, + 0x17, 0x57, 0x62, 0xAB, 0xB0, 0xD5, 0xD8, 0x1A, 0x6C, 0x2D, 0xB6, 0x0E, 0x5B, 0x2F, 0xC3, 0x22, + 0xD9, 0xF0, 0x2A, 0x92, 0x8D, 0xCB, 0x22, 0xD9, 0x54, 0x3D, 0x92, 0xCD, 0x2F, 0x23, 0xD9, 0x32, + 0x35, 0x92, 0xAD, 0xCD, 0x22, 0xD9, 0x56, 0x2C, 0x92, 0xED, 0x2D, 0x22, 0xD9, 0xD1, 0x27, 0x92, + 0x9D, 0xE5, 0x23, 0xD9, 0x55, 0x3B, 0x92, 0xDD, 0xFB, 0x22, 0xD9, 0xC3, 0xFB, 0x7B, 0x4F, 0x44, + 0xB2, 0x6F, 0x78, 0x24, 0xFB, 0x6B, 0x8C, 0x00, 0xA0, 0x39, 0x00, 0xE5, 0x01, 0x78, 0x03, 0xC0, + 0x13, 0x00, 0xEA, 0x00, 0xB0, 0x06, 0x80, 0x0C, 0x00, 0x97, 0x01, 0x78, 0x03, 0x00, 0xB6, 0x0B, + 0xDB, 0x1D, 0x00, 0xD0, 0x0F, 0x80, 0x0D, 0x00, 0x08, 0x00, 0xC5, 0x06, 0x6B, 0x56, 0x69, 0xCD, + 0x95, 0x7B, 0x70, 0x24, 0x07, 0x00, 0x3E, 0x08, 0x91, 0x43, 0x10, 0x39, 0x0C, 0x91, 0x23, 0x10, + 0x39, 0x0A, 0x91, 0x63, 0x10, 0x39, 0x0E, 0x91, 0x13, 0x10, 0x39, 0x49, 0x9C, 0x53, 0x10, 0x39, + 0x0D, 0x11, 0xE5, 0xFD, 0x33, 0x10, 0x39, 0x0B, 0x91, 0x73, 0x35, 0x86, 0x03, 0xD0, 0x1C, 0x80, + 0xF2, 0x00, 0xBC, 0x01, 0xE0, 0x09, 0x00, 0x75, 0x00, 0x58, 0x03, 0x40, 0x06, 0x80, 0xCB, 0x00, + 0xBC, 0x01, 0x00, 0x3B, 0x85, 0x9D, 0x86, 0x88, 0xF6, 0x03, 0x60, 0x03, 0x00, 0x02, 0x00, 0x44, + 0x32, 0xBD, 0x35, 0x77, 0xD6, 0x20, 0x2D, 0xD2, 0xD6, 0x54, 0x0D, 0x34, 0x6F, 0xCD, 0xA1, 0x9A, + 0x0F, 0xCB, 0x5F, 0xD3, 0xDC, 0xC8, 0x68, 0xBE, 0xEC, 0x21, 0x9A, 0x3F, 0xDB, 0xCC, 0xCC, 0xD2, + 0xBC, 0xD9, 0xA6, 0x6C, 0x5E, 0xCD, 0xDB, 0xD9, 0xDC, 0xE5, 0x69, 0x27, 0xFE, 0xBF, 0x85, 0xB9, + 0x96, 0x51, 0x69, 0xA8, 0x39, 0x72, 0x8F, 0xD4, 0xE0, 0xB9, 0xE6, 0xCC, 0x0A, 0xB5, 0x48, 0x7B, + 0x53, 0x23, 0xD0, 0x3C, 0xD9, 0x43, 0x41, 0x31, 0xF3, 0x63, 0x1F, 0xB0, 0xFA, 0x80, 0xD0, 0xD2, + 0x34, 0xE2, 0x79, 0x67, 0x73, 0x2B, 0xA3, 0x79, 0xFA, 0x0E, 0x8D, 0x51, 0x96, 0x67, 0x69, 0xFE, + 0xF2, 0xA6, 0x21, 0x4F, 0xC3, 0x21, 0x9A, 0xB7, 0xAF, 0x39, 0x50, 0x50, 0xA5, 0xC6, 0x08, 0x0D, + 0xCE, 0xDB, 0x67, 0x44, 0x05, 0x61, 0x81, 0x8D, 0x9A, 0x6D, 0x3D, 0x6F, 0xC7, 0x9E, 0x43, 0x62, + 0xCF, 0x0E, 0x41, 0xCC, 0x6D, 0x4A, 0x16, 0x9E, 0x43, 0x63, 0xCF, 0xFD, 0x78, 0x36, 0xC4, 0xF3, + 0x39, 0x9E, 0x2E, 0xF6, 0x50, 0x18, 0x9A, 0xD9, 0x2E, 0x7E, 0xB6, 0x69, 0xFA, 0x69, 0xEC, 0x21, + 0xE8, 0x33, 0x57, 0x33, 0xCE, 0x9F, 0x6F, 0xE5, 0x63, 0xF4, 0x7C, 0x45, 0xCD, 0xCD, 0x0C, 0x1C, + 0x46, 0xC2, 0x81, 0x67, 0x12, 0x6A, 0xA6, 0xB2, 0xA9, 0x86, 0x67, 0x11, 0xA2, 0xE6, 0x30, 0x0F, + 0x32, 0x71, 0x1E, 0x2E, 0xE1, 0x55, 0x8E, 0xEF, 0x81, 0x39, 0x5E, 0xF0, 0x7D, 0x06, 0x3A, 0x86, + 0x1A, 0xB4, 0xD1, 0x9C, 0xB9, 0x4C, 0xBD, 0x4F, 0xA5, 0x8F, 0xF8, 0x92, 0xEC, 0xC5, 0x59, 0x04, + 0xB2, 0x84, 0x12, 0xF2, 0xDD, 0x21, 0x3F, 0x04, 0xF2, 0x9F, 0x78, 0xC7, 0x14, 0x1C, 0xF1, 0x44, + 0x3A, 0x04, 0x49, 0xD7, 0x7B, 0xBF, 0x81, 0xF8, 0x9D, 0xFA, 0xDC, 0xCF, 0xB4, 0xF8, 0xCC, 0xE7, + 0x86, 0x13, 0x4A, 0x8A, 0xE3, 0x58, 0xC8, 0x1B, 0x32, 0x5A, 0x33, 0xCF, 0x34, 0x77, 0xCE, 0x77, + 0xEA, 0x86, 0xA2, 0x0E, 0xAA, 0xD6, 0x1D, 0x85, 0x25, 0xF2, 0xBE, 0x57, 0xB8, 0x22, 0x0B, 0x85, + 0xD4, 0x1C, 0x19, 0xE8, 0x24, 0x5C, 0x18, 0x87, 0xAB, 0xEF, 0x55, 0xC7, 0x0F, 0x97, 0x48, 0xEB, + 0x0B, 0xC5, 0xE5, 0x3E, 0x45, 0x97, 0x14, 0x5F, 0xD6, 0xC1, 0x82, 0x2E, 0x0E, 0x14, 0x8F, 0x98, + 0xEA, 0xEF, 0xB3, 0x1F, 0xC6, 0xFC, 0xE6, 0x59, 0x7E, 0x25, 0x3F, 0xF0, 0x5B, 0x6E, 0xF9, 0x95, + 0xB3, 0xFC, 0xA8, 0xA6, 0x84, 0x48, 0xBB, 0x93, 0x14, 0xC1, 0x8F, 0x35, 0xD4, 0xF1, 0xFB, 0xA8, + 0xE3, 0xC6, 0xC3, 0xEF, 0x05, 0xFC, 0x7E, 0x44, 0xD3, 0x34, 0x7C, 0x4A, 0x6A, 0xCE, 0x0C, 0x6E, + 0xDF, 0x1B, 0xEA, 0xA0, 0x0B, 0x75, 0x7F, 0xA4, 0x66, 0x9D, 0xD2, 0x5C, 0xB9, 0x46, 0x68, 0x26, + 0x5B, 0x73, 0x07, 0xA6, 0x8A, 0x13, 0x98, 0x1C, 0xB9, 0x19, 0xAE, 0xF6, 0x58, 0x22, 0xD6, 0xB6, + 0x6C, 0x27, 0xB3, 0x24, 0x16, 0x4C, 0x4F, 0x80, 0x18, 0x1F, 0x2B, 0x0E, 0xE2, 0xFB, 0xD2, 0xAC, + 0x07, 0x7B, 0x0C, 0xA5, 0x49, 0xE7, 0x93, 0xC8, 0x58, 0x89, 0x4F, 0x9F, 0x71, 0x9A, 0x59, 0xC6, + 0xA1, 0xC7, 0xCF, 0xB1, 0x28, 0x9F, 0xB0, 0x18, 0x01, 0x03, 0x34, 0xC6, 0x18, 0xED, 0x03, 0xBA, + 0x96, 0x21, 0xE0, 0x12, 0xE2, 0x5A, 0xA3, 0x41, 0xE0, 0xF0, 0xEC, 0x48, 0x90, 0xA1, 0x31, 0x9B, + 0x2B, 0xB0, 0x99, 0x3B, 0x12, 0x36, 0x23, 0x34, 0xAB, 0x86, 0xE6, 0xCA, 0x98, 0x5A, 0x8E, 0x54, + 0x0E, 0x4B, 0x19, 0x62, 0x08, 0x41, 0x82, 0x03, 0xCD, 0x26, 0x94, 0xA5, 0x47, 0x3D, 0xDB, 0x04, + 0x24, 0xC1, 0x06, 0x09, 0x47, 0x24, 0x67, 0x32, 0xF6, 0x10, 0xD3, 0x2D, 0x78, 0x37, 0xA4, 0xDE, + 0xB5, 0xC4, 0x08, 0x97, 0x2D, 0xD7, 0xB2, 0xE8, 0x72, 0x87, 0xBC, 0x9C, 0x59, 0xFD, 0x59, 0xD3, + 0xAE, 0x0C, 0x51, 0xE9, 0x37, 0x85, 0x23, 0x00, 0x19, 0x08, 0xF8, 0xF9, 0xE9, 0x9F, 0x1F, 0xB2, + 0xBC, 0xE5, 0x32, 0xEB, 0x55, 0x3A, 0xFB, 0x9B, 0xE7, 0x72, 0x17, 0x3E, 0x5B, 0x4D, 0xA3, 0xEF, + 0x6E, 0xAA, 0x93, 0x05, 0xDF, 0xE5, 0xAB, 0x24, 0x6D, 0x51, 0x99, 0x9E, 0x70, 0x87, 0x72, 0xC8, + 0xD7, 0x54, 0xFA, 0xC7, 0xF3, 0x3A, 0x4A, 0xB7, 0xA2, 0x74, 0xA1, 0x55, 0x1A, 0xDA, 0xA8, 0x73, + 0x9C, 0xAE, 0x6C, 0xFE, 0x45, 0xE4, 0x11, 0x68, 0x25, 0xB9, 0x70, 0x8A, 0xA3, 0xDB, 0x67, 0x35, + 0x4D, 0xDB, 0x00, 0xB6, 0x7C, 0xE7, 0x3D, 0x18, 0xA3, 0xD2, 0xF9, 0xCE, 0x75, 0x95, 0x4E, 0x7C, + 0xFB, 0xBC, 0xF3, 0x05, 0xCF, 0xF9, 0x52, 0x2E, 0xFC, 0xAC, 0x2A, 0x7A, 0x46, 0xF6, 0xA3, 0xF9, + 0x24, 0x08, 0x56, 0x93, 0x1F, 0x3D, 0xD6, 0xE0, 0x7B, 0x93, 0x61, 0xBC, 0xC7, 0x12, 0x1B, 0xF5, + 0x2F, 0x38, 0x17, 0xA1, 0x1B, 0x4C, 0x55, 0x9C, 0xCA, 0x1C, 0xF6, 0x5C, 0x24, 0x87, 0x7F, 0x1A, + 0xBE, 0x45, 0x5C, 0xE3, 0x64, 0x07, 0xC9, 0x10, 0x68, 0x86, 0x8E, 0x13, 0x74, 0xDD, 0x7D, 0xF4, + 0x7B, 0x1A, 0xC2, 0x38, 0x43, 0x53, 0x2C, 0x8B, 0x05, 0x5F, 0x67, 0x31, 0x44, 0xE5, 0x30, 0x1A, + 0x2E, 0xD8, 0xAB, 0xA3, 0xEE, 0x8F, 0xE9, 0xB0, 0xAD, 0xDD, 0xD4, 0x69, 0xB9, 0x8F, 0x96, 0x3B, + 0x08, 0x39, 0xF2, 0x53, 0x2A, 0x56, 0xA2, 0x82, 0x7E, 0xF5, 0xE3, 0xDB, 0xDB, 0x64, 0xAA, 0xC7, + 0xC1, 0x57, 0x71, 0x22, 0x9E, 0x33, 0x56, 0xC5, 0xD7, 0x7A, 0xC1, 0xE5, 0x61, 0xC8, 0x57, 0x55, + 0x8C, 0x50, 0xB9, 0x34, 0x92, 0xED, 0xEA, 0x09, 0xDB, 0xD5, 0x7D, 0xB6, 0xAB, 0x8B, 0x6C, 0x57, + 0xA7, 0xD9, 0xAE, 0x76, 0xB1, 0x5D, 0xAD, 0x64, 0xBB, 0x9A, 0xC1, 0x76, 0x15, 0xB2, 0x5D, 0xF5, + 0x65, 0xBB, 0xEA, 0xCC, 0x76, 0x95, 0xCD, 0x76, 0x55, 0x93, 0xED, 0xAA, 0x3C, 0xDB, 0x55, 0xD1, + 0xC1, 0x94, 0x73, 0x94, 0x3D, 0xFC, 0xB9, 0x47, 0xB3, 0x9A, 0x5C, 0x60, 0x35, 0xB9, 0xC7, 0x6A, + 0xF2, 0x9A, 0xD5, 0xE4, 0x0D, 0xAB, 0x09, 0x76, 0xF4, 0x29, 0xAB, 0xC9, 0x25, 0x56, 0x13, 0x65, + 0x35, 0xA9, 0xC5, 0x6A, 0x52, 0x81, 0xD5, 0x24, 0x07, 0xAB, 0x89, 0x44, 0xA2, 0xD8, 0x99, 0x22, + 0xAC, 0x26, 0x35, 0x58, 0x4D, 0x9A, 0xB3, 0x9A, 0x9C, 0x8F, 0x4F, 0x7D, 0x59, 0xA7, 0x64, 0x25, + 0x1C, 0xB1, 0xFC, 0x2B, 0x4D, 0x13, 0x18, 0xCF, 0xA0, 0xB2, 0x33, 0x98, 0x42, 0x68, 0x0B, 0x35, + 0xDF, 0x0C, 0x9B, 0xCB, 0x90, 0x3C, 0x85, 0x6E, 0x6A, 0xB5, 0x8B, 0x55, 0xF1, 0x4E, 0x5F, 0x37, + 0x19, 0xDF, 0xCD, 0xD1, 0x10, 0xFF, 0xCE, 0x64, 0x12, 0xDD, 0xAE, 0x3B, 0xB2, 0xE9, 0x0E, 0x54, + 0xBB, 0xC5, 0x81, 0x9E, 0xA1, 0x52, 0x31, 0x16, 0x4F, 0x82, 0x85, 0xE0, 0x95, 0x37, 0x75, 0xF0, + 0x97, 0xA1, 0xC4, 0xC7, 0xBF, 0xB3, 0xC9, 0x76, 0xB1, 0x8B, 0x32, 0x3B, 0xDE, 0x45, 0x14, 0xD3, + 0xC2, 0x55, 0x93, 0x88, 0xAC, 0xB7, 0x44, 0xFA, 0xB0, 0xFE, 0xC4, 0xD9, 0x77, 0xB3, 0xD5, 0x34, + 0x0F, 0x58, 0x7A, 0xC9, 0xEA, 0x24, 0xB2, 0x3A, 0x99, 0xAC, 0x4E, 0x21, 0xAB, 0x53, 0xC9, 0xEA, + 0x34, 0xB2, 0x3A, 0x9D, 0xAC, 0xCE, 0x08, 0x59, 0x7A, 0xC9, 0xEA, 0x2C, 0xB2, 0x3A, 0x9B, 0xAC, + 0xCE, 0x21, 0xAB, 0x73, 0xC9, 0xEA, 0x3C, 0xB2, 0x2A, 0x0B, 0x35, 0x47, 0x66, 0xC8, 0x3B, 0x56, + 0x77, 0x58, 0x37, 0xAC, 0xF0, 0xC0, 0x91, 0x2A, 0x0A, 0x01, 0x48, 0xD9, 0xA0, 0xA1, 0x3D, 0x94, + 0x39, 0x48, 0x89, 0xF8, 0x84, 0x28, 0x3D, 0x64, 0xE2, 0x27, 0x72, 0x47, 0x73, 0xE4, 0x72, 0x78, + 0x5B, 0xED, 0x75, 0xF6, 0x1E, 0xCF, 0xB4, 0x4E, 0xB0, 0xC0, 0x05, 0xCB, 0xB4, 0x49, 0x70, 0xDC, + 0x38, 0x9B, 0xFF, 0x01, 0x67, 0xEB, 0xA7, 0xBC, 0x12, 0x1C, 0x24, 0x0B, 0xC9, 0x94, 0xC1, 0xF1, + 0x5E, 0xD8, 0xD8, 0x62, 0x2E, 0x70, 0xFC, 0x52, 0x30, 0x2D, 0x37, 0x20, 0xE8, 0xE7, 0xA0, 0x0F, + 0xDC, 0xEC, 0x30, 0x0D, 0x86, 0x38, 0x6E, 0xA9, 0x38, 0x89, 0xC6, 0x3E, 0x09, 0xB7, 0x85, 0x10, + 0xFB, 0x3A, 0x48, 0x5B, 0x0B, 0xD2, 0xEA, 0x8B, 0x20, 0x0B, 0xBF, 0xA8, 0x8E, 0x97, 0x3D, 0x90, + 0xEF, 0xCF, 0xF6, 0xCF, 0xE1, 0xF8, 0xD9, 0xFE, 0xF1, 0xAA, 0xF9, 0x7C, 0x2E, 0xFC, 0x4A, 0xF5, + 0x2F, 0xC0, 0xC7, 0x56, 0xCA, 0xE3, 0x93, 0x52, 0xA9, 0xB4, 0xFC, 0xFC, 0x64, 0xC5, 0xB7, 0xFE, + 0x07, 0x15, 0x77, 0x19, 0xFE, 0x7B, 0x15, 0xEF, 0xE3, 0xF1, 0xF9, 0x81, 0xB3, 0xD9, 0x27, 0xA9, + 0xF8, 0xC2, 0x9F, 0x3E, 0x98, 0x7E, 0x86, 0x87, 0xFC, 0xB9, 0x9E, 0xF1, 0xCB, 0xED, 0x67, 0xF8, + 0x37, 0x57, 0xFC, 0x42, 0x6A, 0xC5, 0x53, 0xCE, 0xD4, 0xEF, 0xAA, 0xF8, 0xD0, 0x24, 0x3F, 0xA3, + 0x35, 0xEB, 0xBC, 0xE6, 0xCA, 0xD2, 0x02, 0x32, 0x8A, 0xF5, 0x84, 0x3D, 0x8B, 0x3F, 0xC7, 0xF0, + 0xAB, 0xFF, 0xB2, 0xA9, 0xF8, 0xD1, 0xC4, 0x0E, 0x99, 0xD8, 0xBB, 0x58, 0x03, 0x88, 0xC3, 0xC5, + 0x66, 0x71, 0xB8, 0xE8, 0xF8, 0xFE, 0x9E, 0x39, 0xCB, 0x20, 0x6B, 0x5F, 0x12, 0xD5, 0x5E, 0x7F, + 0x33, 0xDD, 0xCF, 0x16, 0x16, 0x0A, 0x1B, 0xBD, 0xED, 0xE7, 0x27, 0xB1, 0x9E, 0x2D, 0x8D, 0x0B, + 0x81, 0x7B, 0x92, 0x56, 0x0F, 0x34, 0x91, 0xC2, 0xBB, 0x0E, 0xD0, 0xEB, 0x99, 0xF1, 0x83, 0x34, + 0xF3, 0x48, 0x73, 0xE7, 0x46, 0x02, 0x7B, 0x56, 0x82, 0x5D, 0xCE, 0xEE, 0x59, 0x76, 0x3B, 0x70, + 0xC4, 0xBE, 0xD1, 0x02, 0xBC, 0x5F, 0x04, 0xFC, 0xD4, 0x03, 0x3B, 0xE4, 0x8B, 0x9C, 0xC3, 0xCF, + 0x39, 0xCF, 0xF7, 0x38, 0x7B, 0xAD, 0xC5, 0xEF, 0xAD, 0xBE, 0x31, 0xE5, 0xAA, 0x1F, 0x43, 0x7B, + 0x77, 0x77, 0x0A, 0x74, 0xCA, 0x79, 0x4E, 0x52, 0x9C, 0x74, 0xCA, 0xD7, 0x52, 0x9C, 0xD4, 0xCC, + 0xBF, 0x2F, 0xD9, 0x63, 0x97, 0xB3, 0x93, 0x8F, 0x7C, 0xD7, 0x2B, 0x83, 0x6C, 0xA6, 0x3F, 0x6B, + 0x94, 0xD3, 0x1F, 0x1A, 0x25, 0xDB, 0x35, 0x0A, 0x99, 0x77, 0x01, 0x53, 0x1B, 0x25, 0xA9, 0xEB, + 0xC7, 0xF7, 0x47, 0xA3, 0x8F, 0x8B, 0x89, 0x7B, 0xCA, 0x3C, 0x48, 0x2B, 0x62, 0xA8, 0x59, 0x6D, + 0x50, 0xE0, 0xB7, 0x8A, 0x5D, 0x04, 0xA1, 0x6D, 0x5B, 0x65, 0x78, 0x4C, 0x6D, 0xD6, 0x8F, 0xB5, + 0xA3, 0x3F, 0x35, 0x52, 0x98, 0xFF, 0x40, 0xFB, 0x5D, 0x78, 0xC7, 0xBC, 0x7C, 0x48, 0xBB, 0x7C, + 0x9A, 0xF8, 0x0F, 0x0B, 0xAC, 0xE5, 0x6D, 0x3A, 0x04, 0xEF, 0x35, 0x84, 0xF6, 0xB4, 0x36, 0xFC, + 0xB4, 0x7B, 0xEC, 0xF2, 0xDA, 0xD4, 0x46, 0x6C, 0xEF, 0xCA, 0xE3, 0x74, 0x80, 0x21, 0x66, 0x46, + 0xEA, 0xC4, 0x21, 0x1E, 0x5A, 0x5A, 0xA4, 0xDC, 0x57, 0xA9, 0x55, 0x18, 0xF9, 0x6E, 0xF2, 0x84, + 0x1F, 0xB5, 0x52, 0xE5, 0x3F, 0xD2, 0x4A, 0x5B, 0x3F, 0xBA, 0x9B, 0x9C, 0x88, 0x1F, 0x98, 0x39, + 0x29, 0xBD, 0x39, 0x7E, 0xF0, 0x57, 0x66, 0x8E, 0xD7, 0x48, 0x3F, 0xDD, 0xAE, 0x70, 0x4E, 0x9F, + 0x93, 0x2D, 0xBC, 0x46, 0xFA, 0xBE, 0x99, 0xF3, 0x27, 0x7B, 0xC8, 0x9F, 0x47, 0xBF, 0x3E, 0x3E, + 0xFD, 0x79, 0xF4, 0x96, 0x57, 0xAB, 0xC7, 0x69, 0x23, 0x0C, 0xA2, 0x52, 0xA2, 0xF4, 0x7B, 0x04, + 0x8B, 0x2A, 0x0A, 0x22, 0x48, 0xA6, 0x4B, 0x88, 0x44, 0xA4, 0xB5, 0x82, 0x65, 0x8A, 0x48, 0xC8, + 0x27, 0x88, 0xB4, 0x6D, 0xD0, 0x6E, 0xC2, 0x1A, 0x28, 0x68, 0x28, 0x42, 0x41, 0x91, 0x32, 0x07, + 0xC8, 0x2D, 0x72, 0x03, 0xCE, 0x92, 0x26, 0x27, 0xC8, 0x78, 0x3C, 0x9F, 0x9F, 0x9F, 0x9F, 0xF9, + 0x3E, 0x81, 0xA3, 0x34, 0xC8, 0x08, 0xF6, 0x9B, 0xBF, 0x37, 0x6F, 0xDE, 0x37, 0xEB, 0x3F, 0xBF, + 0x5E, 0x3C, 0x0F, 0x3E, 0x9A, 0x47, 0xB4, 0x47, 0x11, 0xFD, 0x33, 0x3A, 0xD2, 0x46, 0x40, 0x53, + 0x17, 0xC7, 0x6F, 0xAB, 0xF5, 0x0D, 0x08, 0x45, 0x23, 0x7C, 0xB2, 0x46, 0x98, 0x49, 0x07, 0xB8, + 0xF7, 0x47, 0xF0, 0xFE, 0x91, 0x53, 0xF7, 0x4E, 0x8B, 0x8F, 0x39, 0xD9, 0xE4, 0xE7, 0xA4, 0x95, + 0x35, 0x4A, 0xCC, 0xA8, 0xED, 0xCD, 0x85, 0xAF, 0xF3, 0xE7, 0xDE, 0xCC, 0x0D, 0xF6, 0xBE, 0x17, + 0x4C, 0x08, 0x6E, 0xE7, 0x4F, 0xDC, 0xE6, 0xA8, 0xCB, 0x26, 0x47, 0x67, 0x07, 0x7C, 0x41, 0xB5, + 0x60, 0x9E, 0xE2, 0xF5, 0x0B, 0xF8, 0x07, 0x8A, 0x97, 0x80, 0x7E, 0xB5, 0x08, 0xE0, 0x6C, 0xB3, + 0x27, 0x53, 0xF2, 0x44, 0x88, 0xEC, 0x2A, 0x62, 0x40, 0xB2, 0x5B, 0x8E, 0xFD, 0xA3, 0xC7, 0x6E, + 0x9F, 0x99, 0xDA, 0xCE, 0xD3, 0x73, 0x6F, 0x3D, 0xD9, 0x14, 0x7B, 0x67, 0x4F, 0x04, 0xEC, 0xC7, + 0xCB, 0x17, 0x59, 0x67, 0x4F, 0x7D, 0x93, 0x75, 0x66, 0xAF, 0x1C, 0xEC, 0x15, 0x8A, 0x63, 0x84, + 0xC8, 0xB2, 0x9D, 0xBB, 0x80, 0xEC, 0xBD, 0x8C, 0x95, 0x90, 0x60, 0x3D, 0x96, 0xB4, 0xCD, 0x25, + 0xF0, 0xEE, 0xAB, 0x46, 0xF4, 0x97, 0xFF, 0x5E, 0x77, 0x81, 0x9A, 0x58, 0x7D, 0x44, 0xFA, 0x7C, + 0xAD, 0x67, 0x96, 0x23, 0x7D, 0x3D, 0x5D, 0x9D, 0x7A, 0x26, 0x4F, 0x3F, 0xA7, 0x31, 0x70, 0x24, + 0xF8, 0xDB, 0x79, 0x6C, 0x03, 0x4B, 0xA9, 0xCE, 0x91, 0x49, 0xE7, 0x8F, 0xED, 0xFC, 0xCF, 0x0C, + 0x4D, 0x54, 0xF6, 0x95, 0x3F, 0x1E, 0xD8, 0x6C, 0xE7, 0xE4, 0xF4, 0x7E, 0x89, 0xCD, 0xDA, 0x3F, + 0x83, 0x9C, 0xA6, 0x09, 0x9F, 0x5E, 0x39, 0x10, 0x93, 0xA7, 0x21, 0x0C, 0x57, 0x44, 0x4E, 0x20, + 0x58, 0x33, 0x8A, 0x19, 0xB4, 0xEF, 0x6B, 0xA5, 0x72, 0x07, 0x9A, 0x39, 0xEE, 0xBE, 0xDE, 0x0F, + 0x67, 0xBE, 0x32, 0x0C, 0x23, 0x36, 0xFF, 0x67, 0xE6, 0x70, 0x1C, 0x54, 0xE2, 0xEF, 0xE1, 0xCA, + 0xE6, 0xC3, 0x3C, 0x20, 0x06, 0x8D, 0x55, 0x1E, 0x0E, 0xF3, 0x5D, 0x7A, 0x07, 0x12, 0xF2, 0x3A, + 0xD8, 0xDF, 0xE2, 0x58, 0x8B, 0x28, 0x16, 0xBD, 0xA8, 0x30, 0xCB, 0x3B, 0xF0, 0xC3, 0x1E, 0x41, + 0x8C, 0x6B, 0x10, 0x20, 0x6B, 0x53, 0x1D, 0x46, 0x6E, 0xFC, 0x28, 0x8C, 0xC7, 0x90, 0x80, 0x03, + 0x97, 0x3A, 0x28, 0xDE, 0xB9, 0x23, 0x93, 0xA5, 0x23, 0xD7, 0x2B, 0x50, 0xE8, 0x17, 0x90, 0x9F, + 0x91, 0x3E, 0x80, 0x33, 0x3B, 0x9B, 0xB8, 0x1D, 0x0E, 0x59, 0x4E, 0x28, 0x07, 0xE7, 0xC9, 0x5C, + 0x3D, 0x8F, 0xDB, 0xE1, 0xE0, 0x95, 0x24, 0xF3, 0x3D, 0xCB, 0x93, 0x89, 0xA7, 0x02, 0x76, 0xA1, + 0x4B, 0x32, 0x76, 0x4F, 0x50, 0x81, 0x36, 0x67, 0x7D, 0x2A, 0xD6, 0x1B, 0x5E, 0x99, 0xC0, 0xE4, + 0x40, 0xCA, 0xEC, 0xC1, 0xB5, 0x5C, 0x60, 0x1C, 0x5D, 0xC5, 0xD1, 0x28, 0xF0, 0xE9, 0x1A, 0x11, + 0xCF, 0x68, 0x00, 0x10, 0xDE, 0x1D, 0x1D, 0x5C, 0xE0, 0x2E, 0x15, 0xB8, 0xE6, 0xD8, 0xD0, 0x6C, + 0xDE, 0xF9, 0xF5, 0x45, 0x74, 0x47, 0x90, 0x30, 0x0A, 0x0C, 0x47, 0x8E, 0xB9, 0xD7, 0x83, 0xC9, + 0xD8, 0x8B, 0x81, 0xCE, 0x01, 0x34, 0x32, 0x2A, 0x9B, 0xDA, 0xA5, 0x77, 0x3B, 0x84, 0xF0, 0x0A, + 0x08, 0x0F, 0x8E, 0x19, 0x92, 0xFC, 0x77, 0x60, 0xC5, 0x5B, 0x28, 0xED, 0xA4, 0x69, 0x87, 0x3A, + 0x82, 0xC7, 0x9A, 0x8E, 0xF5, 0x88, 0xBF, 0xAE, 0x45, 0xF5, 0x82, 0x21, 0xEB, 0x67, 0x72, 0x7F, + 0xF3, 0xB3, 0xCA, 0xBA, 0x66, 0xF3, 0x78, 0x3E, 0x88, 0xF3, 0x06, 0xD2, 0x7A, 0xCE, 0xEA, 0x69, + 0xEC, 0xA9, 0x22, 0xB3, 0xCF, 0x42, 0x7A, 0xA8, 0x00, 0x94, 0x25, 0x18, 0x56, 0x53, 0x30, 0xD1, + 0x14, 0x40, 0xD1, 0x68, 0x0A, 0x10, 0x80, 0x79, 0x6E, 0xD9, 0x15, 0x4C, 0x09, 0x71, 0xD7, 0x30, + 0x40, 0xC4, 0xAD, 0x38, 0xBA, 0x0D, 0x36, 0x85, 0x94, 0xCC, 0xB0, 0x69, 0xF9, 0x9A, 0x76, 0x9A, + 0xD7, 0x8F, 0x35, 0xF1, 0xBF, 0x22, 0x91, 0x13, 0x34, 0xE2, 0x04, 0x9D, 0xA7, 0x7C, 0x83, 0x6B, + 0x0D, 0x01, 0x92, 0xA4, 0x04, 0x15, 0xBE, 0x16, 0xDA, 0xCF, 0xEC, 0x25, 0x0A, 0x20, 0x0E, 0xD1, + 0xA8, 0x50, 0xE9, 0x33, 0x50, 0x31, 0x4B, 0x8F, 0x96, 0xF0, 0xCD, 0xBA, 0xF4, 0xAD, 0x99, 0x96, + 0x82, 0x8C, 0xE0, 0xBC, 0x42, 0xA5, 0x12, 0xA8, 0x90, 0x2A, 0xE4, 0x7D, 0xC2, 0xF0, 0x99, 0x12, + 0xAD, 0x0E, 0xDF, 0xA2, 0x70, 0xE7, 0xC6, 0x57, 0x71, 0xF1, 0x3A, 0xCC, 0x0C, 0x80, 0xCF, 0x85, + 0xE7, 0x97, 0x79, 0x71, 0xE1, 0x19, 0x68, 0x59, 0x60, 0x1C, 0x74, 0x49, 0x02, 0x27, 0x2D, 0xB5, + 0xD3, 0x01, 0x6A, 0xC2, 0x3C, 0x33, 0xE1, 0xBD, 0x8D, 0x68, 0x3B, 0x74, 0x6F, 0x58, 0x03, 0x5B, + 0xA0, 0x84, 0xB8, 0xBA, 0x4D, 0x0C, 0x78, 0xE3, 0x0A, 0x2B, 0xA1, 0xEE, 0x1B, 0xF9, 0x70, 0xCC, + 0x0A, 0x6E, 0x8D, 0xF3, 0x46, 0x50, 0x70, 0xAC, 0x12, 0x55, 0xC1, 0xB5, 0x7A, 0xFE, 0x18, 0x31, + 0x0A, 0x7B, 0xED, 0x82, 0x61, 0x95, 0xC1, 0x16, 0x99, 0x5D, 0x8A, 0x87, 0xDB, 0xC4, 0x62, 0xC1, + 0xF4, 0x0A, 0xCC, 0x2A, 0x24, 0x03, 0x21, 0x49, 0x44, 0x5C, 0xB4, 0x42, 0xF1, 0xFE, 0x77, 0x52, + 0x86, 0x10, 0x2C, 0xA8, 0x7E, 0x63, 0x72, 0x57, 0xC6, 0x51, 0x1C, 0x82, 0xBA, 0xDC, 0x03, 0xD5, + 0xD0, 0x27, 0xD9, 0x2A, 0xDB, 0x61, 0xC5, 0x0A, 0x43, 0xC1, 0xF5, 0x2B, 0x48, 0xDF, 0x23, 0x8C, + 0x95, 0xD1, 0x0E, 0x05, 0x5F, 0x46, 0xFC, 0x34, 0x83, 0xF8, 0xC2, 0x8C, 0x2F, 0x63, 0xDD, 0x33, + 0xC2, 0x38, 0xE7, 0x9B, 0x4A, 0xB5, 0x96, 0x85, 0x66, 0x6D, 0xC9, 0x77, 0xC1, 0xA2, 0x91, 0x5B, + 0x6C, 0xCB, 0x4B, 0x12, 0x4C, 0x4B, 0xD0, 0xE8, 0x32, 0x6C, 0xCA, 0xE8, 0x9E, 0x24, 0xD1, 0xC9, + 0x1D, 0xA9, 0xC2, 0x1D, 0x89, 0xEA, 0x7B, 0xBF, 0x50, 0xA6, 0x3B, 0xA8, 0xAF, 0xA8, 0x53, 0xEF, + 0x32, 0xD4, 0x17, 0x03, 0x40, 0xEB, 0x8B, 0x7E, 0xAB, 0x3B, 0xAD, 0xAF, 0x43, 0xB8, 0xF3, 0xC0, + 0xC9, 0x97, 0x21, 0xAF, 0xBF, 0xA2, 0xBE, 0xEC, 0xC7, 0xA0, 0xD5, 0xDB, 0xA8, 0xD6, 0x77, 0xB2, + 0x29, 0xC9, 0x0F, 0x64, 0x61, 0xAC, 0xFA, 0x1C, 0x2A, 0x8E, 0x97, 0x63, 0xC8, 0x6F, 0x58, 0x6A, + 0x10, 0xB1, 0xB3, 0x5D, 0xDF, 0xAA, 0x30, 0x80, 0xBA, 0xF8, 0xBE, 0xF5, 0x45, 0x0D, 0xD9, 0x01, + 0x17, 0xEE, 0x93, 0x92, 0x3B, 0x02, 0xF3, 0x5E, 0x68, 0x07, 0x9A, 0xEF, 0xF9, 0xEA, 0xB4, 0xAB, + 0x8C, 0x78, 0x43, 0x5C, 0x7C, 0xBD, 0xAA, 0xEF, 0x0E, 0x49, 0xE2, 0x8E, 0x68, 0x5C, 0x17, 0x24, + 0x6E, 0x51, 0xDF, 0x6D, 0x21, 0x37, 0xA0, 0x4F, 0x21, 0x37, 0xA8, 0xC6, 0x4A, 0x94, 0xDB, 0x2F, + 0x1B, 0xF8, 0x92, 0xF5, 0x13, 0xCA, 0x8F, 0xDE, 0x64, 0xF4, 0xBC, 0x7C, 0xCB, 0x03, 0x98, 0x60, + 0x25, 0xC3, 0x18, 0x1E, 0x10, 0x53, 0xF4, 0xC2, 0x14, 0x89, 0x13, 0x79, 0x1A, 0x48, 0x9F, 0x8C, + 0x64, 0x12, 0x28, 0x07, 0xA2, 0xFE, 0xF7, 0x22, 0x12, 0x3D, 0xC4, 0x27, 0xCD, 0x82, 0x11, 0xA3, + 0x31, 0xBE, 0x71, 0x0B, 0x34, 0x3E, 0x3C, 0x0B, 0x7E, 0x3C, 0x48, 0x8B, 0x10, 0x1F, 0x25, 0x5A, + 0xCC, 0x10, 0xFE, 0x5D, 0x1C, 0xFF, 0x7B, 0xB3, 0x94, 0xDC, 0xFD, 0x5F, 0xFB, 0x31, 0x95, 0x98, + 0x42, 0x88, 0x25, 0x62, 0x46, 0xA1, 0x8B, 0xA4, 0x5C, 0x31, 0x29, 0xA3, 0xED, 0xFF, 0xF6, 0x72, + 0x25, 0xAF, 0x3F, 0x45, 0x51, 0xFC, 0x9A, 0x43, 0x8A, 0x94, 0x4C, 0xE5, 0x87, 0x42, 0x64, 0x9E, + 0xE7, 0x79, 0x26, 0x22, 0xAC, 0x2C, 0x9E, 0x29, 0x4A, 0x86, 0x2F, 0xBE, 0xCF, 0xAC, 0x64, 0xA1, + 0x64, 0x8A, 0x05, 0x52, 0x94, 0x58, 0xA0, 0xA4, 0x64, 0xCA, 0x7F, 0x80, 0x28, 0x0B, 0x0B, 0x2C, + 0x14, 0x76, 0xB2, 0xA6, 0x0C, 0x9F, 0xF7, 0xB9, 0xC3, 0xE7, 0xDD, 0x77, 0xCD, 0x13, 0x7D, 0xF9, + 0x0E, 0xF7, 0x9E, 0xF9, 0x9E, 0x73, 0xEE, 0xBD, 0xE7, 0xBC, 0xAD, 0xF7, 0x1A, 0xAF, 0xB9, 0xD7, + 0xA4, 0x45, 0x8E, 0x1C, 0x20, 0xE4, 0x98, 0x3A, 0x46, 0x53, 0x52, 0x0D, 0xAA, 0x95, 0x50, 0x93, + 0x24, 0xFA, 0x36, 0x0A, 0x77, 0x09, 0x08, 0x33, 0x10, 0x3A, 0xF8, 0xA4, 0xDE, 0x73, 0x14, 0x19, + 0x5A, 0x0F, 0xCA, 0x77, 0x90, 0x0C, 0xC7, 0x42, 0x06, 0xDE, 0x8F, 0x9E, 0xE4, 0xD8, 0xD7, 0xCD, + 0x0B, 0x5C, 0x23, 0x98, 0xAB, 0xF9, 0xF8, 0x3E, 0xB3, 0x84, 0xA7, 0x63, 0x4B, 0x90, 0x9A, 0x7B, + 0xD8, 0x6D, 0x6D, 0x31, 0x15, 0x3C, 0x28, 0x62, 0x1B, 0x8A, 0xD3, 0x7A, 0x69, 0x67, 0x51, 0xF3, + 0x98, 0x0E, 0x96, 0x30, 0xB1, 0x48, 0xBA, 0x65, 0x5E, 0x48, 0x7F, 0xE5, 0x4A, 0x99, 0xF3, 0x30, + 0xE7, 0xDC, 0x65, 0xCA, 0x6B, 0x4A, 0x57, 0x17, 0x4B, 0x74, 0x2A, 0x05, 0x50, 0xA1, 0xB4, 0x3C, + 0x5B, 0xFC, 0x95, 0xFD, 0x4E, 0x97, 0x96, 0xDE, 0x43, 0x0E, 0x45, 0x75, 0x2C, 0x15, 0x8A, 0x59, + 0xDD, 0x5A, 0xFA, 0xDA, 0xAE, 0x76, 0xD9, 0x71, 0x90, 0x8A, 0x84, 0xA8, 0xF1, 0x47, 0xD8, 0x5A, + 0xBA, 0x6D, 0xEC, 0xF7, 0x75, 0x21, 0x07, 0x83, 0x6B, 0x47, 0x7D, 0x71, 0x39, 0x31, 0x58, 0xE4, + 0xDE, 0xAC, 0x40, 0xBF, 0x0F, 0x20, 0xFC, 0x85, 0x16, 0xB3, 0x84, 0xEE, 0x00, 0x34, 0xE1, 0xEA, + 0xBE, 0xA1, 0x10, 0x5C, 0xB8, 0x41, 0xE8, 0x61, 0x13, 0x6E, 0x88, 0xAC, 0x13, 0x35, 0xCD, 0x43, + 0xAA, 0xCA, 0x65, 0xE6, 0xB4, 0xE8, 0x32, 0x73, 0x46, 0xE3, 0x02, 0xD3, 0x14, 0xF9, 0x7E, 0xD1, + 0xC0, 0xCF, 0xE0, 0x29, 0xC6, 0x5F, 0xF3, 0xF8, 0xBB, 0x12, 0x3F, 0x64, 0x80, 0x4A, 0x35, 0xE1, + 0x07, 0x5F, 0x56, 0x65, 0xFE, 0x32, 0x32, 0xEC, 0x9A, 0x4B, 0x2B, 0x8D, 0xE9, 0x91, 0x76, 0xCD, + 0xDE, 0xC0, 0x73, 0xA9, 0x82, 0x7E, 0x46, 0xC6, 0x0D, 0x31, 0x28, 0x16, 0x44, 0xA2, 0xA0, 0x3D, + 0x90, 0x14, 0xAA, 0xA4, 0x8B, 0x23, 0x67, 0x49, 0x49, 0x1C, 0x43, 0x72, 0xEB, 0x02, 0xB6, 0x69, + 0x7F, 0xA5, 0xE3, 0xDC, 0x58, 0x27, 0xE9, 0xD2, 0x3F, 0x18, 0x45, 0x12, 0xF0, 0xC6, 0xF5, 0x99, + 0xEF, 0x8C, 0xF5, 0x33, 0x4D, 0x31, 0x07, 0x0C, 0x40, 0x03, 0x76, 0x54, 0x0E, 0xC8, 0xB6, 0x5A, + 0x58, 0x7E, 0xAB, 0x04, 0xCB, 0x7E, 0xCE, 0x83, 0x1F, 0x23, 0x2C, 0x18, 0xB4, 0x87, 0x02, 0x2C, + 0x4D, 0x57, 0xF2, 0x88, 0x71, 0x30, 0x1D, 0xB3, 0x65, 0x32, 0xD7, 0xE2, 0x11, 0xAD, 0xA4, 0x45, + 0x90, 0xF7, 0x85, 0x44, 0x66, 0x68, 0x88, 0x38, 0xD4, 0xA4, 0x5D, 0x58, 0xF2, 0xA5, 0xD0, 0x95, + 0xA2, 0x1D, 0xD6, 0xA7, 0xFC, 0xBF, 0x08, 0xA7, 0x53, 0x92, 0xB7, 0x17, 0x7B, 0xD9, 0x51, 0x81, + 0x8D, 0x5C, 0x34, 0x58, 0xDB, 0xE1, 0x85, 0x2D, 0x87, 0x37, 0xA8, 0x98, 0x53, 0x57, 0x39, 0x65, + 0x39, 0xBB, 0x59, 0x93, 0xC6, 0x14, 0x93, 0xEF, 0x28, 0x2C, 0x61, 0x8C, 0x17, 0x51, 0x6A, 0x0D, + 0x64, 0xCC, 0x63, 0xDC, 0x07, 0x8C, 0x2B, 0xFD, 0x68, 0x3A, 0xC2, 0xDC, 0x96, 0x9F, 0x83, 0x4A, + 0xF9, 0x6E, 0x04, 0x85, 0x20, 0xF4, 0xA1, 0x12, 0xBA, 0xC4, 0xC2, 0x22, 0x50, 0x57, 0xEA, 0x5E, + 0x07, 0x0F, 0x95, 0xDD, 0xB8, 0xAC, 0xB8, 0xE2, 0x29, 0xC3, 0x7E, 0xF5, 0xA0, 0x4B, 0x9C, 0x28, + 0x3D, 0x79, 0x4B, 0x9B, 0x2E, 0x31, 0xB1, 0x7A, 0xDE, 0xBC, 0xA0, 0xED, 0x09, 0xBD, 0x25, 0xC7, + 0x62, 0x04, 0x1B, 0x3D, 0x38, 0xEE, 0x4D, 0xF3, 0xB0, 0x5C, 0xE5, 0x2F, 0x6B, 0x7E, 0xA5, 0x98, + 0x2C, 0x6B, 0x0D, 0xCA, 0x39, 0xB6, 0x2D, 0x1D, 0x52, 0x0F, 0xF0, 0x07, 0x59, 0x81, 0xDE, 0xC5, + 0x58, 0xF2, 0xD9, 0x84, 0x28, 0xE1, 0x7D, 0xDC, 0x9C, 0x87, 0x51, 0x91, 0xB3, 0x5C, 0x83, 0xDA, + 0xE1, 0x70, 0xF4, 0xC4, 0x83, 0xAA, 0x73, 0x4D, 0xED, 0x3A, 0x45, 0x42, 0xE8, 0xB4, 0x03, 0x98, + 0xD2, 0x8C, 0x72, 0xB5, 0x79, 0xEE, 0xEA, 0x4B, 0x11, 0x1E, 0xDF, 0xDB, 0xC8, 0x4E, 0x0F, 0x4E, + 0xF3, 0xA4, 0x5B, 0x5C, 0xE3, 0xEE, 0x17, 0xC9, 0x93, 0xBC, 0x39, 0x5D, 0xD3, 0x90, 0x7D, 0xD1, + 0x79, 0xDA, 0x5F, 0x91, 0xE9, 0xB3, 0xE6, 0x8C, 0x0B, 0x7F, 0x55, 0xA6, 0x74, 0x63, 0xFF, 0x47, + 0xA0, 0x32, 0xEF, 0xE9, 0xDF, 0x12, 0xA8, 0xDB, 0x44, 0x9C, 0xAC, 0x08, 0x74, 0x48, 0xDD, 0x3A, + 0xB7, 0x9F, 0x95, 0x29, 0x48, 0x22, 0x07, 0x7F, 0x2E, 0x53, 0x45, 0x74, 0xBB, 0x6F, 0x11, 0x1E, + 0x45, 0xF4, 0xFA, 0x37, 0x22, 0x3A, 0xC3, 0x93, 0x82, 0x7A, 0x2A, 0x5A, 0x12, 0x26, 0xD1, 0x4E, + 0x0E, 0xA2, 0x3D, 0xDF, 0x54, 0xF1, 0x7D, 0xEC, 0x3F, 0x97, 0xAE, 0x22, 0x3A, 0xCF, 0x4D, 0x7E, + 0xEC, 0x0C, 0x82, 0xC9, 0x2A, 0xAA, 0x83, 0x22, 0x1A, 0x8A, 0xE3, 0xC0, 0x89, 0x44, 0x51, 0x15, + 0x78, 0x10, 0xC6, 0xBE, 0x17, 0x51, 0xE3, 0x88, 0x2E, 0x97, 0x1A, 0x1F, 0xA1, 0xC9, 0xAD, 0x12, + 0x7F, 0xEE, 0x55, 0x31, 0xD4, 0xBB, 0xCC, 0x38, 0xCF, 0xD9, 0xA2, 0x00, 0x0B, 0x7E, 0x15, 0x5C, + 0x49, 0xD1, 0x8F, 0x73, 0x8C, 0x7D, 0x39, 0xB2, 0xC9, 0xEF, 0xA5, 0x92, 0x8A, 0x52, 0x5A, 0xCC, + 0xE9, 0xD2, 0xA1, 0xE1, 0x1D, 0x8A, 0x74, 0x1C, 0x1B, 0xDE, 0x5C, 0x19, 0x1E, 0xB4, 0x23, 0xC3, + 0x1B, 0x1E, 0x52, 0x49, 0xC9, 0x7E, 0xE6, 0x8F, 0x52, 0xC9, 0x1C, 0x0B, 0x3A, 0xDA, 0xF5, 0xD2, + 0xEA, 0xD2, 0xE5, 0xE0, 0xD2, 0x49, 0xED, 0x72, 0x2F, 0x34, 0x8D, 0x4B, 0x0A, 0xB4, 0xAF, 0x05, + 0x89, 0x0B, 0x94, 0x52, 0x6A, 0x5F, 0x4B, 0x51, 0x13, 0xED, 0xFF, 0x4A, 0x26, 0x25, 0xE0, 0xC8, + 0xF4, 0xC8, 0x52, 0x6A, 0x7A, 0x3F, 0x50, 0xF4, 0x61, 0x99, 0x9E, 0x8B, 0x2C, 0xDB, 0x93, 0x64, + 0xB2, 0x5F, 0x92, 0x4C, 0xD2, 0x9B, 0x8A, 0x0E, 0x60, 0x91, 0xF9, 0x7B, 0xAF, 0x72, 0x52, 0xF8, + 0xB1, 0x6C, 0x7F, 0x2E, 0x99, 0x9C, 0x95, 0x26, 0x93, 0xA9, 0xCD, 0xA7, 0x09, 0x25, 0x44, 0xF8, + 0x0B, 0x36, 0xFF, 0x93, 0xC9, 0x24, 0xC8, 0xC1, 0xAB, 0x7A, 0x52, 0x3F, 0xE8, 0x5B, 0x89, 0x64, + 0xA4, 0x8F, 0x24, 0x8D, 0xC4, 0xAF, 0xED, 0x94, 0xEA, 0x57, 0xD2, 0xBF, 0xC5, 0x61, 0x8C, 0x51, + 0x3E, 0x06, 0x42, 0xA3, 0x14, 0x72, 0xDB, 0x9F, 0xB6, 0x35, 0x6F, 0x86, 0x78, 0xC3, 0x41, 0x43, + 0x8F, 0x6A, 0x1E, 0x5A, 0x10, 0x58, 0x48, 0xD4, 0xE4, 0x39, 0xCC, 0x60, 0x1B, 0x52, 0xAC, 0x1D, + 0xD4, 0x81, 0x1F, 0xFF, 0xDD, 0x7C, 0x94, 0xB3, 0x6B, 0x2E, 0x9D, 0x63, 0x37, 0xE3, 0x1A, 0x61, + 0xAA, 0xE4, 0xA5, 0x07, 0xFD, 0xF8, 0xA0, 0xF4, 0x5C, 0x59, 0xA9, 0x90, 0x49, 0x0A, 0x15, 0xF0, + 0xB4, 0xD3, 0x7D, 0x4E, 0xCD, 0x62, 0x24, 0x05, 0xBD, 0xA6, 0xB0, 0x27, 0x29, 0xB0, 0x5A, 0xC9, + 0xD0, 0xA3, 0x6C, 0x55, 0xE4, 0x45, 0x16, 0xE5, 0xC0, 0x6C, 0xF1, 0xC2, 0x67, 0xD2, 0x15, 0xBA, + 0x7D, 0x38, 0xFB, 0x9B, 0x49, 0xAA, 0x08, 0xA0, 0x1C, 0xEA, 0x90, 0xA4, 0x66, 0x45, 0xC9, 0x6A, + 0x15, 0xCB, 0xE1, 0x42, 0x16, 0x62, 0xAC, 0xAA, 0xA1, 0x5A, 0x01, 0x77, 0xB6, 0x4B, 0x59, 0xF7, + 0x25, 0x29, 0xAB, 0x9B, 0x95, 0x6E, 0xEE, 0xD9, 0x8B, 0xE4, 0xF1, 0xE0, 0x73, 0xE3, 0xC3, 0x4C, + 0x60, 0x78, 0x02, 0x16, 0x3B, 0x0A, 0xC0, 0x9F, 0x8F, 0xDF, 0x4F, 0x67, 0x13, 0x9D, 0x04, 0x7A, + 0x28, 0x6E, 0x52, 0xB8, 0xD3, 0xB0, 0xA8, 0xAD, 0x2A, 0xD8, 0xD7, 0xC8, 0x8A, 0xAA, 0x7D, 0x5B, + 0xE5, 0x28, 0x06, 0xBA, 0xC4, 0xBF, 0x41, 0x3B, 0x4E, 0xA6, 0x55, 0xC0, 0x5B, 0x40, 0xE4, 0xBF, + 0xA0, 0xC9, 0x1E, 0x16, 0x99, 0xB7, 0x50, 0xA7, 0x36, 0x17, 0xBC, 0xAE, 0xAF, 0x8E, 0x42, 0xF7, + 0xE0, 0xC7, 0xBA, 0x2A, 0xE6, 0x63, 0xEB, 0x88, 0xC1, 0x72, 0xA5, 0x4C, 0x76, 0x93, 0x3A, 0xA1, + 0x6C, 0xAA, 0xC8, 0x97, 0xEC, 0x24, 0x6D, 0x5F, 0xBE, 0x8A, 0xA1, 0x17, 0x46, 0x7E, 0xDC, 0x2B, + 0x59, 0x0E, 0x20, 0x17, 0x8A, 0xF6, 0xF1, 0xC5, 0xC4, 0xF8, 0x6F, 0xA0, 0x1C, 0xA2, 0x64, 0x27, + 0xDD, 0x36, 0xA6, 0x68, 0x5D, 0xD6, 0x29, 0xF3, 0xA8, 0xCB, 0x34, 0x66, 0x6A, 0x74, 0xCE, 0x52, + 0x1A, 0xCE, 0x70, 0x39, 0xB5, 0x63, 0xE9, 0x98, 0x87, 0xED, 0x3A, 0x66, 0x31, 0x47, 0xB0, 0x65, + 0xFB, 0x55, 0xC3, 0xAF, 0xA7, 0x46, 0x4F, 0x86, 0xF0, 0x6E, 0x28, 0x98, 0x8C, 0x31, 0x26, 0x86, + 0x9F, 0x2D, 0x4E, 0x8D, 0xBE, 0x2C, 0x10, 0x50, 0x21, 0xDE, 0x30, 0x82, 0x27, 0x71, 0x76, 0x6A, + 0xFE, 0x2D, 0xA5, 0xC5, 0xEC, 0xE5, 0x29, 0x7B, 0x89, 0xE5, 0xC4, 0x4B, 0x3B, 0x39, 0xB9, 0xCD, + 0x13, 0xE7, 0xFC, 0x3D, 0xCD, 0xD5, 0x1C, 0x93, 0xD9, 0x84, 0xA0, 0xB6, 0x03, 0xDF, 0xC5, 0xCD, + 0x7C, 0x08, 0xFE, 0xF1, 0x4C, 0x36, 0x92, 0x02, 0xFD, 0xB1, 0x31, 0x72, 0x5D, 0xFF, 0xD8, 0x12, + 0xB7, 0xA2, 0x5E, 0xF0, 0x3E, 0xEA, 0x05, 0x9F, 0xA3, 0x5C, 0xF0, 0x13, 0xCA, 0x04, 0xDF, 0xA1, + 0x4C, 0xF0, 0x21, 0xCA, 0x04, 0xEF, 0xA1, 0x4C, 0x70, 0x18, 0xCA, 0x04, 0x7B, 0xA2, 0x4C, 0xB0, + 0x11, 0xCA, 0x03, 0xDB, 0xA2, 0x3C, 0x70, 0x08, 0xCA, 0x03, 0xA7, 0xA2, 0x3C, 0xF0, 0x44, 0xFE, + 0xBD, 0x56, 0xCC, 0x4D, 0xDF, 0xEB, 0xC5, 0xDC, 0x74, 0xAF, 0xF1, 0x46, 0x88, 0xF0, 0xD5, 0xBD, + 0x66, 0x8D, 0xB7, 0xA3, 0x17, 0x13, 0xE1, 0xE3, 0xFA, 0x67, 0x3E, 0x26, 0x42, 0xBD, 0x98, 0x78, + 0xDD, 0xC4, 0xEB, 0x16, 0x5E, 0xB7, 0x0D, 0x7A, 0x31, 0xF1, 0xBA, 0x6B, 0x4A, 0xBD, 0x98, 0xC6, + 0x3D, 0x26, 0xC2, 0xE0, 0x7E, 0x63, 0xDE, 0xBD, 0x16, 0xCD, 0x37, 0x15, 0xCD, 0x7C, 0x7D, 0x98, + 0x02, 0x79, 0x33, 0x7B, 0xD0, 0xDC, 0x56, 0x7C, 0xC1, 0xC3, 0x29, 0xBF, 0x43, 0x44, 0xC8, 0xAD, + 0xD7, 0x41, 0x0E, 0xE7, 0x8A, 0x78, 0xDC, 0xD6, 0xE2, 0x20, 0x12, 0xA5, 0xA2, 0x87, 0xF3, 0xA1, + 0xEB, 0x4C, 0xCF, 0xED, 0x66, 0x8D, 0xB1, 0xFD, 0x72, 0x76, 0x1A, 0xD9, 0x19, 0x17, 0x00, 0x20, + 0x63, 0x1C, 0xA0, 0xA2, 0x67, 0x1A, 0xB0, 0xAE, 0xB4, 0x01, 0x3E, 0xCE, 0x7E, 0xCE, 0xD9, 0x93, + 0x0A, 0x5C, 0x90, 0x35, 0x60, 0xF2, 0x9B, 0xCB, 0x80, 0x02, 0x12, 0x41, 0x03, 0x66, 0xBA, 0x6C, + 0x11, 0x59, 0x20, 0xE6, 0x3F, 0xE5, 0xFC, 0x4B, 0x6D, 0x54, 0xAB, 0x76, 0x90, 0x30, 0x4F, 0xEB, + 0xE4, 0x95, 0x14, 0x18, 0x52, 0x3E, 0x8D, 0x5E, 0xFF, 0xB2, 0xCA, 0x99, 0xCC, 0x39, 0x58, 0xC3, + 0x72, 0x6C, 0xC9, 0x76, 0xE1, 0xB2, 0xF9, 0x97, 0x78, 0xB6, 0x9D, 0xD5, 0x29, 0xCF, 0x2E, 0x75, + 0x8A, 0x79, 0x9E, 0xF7, 0x0D, 0x9E, 0x4D, 0x89, 0xE7, 0x47, 0xCC, 0xA5, 0xC9, 0xAD, 0xC3, 0x0B, + 0x5A, 0x41, 0x97, 0x78, 0x5E, 0xE2, 0x78, 0x7E, 0x82, 0x91, 0xD3, 0x30, 0x93, 0x19, 0x50, 0x2B, + 0xF6, 0xA0, 0x63, 0x93, 0x2C, 0xCE, 0x57, 0xB6, 0xA6, 0x7C, 0x88, 0xFD, 0x2C, 0xB0, 0x5F, 0xCC, + 0x46, 0x35, 0x76, 0x57, 0x8A, 0x18, 0x21, 0xBE, 0xFC, 0xBC, 0x1C, 0x8F, 0x4B, 0xC0, 0x6C, 0x2B, + 0x57, 0x4A, 0xE8, 0x34, 0xA8, 0xF3, 0xB4, 0xBF, 0xBF, 0xD7, 0xA2, 0x99, 0x36, 0x70, 0x60, 0x41, + 0x00, 0xC0, 0x26, 0x26, 0x1F, 0xF9, 0xAE, 0x21, 0x48, 0x0D, 0xDD, 0x98, 0xAE, 0x89, 0xB4, 0x71, + 0x54, 0x02, 0x02, 0x16, 0x9B, 0xEA, 0x17, 0x43, 0x09, 0x77, 0x60, 0x83, 0xD2, 0x03, 0xE5, 0x99, + 0xE8, 0xA1, 0x9E, 0xDA, 0x1E, 0xF5, 0x20, 0xFE, 0xA1, 0x3F, 0xD9, 0x9E, 0xD0, 0x9D, 0xA6, 0x24, + 0x80, 0x94, 0x52, 0x90, 0xED, 0x5C, 0x2D, 0x6C, 0x4F, 0xB3, 0xCB, 0xB2, 0xC9, 0x9E, 0x51, 0x1B, + 0xA7, 0x30, 0xF3, 0x4E, 0x36, 0x49, 0xB6, 0x07, 0xCD, 0x4A, 0x8F, 0xD7, 0x28, 0xBB, 0x67, 0x9C, + 0xFB, 0x18, 0x9A, 0x31, 0x78, 0xEF, 0xF1, 0xBD, 0x48, 0x04, 0x67, 0x4F, 0xB1, 0x4F, 0x7C, 0x57, + 0x70, 0xA4, 0x43, 0x57, 0x34, 0x4C, 0xF6, 0xF1, 0x2E, 0x70, 0x22, 0xEB, 0xBD, 0x06, 0xEB, 0x3D, + 0xB1, 0xA3, 0xA0, 0x6E, 0x38, 0xAF, 0x7B, 0x62, 0xA9, 0xF1, 0x1C, 0x9D, 0xFD, 0xE7, 0x0C, 0x4C, + 0x80, 0xD4, 0x95, 0x96, 0x6B, 0xD5, 0x60, 0xC4, 0x33, 0xB7, 0x47, 0x3A, 0x64, 0xA3, 0x7C, 0x99, + 0x21, 0x05, 0x5B, 0x64, 0xDE, 0xB1, 0x78, 0x67, 0xC1, 0xD1, 0x68, 0xDE, 0x75, 0xC8, 0x26, 0x29, + 0x0B, 0x42, 0x20, 0x5F, 0xCE, 0x32, 0x0F, 0x86, 0x03, 0x08, 0xC2, 0x05, 0x46, 0x99, 0x85, 0x30, + 0xC2, 0x24, 0x90, 0x0E, 0x68, 0xAD, 0xA0, 0xBB, 0x82, 0x3D, 0xEE, 0xE7, 0x10, 0xCD, 0x3F, 0x32, + 0xE0, 0x0D, 0x2D, 0xF4, 0x58, 0x8B, 0x2C, 0x62, 0x47, 0xC0, 0xB7, 0x25, 0x78, 0x89, 0x12, 0xCE, + 0x97, 0xCD, 0xEF, 0x35, 0x5D, 0x8C, 0x06, 0x75, 0x88, 0x1E, 0xCC, 0xFA, 0x13, 0x46, 0x09, 0x96, + 0x62, 0x01, 0x58, 0x97, 0x12, 0xD1, 0x8A, 0x99, 0xD8, 0x2D, 0x71, 0xE2, 0xC5, 0x8E, 0xAA, 0xF1, + 0x78, 0xE7, 0x59, 0xF1, 0xD4, 0x86, 0x4E, 0x08, 0xBF, 0x56, 0xBC, 0xFE, 0x5E, 0x9E, 0xC2, 0xE4, + 0xF2, 0x74, 0x42, 0x5E, 0x42, 0x68, 0x64, 0xB3, 0xEE, 0x85, 0x3D, 0xB4, 0x16, 0x6F, 0x47, 0x96, + 0xEF, 0xBD, 0xD7, 0xB4, 0x57, 0x41, 0xD6, 0x28, 0x4C, 0x68, 0xE2, 0x40, 0x1D, 0x44, 0x18, 0xF7, + 0x77, 0x65, 0xC0, 0x45, 0xAD, 0xBB, 0xE8, 0x03, 0x5B, 0xF2, 0xA5, 0x8F, 0x8D, 0x42, 0x95, 0x5E, + 0x8F, 0x9D, 0x20, 0x10, 0xE0, 0xE3, 0x47, 0x2C, 0xA9, 0x20, 0x7D, 0x53, 0x54, 0x91, 0x1E, 0x57, + 0xA2, 0xEF, 0xDB, 0x1E, 0x6D, 0x53, 0x77, 0x86, 0x7C, 0xA9, 0xE8, 0x12, 0xEC, 0xFD, 0xE3, 0x6A, + 0xE9, 0xE4, 0x16, 0x5E, 0x15, 0xD3, 0x4A, 0x9D, 0xD3, 0x7B, 0xF4, 0xCB, 0xDC, 0xAC, 0xB8, 0xB3, + 0x2A, 0x32, 0xDE, 0xC0, 0x03, 0x83, 0xC6, 0x16, 0x56, 0xD6, 0xE0, 0x4C, 0xB2, 0x51, 0x61, 0x06, + 0x4C, 0x4A, 0x2F, 0x66, 0x53, 0x43, 0x15, 0x28, 0xDD, 0xB6, 0x7F, 0xB4, 0x06, 0x1F, 0x77, 0x02, + 0xC7, 0xD9, 0x94, 0x07, 0xED, 0xBF, 0x5D, 0x1B, 0x74, 0xF0, 0x6B, 0x35, 0x9B, 0xFB, 0xB6, 0xE9, + 0xDC, 0x80, 0xB0, 0x9B, 0xF8, 0x3B, 0x7E, 0xB8, 0x02, 0x7F, 0x6A, 0xD0, 0x38, 0xB7, 0x8F, 0xEC, + 0x08, 0xE7, 0x05, 0xC4, 0xDB, 0x28, 0x5C, 0xE5, 0xBD, 0x68, 0x1E, 0x84, 0x8A, 0x8B, 0x1A, 0x4F, + 0x97, 0x11, 0x5D, 0x30, 0x78, 0x77, 0x8A, 0x69, 0xCE, 0x65, 0x33, 0x7F, 0x5C, 0x3D, 0xA4, 0x6B, + 0xE9, 0x9F, 0xAA, 0x1E, 0x3A, 0x51, 0x2B, 0x38, 0xA9, 0xBB, 0x13, 0x1E, 0x09, 0x3C, 0xDD, 0xF7, + 0x91, 0x74, 0xAF, 0x04, 0xB2, 0x48, 0x9F, 0xA1, 0x1B, 0xEF, 0x46, 0xBA, 0xA5, 0xB5, 0x87, 0xF6, + 0x1C, 0x99, 0x3B, 0xA5, 0x90, 0x54, 0xCC, 0x0A, 0x73, 0xF9, 0x2F, 0x44, 0x13, 0xC6, 0x1F, 0xDE, + 0xB7, 0x83, 0x64, 0xFC, 0x72, 0xD1, 0x8E, 0x6A, 0x33, 0xA6, 0xFE, 0x4C, 0xD1, 0x8E, 0xF8, 0x0E, + 0xC7, 0x52, 0xD4, 0x9C, 0xCB, 0xC4, 0xE7, 0x8A, 0xC9, 0x32, 0xA1, 0xB4, 0xB7, 0x50, 0x97, 0x82, + 0x51, 0x60, 0xD0, 0x55, 0x43, 0x30, 0x89, 0x94, 0xE6, 0xD2, 0xAD, 0x32, 0x98, 0xA4, 0xB0, 0x3C, + 0x4D, 0x80, 0xE8, 0x61, 0x9C, 0x00, 0xD3, 0xB4, 0x22, 0xB2, 0x9E, 0x96, 0xF3, 0xFC, 0x52, 0xB9, + 0xD6, 0xB8, 0x5F, 0x2C, 0xE7, 0x39, 0xB7, 0xC5, 0x9A, 0xB1, 0x4E, 0x9F, 0xFE, 0xA6, 0x09, 0x90, + 0xBB, 0xED, 0x51, 0x53, 0xC7, 0x6F, 0x56, 0x20, 0xF0, 0x8A, 0x7B, 0x4D, 0x3D, 0xDC, 0x18, 0xA9, + 0x0A, 0xC1, 0x6E, 0xD2, 0x92, 0x8A, 0xB4, 0x25, 0xE5, 0x92, 0x0B, 0xB1, 0x28, 0x2A, 0xB6, 0x68, + 0xB5, 0x46, 0x9B, 0x86, 0x1D, 0x15, 0x1E, 0xC9, 0xC5, 0xE2, 0xEA, 0xB5, 0x60, 0x2D, 0x70, 0xAA, + 0x1B, 0x2A, 0x19, 0xEF, 0xAF, 0x96, 0x1D, 0x34, 0x7C, 0xBF, 0xEC, 0x80, 0xCB, 0x57, 0x86, 0x7B, + 0x39, 0x31, 0xDC, 0xB6, 0x91, 0xE1, 0x42, 0x5C, 0xDE, 0xE0, 0xA8, 0x20, 0xAA, 0x85, 0x30, 0xA8, + 0x12, 0xEF, 0x2A, 0xE9, 0x34, 0x8C, 0x7D, 0x52, 0x93, 0x63, 0x63, 0x7F, 0x6B, 0x6E, 0x19, 0x29, + 0xD0, 0x5F, 0xAA, 0xC3, 0x55, 0x61, 0x95, 0x1A, 0x61, 0xD4, 0x86, 0x06, 0x13, 0xA2, 0x8B, 0x91, + 0x99, 0xD3, 0x57, 0xD2, 0xC2, 0xA2, 0xE3, 0x7D, 0x86, 0x33, 0xAE, 0x0D, 0xE8, 0xF9, 0xA8, 0x7C, + 0xA5, 0xAB, 0xAE, 0x95, 0xAF, 0xDC, 0x14, 0x9C, 0x25, 0x25, 0x69, 0x4A, 0x05, 0xDA, 0x0D, 0x91, + 0xB3, 0xFC, 0x9B, 0xC5, 0xAE, 0xA9, 0xB3, 0x4C, 0xAD, 0x28, 0x56, 0x72, 0xBA, 0x52, 0x54, 0xE3, + 0xC6, 0x08, 0x20, 0x5F, 0x22, 0x67, 0x39, 0x2F, 0x71, 0x96, 0x52, 0xA5, 0xFC, 0x46, 0xEA, 0x2C, + 0xA5, 0x2D, 0xD9, 0xDB, 0xCF, 0x16, 0x11, 0xCB, 0xDE, 0x54, 0xB7, 0x14, 0xF3, 0x9D, 0x06, 0x64, + 0x5D, 0x20, 0xC7, 0x4C, 0x92, 0xDC, 0xD4, 0x15, 0xA4, 0xCE, 0x72, 0xD2, 0xD7, 0x9D, 0x65, 0x2A, + 0x2C, 0x40, 0x4C, 0xDD, 0x49, 0xBA, 0xD6, 0x7E, 0x5C, 0x5A, 0x27, 0xDE, 0x7F, 0x5C, 0x5E, 0xA7, + 0xB3, 0x3D, 0x49, 0xE1, 0x5C, 0xE4, 0x4A, 0xFE, 0xD8, 0x08, 0x6A, 0x89, 0x8C, 0x14, 0x0C, 0x52, + 0xF6, 0x7E, 0xBA, 0xB6, 0x93, 0xDE, 0xD2, 0x07, 0x84, 0xD1, 0x5F, 0xBB, 0x5F, 0x4F, 0x02, 0xC2, + 0x92, 0x5A, 0xEC, 0x2D, 0xEB, 0x96, 0xC5, 0x88, 0x43, 0xEE, 0x97, 0xBE, 0xC9, 0x61, 0x5C, 0x1D, + 0xA1, 0xD3, 0x9C, 0xC8, 0xD0, 0xED, 0xE5, 0xFB, 0x7B, 0xF4, 0x86, 0x62, 0x0B, 0x32, 0x6F, 0xBB, + 0xCD, 0x96, 0xB3, 0xC1, 0x8C, 0x51, 0x3F, 0xDD, 0xCF, 0xA1, 0xFE, 0x13, 0xFA, 0x30, 0xF9, 0x97, + 0x1A, 0x36, 0xBE, 0xEE, 0x3C, 0x02, 0xF9, 0xB2, 0xF7, 0xAA, 0xED, 0x38, 0xC7, 0xB0, 0x90, 0x89, + 0x0F, 0x99, 0xA4, 0x2B, 0xE9, 0x5E, 0x7C, 0x6E, 0xFC, 0x3E, 0x14, 0x76, 0x13, 0xBC, 0xDA, 0x30, + 0xBE, 0xF9, 0x64, 0x10, 0x96, 0x0F, 0xCE, 0x2B, 0x2E, 0x64, 0x37, 0xDB, 0x24, 0x5F, 0xE5, 0xE6, + 0xA2, 0xDC, 0x27, 0x91, 0x35, 0xBF, 0xF2, 0xD2, 0xC6, 0x1F, 0xD5, 0x86, 0xEF, 0x52, 0x7F, 0xEA, + 0x9D, 0x7D, 0xEE, 0x48, 0xC5, 0xF6, 0x27, 0x84, 0xAD, 0x9D, 0xEA, 0x2B, 0x73, 0x05, 0x0E, 0xC7, + 0xBC, 0x72, 0xD4, 0x35, 0x21, 0x47, 0xA5, 0xB0, 0x69, 0x5E, 0x43, 0xEB, 0xB4, 0x1A, 0x5F, 0x29, + 0xBF, 0x03, 0x12, 0x42, 0xFC, 0x04, 0xEC, 0xFA, 0x57, 0xE1, 0x42, 0x89, 0xF4, 0x2E, 0x31, 0x5C, + 0x40, 0xF3, 0x41, 0xA8, 0x9D, 0x2B, 0x39, 0x59, 0x02, 0xE7, 0x6B, 0xCF, 0x5B, 0x69, 0x2E, 0xD8, + 0x23, 0xF3, 0x14, 0xF8, 0x60, 0xEE, 0x77, 0x28, 0xE3, 0xFD, 0xF5, 0x87, 0x05, 0x1F, 0x5A, 0xE4, + 0x4C, 0xB9, 0x45, 0x2E, 0x5B, 0xC4, 0xCD, 0x10, 0x57, 0x21, 0xC6, 0xA9, 0x2D, 0x6E, 0x71, 0xB1, + 0x6B, 0xE6, 0xF7, 0x10, 0x37, 0x4F, 0x6A, 0xA9, 0xA9, 0x09, 0x05, 0x8A, 0x52, 0x0D, 0xB9, 0x71, + 0x0F, 0x1C, 0xFC, 0xC9, 0xF6, 0xB8, 0xB4, 0x3B, 0xCC, 0x3D, 0xAC, 0x4E, 0x3D, 0xD2, 0x5B, 0xFD, + 0x73, 0x51, 0x20, 0xA7, 0xC1, 0x05, 0x36, 0xC0, 0x08, 0x07, 0x78, 0x9C, 0xD3, 0x2E, 0x75, 0x75, + 0x78, 0x6F, 0xA1, 0xDC, 0xA9, 0x43, 0x87, 0x7B, 0xA1, 0x42, 0xE8, 0x0F, 0x73, 0xC3, 0xD1, 0xFF, + 0xE4, 0x2A, 0x8C, 0x9A, 0x44, 0x80, 0xB9, 0x9E, 0x79, 0x52, 0x26, 0x68, 0x2F, 0x00, 0xED, 0x05, + 0x44, 0xDB, 0x01, 0x01, 0x59, 0xD0, 0x1A, 0x1C, 0xB4, 0x63, 0x36, 0xE0, 0x07, 0x8A, 0x04, 0x49, + 0x2D, 0x80, 0xC0, 0x28, 0xBA, 0xC0, 0x53, 0xAF, 0xC2, 0x5E, 0x87, 0x78, 0x60, 0x98, 0xF6, 0x1D, + 0x40, 0xFC, 0x57, 0x20, 0x7A, 0xD9, 0xA3, 0xF5, 0x5E, 0x3C, 0xEA, 0xF6, 0x10, 0xB8, 0x5E, 0x27, + 0x55, 0xA1, 0x6C, 0x11, 0x73, 0x98, 0x2D, 0xCD, 0x8A, 0xB9, 0x1A, 0x98, 0x83, 0xC0, 0x0B, 0xFE, + 0x20, 0xEC, 0x08, 0x5E, 0x43, 0x0A, 0x4F, 0xF9, 0x8C, 0xD8, 0x8B, 0x60, 0x8D, 0x07, 0x6D, 0x39, + 0x76, 0xDF, 0x81, 0xB6, 0x5A, 0x44, 0x5B, 0xAE, 0x58, 0x25, 0x05, 0x46, 0x41, 0xB5, 0x1E, 0x02, + 0xF1, 0x16, 0xFB, 0x0C, 0xD5, 0x8D, 0x82, 0x03, 0xEC, 0x3F, 0x6F, 0x00, 0x17, 0x5D, 0x29, 0x1A, + 0x3C, 0xDB, 0x0D, 0xEC, 0x96, 0xF9, 0xB0, 0x0E, 0xB7, 0x57, 0xEE, 0x6B, 0x99, 0x94, 0x2F, 0x77, + 0x37, 0x1B, 0x7A, 0xDE, 0x82, 0xCF, 0x63, 0xD3, 0x76, 0x5A, 0x1C, 0xD9, 0x28, 0xFB, 0x25, 0x1C, + 0x6F, 0xD7, 0x79, 0xDA, 0x56, 0x6B, 0xE1, 0xC8, 0xBE, 0x39, 0xFF, 0xA2, 0x0F, 0xDF, 0x9C, 0xDB, + 0xA4, 0x6A, 0x47, 0xF8, 0xB5, 0x31, 0xD7, 0x0D, 0x71, 0x61, 0x96, 0x2C, 0x7F, 0xAE, 0xCF, 0xBF, + 0x30, 0x5E, 0x17, 0x58, 0x80, 0x58, 0x1E, 0xB5, 0xC5, 0xED, 0xB9, 0xF5, 0xBB, 0xA0, 0x08, 0x8B, + 0xE6, 0x2A, 0x73, 0x39, 0x18, 0xED, 0x75, 0xBC, 0xD7, 0xF8, 0x71, 0x03, 0x2A, 0x10, 0x84, 0x2E, + 0xE3, 0xD1, 0x15, 0x91, 0xA4, 0x05, 0xE6, 0x84, 0x43, 0x56, 0x28, 0x58, 0xCE, 0xBF, 0xB8, 0x25, + 0x88, 0x84, 0x19, 0x4D, 0xC5, 0xF6, 0xB6, 0x78, 0x91, 0x10, 0x17, 0x67, 0x1D, 0xE4, 0x45, 0x47, + 0xEE, 0x4E, 0x57, 0x24, 0x92, 0x63, 0x62, 0x2B, 0x0E, 0x9B, 0x07, 0xA3, 0xB3, 0x91, 0x71, 0x24, + 0x2E, 0x2A, 0x0B, 0x75, 0x84, 0x2D, 0x70, 0x8C, 0x49, 0xD7, 0xB9, 0x25, 0x6C, 0x67, 0xDC, 0xF3, + 0xB4, 0x26, 0xF4, 0x14, 0xAE, 0xB1, 0x89, 0x6A, 0x40, 0x1C, 0x15, 0xC5, 0x12, 0x2F, 0x9F, 0x73, + 0x2C, 0x3B, 0xA3, 0xAB, 0x95, 0x40, 0x87, 0x06, 0x81, 0x0E, 0x15, 0x5E, 0xBA, 0xF2, 0x4D, 0x85, + 0x44, 0x07, 0x25, 0x86, 0x42, 0xA9, 0xAA, 0x00, 0xA0, 0xCC, 0x75, 0x61, 0x3C, 0xBB, 0xC5, 0x38, + 0x09, 0x60, 0x7E, 0x37, 0xDE, 0x33, 0x4C, 0x44, 0x6D, 0xB7, 0x70, 0xB3, 0xA6, 0xB9, 0x32, 0x12, + 0x3E, 0x70, 0x98, 0x90, 0xAB, 0xC6, 0xB2, 0x6F, 0x8F, 0x7D, 0xDE, 0x23, 0x42, 0x7A, 0x88, 0x89, + 0xB9, 0x0D, 0xFD, 0xC1, 0xEB, 0x0F, 0x55, 0x3B, 0x03, 0x65, 0xD3, 0xA3, 0xD2, 0xFB, 0xDE, 0xAE, + 0xD2, 0x75, 0x6D, 0xCA, 0x4D, 0x0D, 0xF8, 0xD7, 0x2B, 0xFF, 0x09, 0xCF, 0x97, 0xD6, 0xB6, 0xF6, + 0xCA, 0x5D, 0x85, 0x77, 0x26, 0xEB, 0xDC, 0xA9, 0xD8, 0x1E, 0xB5, 0xEF, 0x84, 0x07, 0x60, 0xE6, + 0x08, 0x0E, 0xB9, 0x7D, 0x8E, 0x25, 0xB8, 0x94, 0x7A, 0xD7, 0xB8, 0xBC, 0x38, 0x69, 0xC6, 0x27, + 0xC2, 0xA8, 0x5D, 0x99, 0x76, 0xA4, 0x56, 0xE5, 0x69, 0x69, 0x30, 0xAA, 0xAB, 0x59, 0x4F, 0x0E, + 0xE3, 0x27, 0x30, 0x88, 0xE5, 0x1F, 0x61, 0xC8, 0x55, 0xB0, 0x8E, 0x1F, 0x5D, 0x3F, 0x7D, 0x39, + 0x79, 0x91, 0xD4, 0x92, 0xC4, 0xA5, 0x9D, 0x4B, 0x5C, 0xE6, 0xFC, 0xF6, 0x63, 0x2B, 0x37, 0x25, + 0x4F, 0x40, 0xC9, 0xC6, 0xFC, 0xB5, 0xC7, 0x4C, 0xAE, 0x99, 0xB9, 0x6A, 0x68, 0x83, 0x69, 0x64, + 0xCC, 0x07, 0x63, 0xCC, 0x3C, 0x93, 0x99, 0x2D, 0x66, 0xAB, 0x69, 0x30, 0x53, 0xF0, 0x6E, 0x2B, + 0xFE, 0xAE, 0x37, 0xAB, 0x4D, 0xF1, 0x67, 0x86, 0x59, 0x67, 0x36, 0x98, 0x0C, 0x9F, 0x1B, 0xF0, + 0x3E, 0xC3, 0xFB, 0xAD, 0x1C, 0xBB, 0x83, 0xBF, 0x2E, 0x33, 0x1B, 0xF1, 0xCD, 0x2A, 0x8C, 0xAD, + 0x99, 0xC5, 0x26, 0xE7, 0xEF, 0x0D, 0x66, 0x2A, 0xBE, 0x5D, 0x8F, 0x57, 0xCD, 0xF8, 0x3F, 0x43, + 0xCC, 0x20, 0xFE, 0xED, 0x6F, 0x06, 0xE3, 0x7D, 0x7F, 0xBC, 0x1B, 0x8D, 0x6F, 0x97, 0x16, 0x70, + 0x89, 0x69, 0x07, 0x31, 0xAF, 0xE6, 0x27, 0x63, 0x16, 0x63, 0xEE, 0x36, 0xC2, 0x6D, 0x30, 0xB3, + 0xCD, 0x34, 0x33, 0x06, 0xFF, 0x0F, 0x06, 0xD4, 0x56, 0x66, 0x26, 0xC6, 0x6C, 0x28, 0xB0, 0x91, + 0xCA, 0xD5, 0x1C, 0xB1, 0x82, 0xB3, 0x97, 0x61, 0xD6, 0x22, 0xD3, 0x10, 0xCD, 0x5D, 0x08, 0x68, + 0x99, 0xD9, 0xC1, 0xF1, 0x0D, 0x8E, 0x86, 0x11, 0xC4, 0x3E, 0x9C, 0xFF, 0x0E, 0x01, 0xAE, 0x2F, + 0x11, 0x23, 0x48, 0xC8, 0x5B, 0x60, 0x00, 0x00}; diff --git a/Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C_trimmed_optimized.vgz b/Frameworks/GME/vgmplay/XMasFiles/SWJ-SQRC01_1C_trimmed_optimized.vgz new file mode 100644 index 0000000000000000000000000000000000000000..97fc202ac3b80f80b873fe3f7e96e6098ed296ee GIT binary patch literal 6408 zcmV+j8TaNNiwFSJdM{D}0MwXQ%nnfyhR6DSdJRHc5E3p3q9uC!l>{Nt;zBtgBofiB z7QOf0d+)s)+-)R?9-;)7Zb)26M3iTb!{T7o;7ca)t(|w?cV^C<`Tj2b29)dCOJr~k z`S5AlQ{-{Ua&onRNTnWS-GlR5HDJ>kB)VpB0K5U<2>^8fSOY)?fXe{90RTINorZ|^ z17HIH0surZy)}2%AT83?R1Gw5&7qnP%U{#n$Y{RoA(}ti%*@if*`{n>&D+`Bzk&Fj`rOa7 z_{`^q6H1EXSjZbPS~Jfycg}a}hN2Ft)*Xc<^1fHNA`6Ao92w{&To{L|?^VYSmq_ai z#pOOlmlR*t!GbOh6pxtd@3SAq{N`DHB|q?6Zuyn7{HUgcNV}61!H+f6@%yOb#>4Lg z{C-5!G<-^G&J1qf8|t77b$_I0ozh&nMrbBma|fo16PO&i8LswNBG$HSC$l6LOizFq zA1e;x`_iljdkm#X=aa}-iBF$#npwu7$Ir1SpRgDK12o1M?E|`&A>b0?TBAYW@a#yC zp)s2)u$em&HX}5vhUUtcT8>l>OBrGaL4pI#CX}T|J=+8%!3GIOBxf@A5YsJqH>BRR z#0Zxy*RtqxhrFCw{aMql3{mMfg(CnN2>AKwpC7WkqbfHxm+Ng7!u5A8A#7yz>Rm`BE_--F@_X8p1e3Q!I-GUuQ8ye?*{*i6 zU9Duhn!$DzvR(CMyXwq#mCbfllkKWJ+f~uQlyj6l{JBT7np|KtxyNeqn$_eDs|j}W zDXYmPRug44sl{qifz>1jtBFLS*qI`%CN)@1nzNdmk57GBr(`7@vHUAd6By5+vNM=M zXbUyp44cc4JcVv*wTo5pPG2_}-_6kw|ITC*-L5;b=?+x8#etroQZW`!f`#CfH+Arr zp~T0(v&nX`i!wXjMHJJ*Rg;LUw?z7|{>0gLv(+>YSbE9oB(gf0tWF`TQ_1QyvO1lt z&Inn0$?7b!I-9J{A**xA>O8V43pGb($U9bdSU0Tjz>z8n0Z7@Pp*@s2NQwA_C_QB2 zCvrz~!-XD93%_Ll)>>{*w|t&aQsp*EjJ+O z=b#VV>@W?6IBuypiN-z&w}lY%8X5(gHGy^@Kf(KgaN^|FWt0(Zmj!*t=K*h{MHZak%LC{+>C@ zAZuR|)n0!y-Om;l3LCxc=O?kAEsYC-dy-ZUG?Bu)4$$kF@q;w;NOR{#0((}1UC!=V zb~|vyzR?1_Uj+Qr13To4oWsvK&-XZct;O%HzA@#hA47Y0cM5dMc}`NCbjsIKoYc&} zFWF<4vy+c}*DHf;raxv^M;-d+hSs)JAOY z%Hwo^A!bJE#33G9TBO`psZ|*HZt{d5wNm9j(n?!Dx|^JcADB5Qy*YmHS7wgaNI%`K za893-ExbnhzM1nUd&r+hzdkp#C+$8W3k4G2Xee6mKENe8(1NAUiUQ}<4bk2%@o=cJW2O3bM3&4sV? z=ibZ>_P!TP2yW+3+~%muCP!xnd_lEfp5pZj_y>xYfPS?I;B2!eQ}RTJ>?&eFvRjV* zqsMNancB|Nb8kJ_6nke+1nkGl7g=XdeUo)&uN>?ZierSxvYoqt?7fVYkYGKQw9O^( z?yE-hm;YXOftpx-MEZQKXO<&oaK+j(pQOR3J0N#~F4{Lc(cRQ8oW z9@uQ>Fh_Fv~me!yZA5YY-r7QyoTb{3wja zkbnc1Iw;<`Ba@{g*DFxy7m%NkK7cbjo46egSxzVi=gC|>kK5tkl_c{$%ae(x0(PNG zGImc;fi`D-D%KlbnZV>g%Iv|lTwoks#nCA6=^7EwXaEr29UWZYE)}@u*wAd=`SnF~ zN02ZI3`dT+*MrH}i-FDoG%_li+NF0pgzyRo4~`j<{C8l*TPW?MX@@$3$C{4yh`+8y z^@1VmXL5h@v&y<=^LRdp^9Is-&gyCGsUow_LVBnGlqDEeQ!vpKK+$Ll5P*4Z*%eF) zan~?F#H~2G4K{_8%&=+sns%Dkk2UeXB9Rj`;sl+ie1mHZ0g|Ky6}}eQ&+H`%AP&)} zQ0X&JF-wn@@XfmPt(lgBjNrUNr4p!+D&&2_@R<^=4&S11=f+oY@jA>5fX{{Jmw9pF z8E9Ex9CS$nCoQ$p0cyfLGvU3BXghRoSOZ(2gt)p*40z)TE1~Wi`Ebk%ZjJLsP;jgg zRl&7-{unU|d+mZ@mBAL7U5ba>#A3np3d|}b10hL7T(m;*{&!M_5G<&EV{%utY|7}KB+U+OXraP#ws8jDU)E5Il>x+J?JFTxnxbwitmm#me zqQ30Mpw-)`)_1Zg$q!^a?qjctc!#y{hjHN#c%ZTo)v6QQvJLH=s`;H>k!+l-n;G_>%P#%8%Y;^t^n_ z1DIeX!x$bAQ}n_Vi4%DmNS};k5-10v{`VphJ;W!?f)S%JzHtjQK0FJ4JV=WWA0=AM z5dK{p|9i8P-2GqsF_oA?h$Ui1p^KzkF)5?%|MqevuRldm{F+0GluYG^LS&xjc_t#P zES@P!hA+O)tYo2NiUm@XSfHe2%71_;3kxh1cCw}npZB@L=iPVB6Mgv(_n!HlbMC$8 zysh^duesNxMUEUG&XHoN*4wf*nNV;WSNPp!@ne#B` z+-1sZN>>+4bSecU-S685j9XS2<-lyIGUQ6qD~LEMbfnQEClj%wyaKj4SOwRpMy&IZUluO^q~^E#KIy_SL$8|50%}xthnMS!+!OkOPnY@c}trvF|m5yyrAs~ zy(PzD1RFg#?oIQsT;(roex;Z!YYoadwk;h}5AX+Bf!@Vf^4Wwa;eFhkhf$HUT!Y%- zaP-83s&h^4Iz>Y0v;&yZjB>Sk?&?bs9^c!@P7hA=q+49$?ib0Eyg6OdkbTj4+Fz0B zv*wHoe^sV*WB*5>GVkf%5~$lmoU90RNZmTOpOq;{A_spiQ$=*UZxJ3v^!hgVEK zdMa8HCecm~qCMYK@qO$+m#!i@&rN>p41&ojs)HgLisQhEL{SxZ5XQb2QR9d%ml_|T z$*mH9?o~0=yUaMx*`fdoeid9v(T{VCeL1qp@1>H8Qd-RE(xKrUigX+s-dq_Tv?IeC z9#Toke&&x-$s7wh+V#@v(&gaNN$qkkG>uCNw66t;3zSl7UrSLF?f*(9CE&>P$Wm&0 zKTz~xnVyR*+mmFnUy@AHn~Dx#k@X4*nMagOv%i%&6Vyvp6S^;;qOIi0NDXW{6 zZtmHOqk@ByC^5ilG-q ze%vcr4j>nb_VLBU_-~*bqGTuCHl}D@)+zI-%QR+cbk0&5&Tu)kW9gWO?K);LN3do4 zRl~2RMo)(DZYY?HF(!teEl!QTED%s3-Y|kEadv9{Ev3cm$U*Hj)DUu`zO8|JV|@qF(Sm>II*pT<{ah1mB@d@FB_s@1aa^ z7|H~DqD-(6$^+Y?Ja7og1E-=q@I?N-R?JP`>&4u3ukl8R@YTI$jklv06XE0fXFeuE zy_k62#OoGbw+(tR@w#hDy_m*5Cc@x;W8S?M%{LXze0`V#c{6*^+*W+S!zsT*M98hz zK@R7Nc-+?FAQGjbhx4K9Owa9Rjj{c5b{g3k7XTn*9H63S8nCXk0Y1)t&e@X+T#z+j z@|(+m0un(3X1Z(;=<@`%+RI7H7Fi-2`l@u6KK8;@gvdWpvIatGTY{_=vviX;I zww=`}&n|U}d7k$To=u78N6e+kt-}jiL6>-5;&^_7k<&1f8BiI4c(ZoiL`N4 z0sK8)^cVL6k1Lc|Mp`O|v^&tMCKiQtpou(v!lv0rGTrF%*v&*u{o>3d8F!N3} zNwx>gN{`nlyJwvH7@9G7@A17z1ZPjNPkdK!q(fIUO!o0Ea3bry2J1bsqo8g$uX|#t z@f@A~=L}4M)RmTN)nLRkw?{f;qkNem1zTj^v3O^}(P-Xv$R;TZLLgtxG7p9W2*L$M znT0UIB#;hht)RPtJ?`fa%|9~W4K4Ioi!5SCz_%rMi4xA2&F?i`j0Sax9?bd^MhTW; z0a%w3(TbVaEpg(-j;ir^=UMU8hE52-R=oaRp2FnvbV6Q2Xk@eQg*~(uZ%3Bzz1Or? zLDnctXyQN}L>TvXWxz$PyXXU3@}-ZBLX}>Rogfh4<3}v1AbnF*kseo~@7o@2Q+I}Z zspt~y{o`useXdZ-$iyBXt z5nV8NcZzAwWix+VJ)~>tpQ;`@Q7bs9yAvJ>Jl*$^bgu-mNS~n_??$?%wTJc`nLDMB zRLlx<`7cBh#vk6dgJk@2(T-{}rv6OPj`$A8Qq-Kw#OI31GNH7$P?v&HV9-@VFo~4r z(ycO>q_8|qfaqbI0MoQ0vYwXn{8G8rxL+*iIk&}{u}+`^oC*!GH<)hmb1Z(Q9s?!q5c#4F!`4nHoopW0{OD{s_i((u#kxlnWQV} zUVLu+J`{EnyxK6n`D75556&wtWH;L+U*+ba+$WJWzw*+?AJvBMQh(~5eeOub_9uj` zo$4!d1rEo&a?~{;N2Rc7UPJ4&8eb{D0}?{~1nl}(;X;Y@E-_p{^FgScD>+#L=WEYf zX)f3!^Az}C?sq8T{q=_hA6n#0xkWBp#LW;_A;zgKZgH_|=JyacPim>D2?dH$4Y_d- z9Q?`lxU@UWJ*-YW(p&v=Q6Q(m1vbD42Y8&*E_HQyZ=u|&Lx+2zLg){3!_oibDvX*$ zfVrM|QYL6G0PO`t+W`St&>9@HV>aLiiUdh5fWRoaz*DcnUWa&KnD!k&{8u1)*`xJd zJnHrkxLzk!p==Rzm@UnUxf+;*zzhB$>fa-+{JTL|d*{B?U%7 zwH^ZT7W)jlrMKT8+zV?&tubeS{&IZ`z0K2 z*Lm7jYvIVgIsc*$-<;c|YDf6BG1m=oVU{d^uFt-?YZ^4pa_fRDswp) z?RC7@_;CX&5QHw{(G`)T1?Gf9Rw!7`zqlocgc(i6_O^JD5Eo}1)ff>9<}d;2Vmw14!M1f zCAgG&*SNemX~rkQs9l+Mq*mo=X)vm*Sx*k*CoqWRA7RKo%iK*`4ZwZ}}ZnYn6cgK1>U2V=}UlbJ1Mt7$M(@NO&qt~aYq@n?ovXEvA& zpBwN#!~3lqyMwEDvYg}#U|(P4Q497U5{7I_&-C;ApY%d2IGByyc>;gmlkk| zX$LW{<|ktoW9~M>Z7{RVG&2VO4+hj$W+ujLD6o;r)f};cvzKAkBGVAfT?lBI*}?G* Wj)oD$KAis!0j@6*BS^?wU;qHI?sG2y literal 0 HcmV?d00001 diff --git a/Frameworks/GME/vgmplay/XMasFiles/WEWISH.CMF b/Frameworks/GME/vgmplay/XMasFiles/WEWISH.CMF new file mode 100644 index 0000000000000000000000000000000000000000..6e6ebc6fb6783f80e4709c55f4242c96e9197b23 GIT binary patch literal 8773 zcmbtZJB(G=6+MrSN$z_>#!{pdDUc2Gfsw-q0mjA*!wka=3=HBUrm>(IN#+k`4n3^=J0#gYpbE>gS2lI5gw*4k(9bNBr`J~7hs^?mQ`@7kZU z_sm_ru+ZyGgs;OyxEbQVp8S*l@AX2jHwbx{{=?61%?^9-gs@wF_~XZ4-1^Ox}0Bwi3Si=H^BSKa%%7z2BdD`gG3&J0C(n zeE8+^iu{QG4Dk8Smk-nX4!nQo-g|ie*1!K7zrXqr-^PEvPqx4J)wZAFSd@%L@v&v^XfmGBol{<$ms6_0=YIDCr7 z=h?MEs8T!SE^>F7c`7Xn2J`WOn ziW31}SEO?6@Os!9i{x8lLY~?Ad;uaB);rjP&tdRc0AGAe6%(ucGUb=#=X2mU*W#Cz zQ%<)z@Wdyd#o1YyH{CX`#eWtw_!v#`ZZyAX7d({fQF^JIhS+!2B`U6%0qJpwJru>7 zD042;+Z872yW%9s->yjI*pUuQ%-4$$vDorq zU-%pWpGEirA5+D|njvOH6T3zTW#yZ2j_IcHWB8Sa+vc_S8;&t-gE;nB+b&|F5iOV? zti+?oU_yh`Lcv6JV4|Fi0};;EVTNcePfh_7D24ANPt&5XQxTPnTbY z$R&teSQGQ=;s7L_e;IO^iVYP_9Xg&WDgT;YYBE|=i+@(`b$Y7{q=l&)hqG|Q{R4)pgK`q5NFpR`~{r&5VC za1uC5WU;j&GR48?qJzuV+r|_pV|*E=^!2&u>(oYwk+P8EN7^!Un`*ZQ> zq+bnF%|d3EV2aEVB-oKUSXgM8=nhOBN8`ju5eF|v2U}!#V~SHzNyC)BUXH#_Es_{1 z3p?J7)7;VJ*KqyCWj6VvbeN$jPd)$9bs%FHr}jCb7L=16p*;&X zbjfvbQBHz48wOh`6OXlsiDzXODbw_eJwtCdslgK7=D&VsyZQ|&C%6k`jtp0B2@4ze_`)yZT&^S6y0O>l@p7Vn8k-%43r3Z1gz zB3vn3%2mfiIlVWDa!!JhRm#=KWKgjySIsWD#Ewu7vslxUDE8j2PGuumO<9s5&xee7 z+?hLsy%h(~N{TOc5_L&HF~5%;5Z^YNxpd^aIW^qPMF(E<#c< zwL+qL*-R4)bLH*yJ>A9aO`LI{^E`ZnkDF~W+Wd&_+l*CqJroEs$sn1^YN-KEzwD2N zF!pQu!0f`eze$Nwpq_Brr%f#SoYAnS^Sd=I;aJ0gOI)*Qd%!zdE9$nc+ayL*=`_pO z(8pudy3LeHM%v0o2T4h@$X@3cq~$Z#Z5FZ>zxui@V*?WKUvAw7JsCi&6@M*YHk>!s zZON|sx*&Urb(@$@4^v&;%~;h$*1W*FEwjBB6=&yD-=#afEe5!itJZBvcROz@n-(UV z4W7Slt9;hLPWe@syxh92jfpl-+o>>VnG%X!vWu8#-C<~19?Q1t+3U70Ok4$%44Ovv zj9#~8wnsBetY6r@zkA)5bWf|z?sXg3$3)I~p~lBl5wXah`rdlE!o@1pEMghf8AEY%J8<(fcJTYWcg;feZh4b%5GKU{*QHAvdS{a zGEA!yya#(Y{eg$(U_a$esZ+gSzVjnSu)tkz_H^oU1y1j$-8@``{8do5Ww;_m> zpuiv7JS#bVG^z)ZjH1A%MkixE*8m*rVXldc;sESX@Z?y}^#N1UscP!beA0FDcf+Of z&u9|$DBo~;H^+LoRkn~5(H&l|mOOPk?;8dQX$4BSOV20!p{gN85}I00C^~eZpEpm# zd_RA`V`O4z|ef#uPGQ zS9wZbW1T2d3);Q>)sPq|3p?JlNib5nd==*}LF94=rp9v>au9|?Ou_@i`~p(#(D zULAFy%2VWPK^bT~qiR7p)e+ji%Uv#xkTOlbSpKpkY5q#L zXAk0XlvuNxvYInoPy!4kbAiWN;)Z1uapP%$C=*+NM_G_$W0?|g3V)FyP*!YloY?7CK_6t8m4=eunKM&vk54Ej_asU7T literal 0 HcmV?d00001 diff --git a/Frameworks/GME/vgmplay/XMasFiles/XMasBonus.h b/Frameworks/GME/vgmplay/XMasFiles/XMasBonus.h new file mode 100644 index 000000000..619c04a75 --- /dev/null +++ b/Frameworks/GME/vgmplay/XMasFiles/XMasBonus.h @@ -0,0 +1,2255 @@ +// WEWISH.CMF - We Wish You A Merry Christmas +const unsigned char WEWISH_CMF[0x2245] = +{ 0x43, 0x54, 0x4D, 0x46, 0x01, 0x01, 0x28, 0x00, 0xE8, 0x00, 0x28, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x0C, 0x00, 0x90, 0x00, 0x31, 0xA1, 0x1C, 0x80, 0x41, 0x92, 0x01, 0x3B, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xB1, 0xD2, 0x80, 0x81, 0xF1, 0x03, 0x05, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x4F, 0x00, 0xF1, 0xD2, 0x53, 0x74, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x01, 0x4E, 0x00, 0xDA, 0xF9, 0x25, 0x15, + 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x12, 0x4F, 0x00, 0xF2, 0xF2, 0x60, 0x72, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x01, 0x8A, 0x40, 0xF1, 0xF1, 0x11, 0xB3, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0xA8, 0xD6, 0x4C, 0x4F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xF8, 0xD6, 0xB5, 0x4F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xF7, 0xD6, 0xB5, 0x4F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF5, 0xD6, 0xB5, 0x4F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xDE, 0x00, 0x00, 0xF7, 0x10, 0xB5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xB2, 0x03, 0x5D, 0xDA, 0x02, 0x18, 0x01, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x67, 0x01, 0x00, 0x69, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xB1, 0x69, 0x00, 0x00, 0xC1, 0x02, 0x00, 0xB2, 0x69, 0x00, 0x00, 0xC2, 0x03, + 0x00, 0xB3, 0x69, 0x00, 0x00, 0xC3, 0x05, 0x00, 0xB4, 0x69, 0x00, 0x00, 0xC4, 0x05, 0x00, 0xB5, + 0x69, 0x00, 0x00, 0xC5, 0x02, 0x00, 0xBB, 0x69, 0x00, 0x00, 0xCB, 0x06, 0x00, 0xBC, 0x69, 0x00, + 0x00, 0xCC, 0x07, 0x00, 0xBE, 0x69, 0x00, 0x00, 0xCE, 0x09, 0x00, 0xBF, 0x69, 0x00, 0x00, 0xCF, + 0x0A, 0x55, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x2A, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, + 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, + 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3C, + 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, + 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x44, 0x78, + 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x33, 0x6B, 0x00, 0x9C, 0x26, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x44, 0x00, + 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3C, 0x00, + 0x00, 0x94, 0x33, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, + 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, + 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x9B, 0x24, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x2C, 0x00, + 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, + 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x41, 0x00, + 0x00, 0x92, 0x41, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, + 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, + 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x39, 0x6B, 0x00, + 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, + 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x28, + 0x00, 0x0A, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x39, + 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x95, 0x2E, + 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, + 0x00, 0x0B, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, 0x00, + 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, + 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x35, 0x6B, + 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0A, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, + 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0B, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x35, 0x00, 0x00, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, + 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, + 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, + 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0A, 0x95, 0x2E, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x44, 0x00, + 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x95, 0x33, 0x74, + 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0B, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, + 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, + 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x28, 0x58, 0x0B, 0x90, 0x3F, 0x00, 0x00, + 0x92, 0x3F, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x33, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, + 0x94, 0x37, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, + 0x94, 0x37, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, + 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0A, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, + 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x95, 0x31, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, + 0x6B, 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, + 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9F, 0x28, + 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x40, 0x00, + 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, + 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x49, 0x78, + 0x00, 0x92, 0x49, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x49, 0x00, + 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, + 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x46, 0x78, + 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x30, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3C, 0x00, + 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, + 0x00, 0x95, 0x35, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x41, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, + 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, + 0x00, 0x92, 0x44, 0x00, 0x00, 0x95, 0x35, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3C, 0x00, + 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x3A, 0x6B, + 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, + 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, + 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, + 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, + 0x92, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, + 0x28, 0x00, 0x0A, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, + 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, + 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, + 0x28, 0x00, 0x0B, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x90, + 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, + 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, + 0x41, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, + 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9B, 0x24, 0x6B, 0x00, 0x9F, + 0x28, 0x58, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, + 0x28, 0x00, 0x0B, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, + 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, + 0x33, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, + 0x28, 0x00, 0x0C, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x90, 0x43, 0x00, 0x00, 0x92, + 0x43, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, + 0x3D, 0x00, 0x00, 0x95, 0x33, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, + 0x3F, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, + 0x28, 0x58, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x9C, 0x26, 0x6B, 0x00, 0x9F, + 0x28, 0x58, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x58, 0x00, 0x9F, + 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9C, 0x26, 0x5F, 0x00, 0x9F, + 0x28, 0x58, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, + 0x3C, 0x00, 0x00, 0x95, 0x2C, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, + 0x4B, 0x78, 0x00, 0xC1, 0x00, 0x00, 0x91, 0x43, 0x72, 0x00, 0xC2, 0x04, 0x00, 0x92, 0x3F, 0x65, + 0x00, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x27, 0x78, 0x00, 0x9B, 0x24, 0x6B, + 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x43, 0x00, 0x00, 0x90, 0x4D, 0x78, + 0x00, 0x91, 0x44, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x4D, 0x00, + 0x00, 0x91, 0x44, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x00, 0x90, 0x4F, 0x78, + 0x00, 0x91, 0x46, 0x72, 0x0A, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, + 0x00, 0x95, 0x27, 0x00, 0x00, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, + 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x20, 0x78, 0x00, 0x9B, 0x24, 0x6B, + 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3C, 0x72, + 0x00, 0x94, 0x38, 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x29, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, + 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, + 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x3C, 0x72, 0x00, + 0x94, 0x33, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0A, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, + 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, + 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3C, 0x00, 0x00, + 0x94, 0x33, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, + 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x3C, 0x72, 0x00, + 0x94, 0x38, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, + 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x43, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, + 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x95, 0x20, 0x00, 0x00, + 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, + 0x92, 0x43, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, + 0x95, 0x25, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, + 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x93, 0x3D, 0x72, 0x00, + 0x94, 0x38, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x92, + 0x41, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x95, 0x25, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, + 0x38, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x45, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x93, + 0x3F, 0x72, 0x00, 0x94, 0x39, 0x72, 0x00, 0x95, 0x24, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, + 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, + 0x29, 0x00, 0x0B, 0x29, 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x45, 0x00, + 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x39, 0x00, 0x00, 0x95, 0x24, 0x00, + 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x4A, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x95, 0x22, 0x78, + 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, + 0x0B, 0x93, 0x3E, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x29, 0x00, 0x0B, + 0x90, 0x52, 0x00, 0x00, 0x91, 0x4A, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, + 0x94, 0x3A, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x4A, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, + 0x93, 0x3E, 0x72, 0x00, 0x94, 0x35, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, + 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x90, 0x52, 0x00, 0x00, 0x91, 0x4A, 0x00, 0x00, + 0x92, 0x46, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x48, 0x65, 0x00, + 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, + 0x93, 0x3E, 0x00, 0x00, 0x94, 0x35, 0x00, 0x00, 0x90, 0x54, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, + 0x92, 0x48, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x4A, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, + 0x93, 0x3E, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, + 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x4A, 0x00, 0x00, + 0x92, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, + 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, + 0x95, 0x22, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x50, 0x00, 0x00, + 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, + 0x92, 0x43, 0x65, 0x00, 0x95, 0x27, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, + 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, + 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, + 0x92, 0x43, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x43, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, + 0x93, 0x3F, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, + 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x91, + 0x43, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x95, 0x27, 0x00, 0x00, 0x93, + 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x43, 0x72, 0x00, 0x92, + 0x3F, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x95, 0x25, 0x78, 0x00, 0x9B, + 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, + 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x29, 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x90, 0x4B, 0x00, + 0x00, 0x91, 0x43, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x00, + 0x00, 0x95, 0x25, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x48, 0x65, + 0x00, 0x95, 0x24, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x40, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x9E, 0x29, 0x4C, + 0x0A, 0x29, 0x00, 0x0B, 0x90, 0x54, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, + 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, + 0x92, 0x48, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x37, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, + 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x90, 0x54, 0x00, 0x00, + 0x91, 0x4C, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, + 0x92, 0x49, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x55, 0x00, 0x00, + 0x91, 0x4D, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x54, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, + 0x92, 0x48, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, + 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x54, 0x00, 0x00, + 0x91, 0x4C, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, + 0x92, 0x46, 0x65, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0A, 0x95, 0x24, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, + 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, + 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x29, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, + 0x9E, 0x29, 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x41, 0x72, 0x00, + 0x94, 0x3C, 0x72, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, + 0x95, 0x29, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, + 0x91, 0x49, 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, + 0x95, 0x22, 0x78, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, + 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, + 0x41, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, + 0x22, 0x00, 0x00, 0x90, 0x4B, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x93, + 0x3F, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x95, 0x24, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, + 0x26, 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, + 0x29, 0x00, 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, + 0x4B, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x3F, 0x65, 0x00, 0x9E, 0x29, 0x4C, 0x0B, 0x29, + 0x00, 0x0A, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x95, 0x24, 0x00, 0x00, 0x90, 0x4B, + 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x4D, 0x78, 0x00, 0x91, 0x44, + 0x72, 0x00, 0x92, 0x41, 0x65, 0x00, 0x95, 0x25, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x3D, 0x72, 0x00, 0x94, 0x38, + 0x72, 0x00, 0x9E, 0x29, 0x4C, 0x0A, 0x90, 0x4D, 0x00, 0x00, 0x91, 0x44, 0x00, 0x00, 0x92, 0x41, + 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x95, 0x25, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, + 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x93, 0x41, + 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x95, 0x22, 0x78, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x24, + 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x22, + 0x00, 0x00, 0x90, 0x4F, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x43, 0x65, 0x00, 0x93, 0x3F, + 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x95, 0x27, 0x78, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0C, 0x90, 0x4F, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x9B, 0x24, + 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x27, + 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x3F, + 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x95, 0x20, 0x78, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x9C, 0x26, 0x6B, 0x00, 0x9E, 0x29, + 0x4C, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0A, 0x29, 0x4C, 0x0B, 0x90, 0x50, 0x00, + 0x00, 0x91, 0x48, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3C, 0x00, + 0x00, 0x95, 0x20, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0xC0, 0x01, 0x00, 0x90, 0x3F, 0x65, 0x00, + 0xC2, 0x03, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9E, + 0x29, 0x4C, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x29, + 0x4C, 0x0B, 0x29, 0x00, 0x0A, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, + 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x6E, 0x00, 0x9B, 0x24, 0x65, + 0x00, 0x9E, 0x29, 0x45, 0x00, 0xCF, 0x0B, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, + 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x90, + 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, + 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, + 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, + 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, + 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, + 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3C, + 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2C, 0x00, + 0x00, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x43, 0x65, + 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x95, 0x2E, 0x6E, 0x00, 0x9B, 0x24, 0x65, + 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x3A, 0x00, + 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, + 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, + 0x28, 0x00, 0x0B, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, + 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, + 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3A, 0x00, 0x00, 0x93, + 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x43, + 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, + 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x95, 0x2E, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, + 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3E, + 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, + 0x00, 0x93, 0x3E, 0x00, 0x00, 0x90, 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x6B, + 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0A, 0x94, 0x3A, 0x00, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, + 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x90, 0x41, 0x65, 0x00, + 0x92, 0x41, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, + 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3E, 0x6B, 0x00, + 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x41, 0x00, 0x00, 0x92, + 0x41, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x94, + 0x37, 0x6B, 0x00, 0x95, 0x27, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, + 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x37, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, + 0x28, 0x5F, 0x0A, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, + 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x37, + 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, + 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x46, 0x65, 0x00, 0x92, 0x46, + 0x72, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, + 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x37, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, + 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, 0x27, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, + 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x48, 0x65, 0x00, 0x92, 0x48, 0x72, 0x00, 0x94, 0x38, 0x6B, + 0x00, 0x95, 0x2C, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, + 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, + 0x90, 0x46, 0x65, 0x00, 0x92, 0x46, 0x72, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, + 0x9F, 0x28, 0x5F, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3A, 0x00, 0x00, + 0x93, 0x3D, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, + 0x46, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, + 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, + 0x28, 0x00, 0x0B, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, + 0x00, 0x0A, 0x95, 0x2C, 0x00, 0x00, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, + 0x00, 0x00, 0x90, 0x4B, 0x65, 0x00, 0x92, 0x4B, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x30, + 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, + 0x00, 0x0B, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x28, 0x00, + 0x0B, 0x90, 0x4B, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x65, + 0x00, 0x92, 0x3F, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, + 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3F, 0x6B, + 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, + 0x93, 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, 0x92, 0x3F, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, + 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, + 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x3F, 0x65, 0x00, + 0x92, 0x3F, 0x72, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x28, 0x00, 0x0A, 0x95, + 0x30, 0x00, 0x00, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, + 0x41, 0x65, 0x00, 0x92, 0x41, 0x72, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, 0x31, 0x6E, 0x00, 0x9B, + 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0B, 0x94, + 0x38, 0x00, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x90, 0x41, 0x00, 0x00, 0x92, + 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x93, 0x3D, 0x00, 0x00, 0x90, 0x46, 0x65, 0x00, 0x92, + 0x46, 0x72, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x9C, + 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x94, 0x3D, 0x00, 0x00, 0x93, 0x41, 0x6B, 0x00, 0x9F, + 0x28, 0x5F, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, + 0x31, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x90, 0x43, 0x65, 0x00, 0x92, 0x43, 0x72, 0x00, 0x94, + 0x3D, 0x6B, 0x00, 0x95, 0x33, 0x6E, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, + 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x94, 0x3D, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9F, + 0x28, 0x5F, 0x0C, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x95, + 0x33, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x90, 0x44, 0x65, 0x00, 0x92, 0x44, 0x72, 0x00, 0x94, + 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x6E, 0x00, 0x9B, 0x24, 0x65, 0x00, 0x9E, 0x29, 0x45, 0x0C, 0x9B, + 0x24, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x0C, 0x94, 0x3C, 0x00, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x9C, + 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x93, + 0x3F, 0x00, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, 0x0C, 0x9C, + 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x94, 0x38, 0x00, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x9C, + 0x26, 0x5F, 0x00, 0x9F, 0x28, 0x5F, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x95, + 0x2C, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3D, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0xC2, 0x03, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3A, 0x6B, + 0x00, 0x94, 0x37, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, 0x9C, 0x26, 0x65, 0x00, 0x9F, 0x28, 0x5F, + 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x00, 0x9C, 0x26, 0x68, 0x0B, 0x26, 0x00, 0x00, + 0x26, 0x6A, 0x00, 0x9F, 0x28, 0x5F, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x00, 0x9C, + 0x26, 0x6D, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3A, 0x00, 0x00, 0x94, + 0x37, 0x00, 0x00, 0x95, 0x33, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0xC1, + 0x02, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, + 0x6B, 0x00, 0x9E, 0x29, 0x4C, 0x00, 0xCF, 0x0A, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9E, 0x29, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3C, 0x6B, + 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, + 0x92, 0x44, 0x00, 0x00, 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, + 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x33, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, + 0x92, 0x44, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x3C, 0x00, 0x00, + 0x94, 0x33, 0x00, 0x00, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, + 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3C, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, + 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x2C, 0x00, 0x00, + 0x93, 0x3C, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, + 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x9C, 0x26, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x41, 0x00, 0x00, + 0x92, 0x41, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, + 0x94, 0x38, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, + 0x28, 0x00, 0x0A, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, + 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x39, 0x6B, 0x00, 0x95, + 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, + 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0A, 0x28, 0x00, + 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x39, 0x00, + 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x95, 0x2E, 0x74, + 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, + 0x0A, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x28, 0x00, 0x0B, + 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, + 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x35, 0x6B, 0x00, + 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, + 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, + 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, + 0x93, 0x3E, 0x00, 0x00, 0x94, 0x35, 0x00, 0x00, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, + 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x3E, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, + 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, + 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, + 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, + 0x95, 0x2E, 0x00, 0x00, 0x93, 0x3E, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x44, 0x00, 0x00, + 0x92, 0x44, 0x00, 0x00, 0x90, 0x43, 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x95, 0x33, 0x74, 0x00, + 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, + 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, + 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, + 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, + 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, + 0x3F, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x95, 0x33, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, + 0x37, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, + 0x37, 0x6B, 0x00, 0x95, 0x31, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, + 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x28, + 0x58, 0x0A, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x93, 0x3F, 0x00, + 0x00, 0x94, 0x37, 0x00, 0x00, 0x95, 0x31, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, + 0x00, 0x95, 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, + 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x40, 0x6B, 0x00, 0x94, 0x37, 0x6B, 0x00, 0x9F, 0x28, 0x58, + 0x0B, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, + 0x94, 0x37, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, 0x00, + 0x94, 0x37, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x49, 0x78, 0x00, + 0x92, 0x49, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x40, 0x00, 0x00, 0x94, 0x37, 0x00, 0x00, 0x90, 0x49, 0x00, 0x00, + 0x92, 0x49, 0x00, 0x00, 0x90, 0x48, 0x78, 0x00, 0x92, 0x48, 0x6B, 0x00, 0x93, 0x40, 0x6B, 0x00, + 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x48, 0x00, 0x00, 0x92, 0x48, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, + 0x92, 0x46, 0x6B, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9B, 0x24, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x30, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, + 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, + 0x95, 0x35, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0A, 0x93, 0x41, 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, + 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x44, 0x00, 0x00, + 0x92, 0x44, 0x00, 0x00, 0x95, 0x35, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, + 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x3A, 0x6B, 0x00, + 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, + 0x9F, 0x28, 0x00, 0x0B, 0x28, 0x58, 0x0B, 0x90, 0x41, 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, + 0x28, 0x00, 0x0A, 0x93, 0x3D, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, + 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x95, + 0x30, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, + 0x24, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x90, 0x3F, 0x00, 0x00, 0x92, + 0x3F, 0x00, 0x00, 0x90, 0x3F, 0x78, 0x00, 0x92, 0x3F, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0A, 0x28, + 0x00, 0x0B, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x95, 0x30, 0x00, 0x00, 0x90, 0x3F, + 0x00, 0x00, 0x92, 0x3F, 0x00, 0x00, 0x90, 0x41, 0x78, 0x00, 0x92, 0x41, 0x6B, 0x00, 0x95, 0x31, + 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, + 0x00, 0x0A, 0x93, 0x3D, 0x6B, 0x00, 0x94, 0x38, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x90, 0x41, + 0x00, 0x00, 0x92, 0x41, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x95, 0x31, 0x00, 0x00, 0x93, 0x3D, + 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x46, 0x78, 0x00, 0x92, 0x46, 0x6B, 0x00, 0x93, 0x41, + 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x2E, 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, + 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, + 0x58, 0x0B, 0x90, 0x46, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, + 0x00, 0x0A, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x2E, 0x00, 0x00, 0x90, 0x43, + 0x78, 0x00, 0x92, 0x43, 0x6B, 0x00, 0x93, 0x3F, 0x6B, 0x00, 0x94, 0x3D, 0x6B, 0x00, 0x95, 0x33, + 0x74, 0x00, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0B, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, + 0x00, 0x0C, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, 0x58, 0x0C, 0x90, 0x43, 0x00, 0x00, 0x92, 0x43, + 0x00, 0x00, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3D, + 0x00, 0x00, 0x95, 0x33, 0x00, 0x00, 0x90, 0x44, 0x78, 0x00, 0x92, 0x44, 0x6B, 0x00, 0x93, 0x3F, + 0x6B, 0x00, 0x94, 0x3C, 0x6B, 0x00, 0x95, 0x2C, 0x74, 0x00, 0x9B, 0x24, 0x6B, 0x00, 0x9F, 0x28, + 0x58, 0x0C, 0x9B, 0x24, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0C, 0x9C, 0x26, 0x6B, 0x00, 0x9F, 0x28, + 0x58, 0x0C, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x58, 0x00, 0x9F, 0x28, + 0x58, 0x0A, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0B, 0x9C, 0x26, 0x5F, 0x00, 0x9F, 0x28, + 0x58, 0x0B, 0x90, 0x44, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3C, + 0x00, 0x00, 0x95, 0x2C, 0x00, 0x00, 0x9C, 0x26, 0x00, 0x00, 0x9F, 0x28, 0x00, 0x0A, 0x90, 0x4E, + 0x78, 0x00, 0xC1, 0x00, 0x00, 0x91, 0x46, 0x72, 0x00, 0xC2, 0x04, 0x00, 0x92, 0x42, 0x65, 0x00, + 0x93, 0x40, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x2A, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, + 0x9C, 0x29, 0x65, 0x00, 0x9F, 0x2B, 0x58, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, + 0x9F, 0x2B, 0x00, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, + 0x91, 0x47, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9F, 0x2B, 0x58, 0x0A, 0x90, 0x50, 0x00, 0x00, + 0x91, 0x47, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9F, 0x2B, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, + 0x91, 0x49, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x0B, 0x92, 0x42, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, + 0x94, 0x3A, 0x00, 0x00, 0x95, 0x2A, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, + 0x9C, 0x29, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, + 0x95, 0x23, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, + 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x3F, 0x72, 0x00, + 0x94, 0x3B, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, + 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x93, 0x3F, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x90, + 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, + 0x36, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x90, + 0x55, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, + 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x3F, 0x00, 0x00, 0x94, + 0x36, 0x00, 0x00, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, + 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x93, 0x3F, 0x72, 0x00, 0x94, + 0x3B, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x90, + 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, + 0x2C, 0x4C, 0x0A, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x95, 0x23, 0x00, 0x00, 0x93, + 0x3F, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, + 0x46, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, + 0x28, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0A, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x92, 0x44, 0x00, 0x00, 0x90, + 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, + 0x3B, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x92, 0x44, + 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x95, 0x28, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3B, + 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x48, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x42, + 0x72, 0x00, 0x94, 0x3C, 0x72, 0x00, 0x95, 0x27, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, + 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, + 0x00, 0x0B, 0x2C, 0x4C, 0x0A, 0x2C, 0x00, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, + 0x92, 0x44, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3C, 0x00, 0x00, 0x95, 0x27, 0x00, 0x00, + 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x95, 0x25, 0x7B, 0x00, + 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, + 0x93, 0x41, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x2C, 0x00, 0x0B, 0x90, + 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, + 0x3D, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x93, + 0x41, 0x72, 0x00, 0x94, 0x38, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, + 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, + 0x49, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4E, 0x72, 0x00, 0x92, 0x4B, 0x65, 0x00, 0x9B, + 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, + 0x41, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x90, 0x57, 0x00, 0x00, 0x91, 0x4E, 0x00, 0x00, 0x92, + 0x4B, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4D, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x93, + 0x41, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, + 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4D, 0x00, 0x00, 0x92, + 0x49, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x9B, + 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x95, + 0x25, 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x90, 0x53, 0x00, 0x00, 0x91, + 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, + 0x46, 0x65, 0x00, 0x95, 0x2A, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, + 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, + 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, + 0x46, 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x93, + 0x42, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, + 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x46, + 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x95, 0x2A, 0x00, 0x00, 0x93, 0x42, + 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x46, 0x72, 0x00, 0x92, 0x42, + 0x65, 0x00, 0x93, 0x42, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x95, 0x28, 0x7B, 0x00, 0x9B, 0x27, + 0x6B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, + 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0A, 0x2C, 0x00, 0x0B, 0x90, 0x4E, 0x00, 0x00, + 0x91, 0x46, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, + 0x95, 0x28, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4F, 0x72, 0x00, 0x92, 0x4B, 0x65, 0x00, + 0x95, 0x27, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, + 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, + 0x2C, 0x00, 0x0B, 0x90, 0x57, 0x00, 0x00, 0x91, 0x4F, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x93, + 0x43, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4F, 0x72, 0x00, 0x92, + 0x4B, 0x65, 0x00, 0x93, 0x43, 0x72, 0x00, 0x94, 0x3A, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, + 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x57, 0x00, 0x00, 0x91, + 0x4F, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x90, 0x58, 0x78, 0x00, 0x91, 0x50, 0x72, 0x00, 0x92, + 0x4C, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0A, 0x93, 0x43, 0x00, 0x00, 0x94, 0x3A, 0x00, 0x00, 0x90, 0x58, 0x00, 0x00, 0x91, + 0x50, 0x00, 0x00, 0x92, 0x4C, 0x00, 0x00, 0x90, 0x57, 0x78, 0x00, 0x91, 0x4F, 0x72, 0x00, 0x92, + 0x4B, 0x65, 0x00, 0x93, 0x43, 0x72, 0x00, 0x94, 0x3F, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, + 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0B, 0x90, 0x57, 0x00, 0x00, 0x91, + 0x4F, 0x00, 0x00, 0x92, 0x4B, 0x00, 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, + 0x49, 0x65, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9B, 0x27, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x95, 0x27, 0x00, 0x00, 0x93, 0x43, 0x00, 0x00, 0x94, 0x3F, 0x00, 0x00, 0x90, + 0x55, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, + 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x95, 0x2C, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, + 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x44, 0x72, 0x00, 0x94, + 0x3F, 0x72, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9C, 0x29, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x47, 0x00, 0x00, 0x95, + 0x2C, 0x00, 0x00, 0x93, 0x44, 0x00, 0x00, 0x94, 0x3F, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, + 0x4C, 0x72, 0x00, 0x92, 0x44, 0x65, 0x00, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3D, 0x72, 0x00, 0x95, + 0x25, 0x7B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x9C, 0x29, 0x00, 0x00, 0x9E, + 0x2C, 0x00, 0x0B, 0x2C, 0x4C, 0x0B, 0x90, 0x50, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x44, + 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0A, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3D, 0x00, 0x00, 0x95, 0x25, + 0x00, 0x00, 0x90, 0x4E, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x93, 0x42, + 0x72, 0x00, 0x94, 0x3B, 0x72, 0x00, 0x95, 0x27, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, + 0x6B, 0x00, 0x9E, 0x2C, 0x4C, 0x0B, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, + 0x00, 0x0B, 0x90, 0x4E, 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x90, 0x4E, + 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x42, 0x65, 0x00, 0x9E, 0x2C, 0x4C, 0x0A, 0x2C, 0x00, + 0x0B, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3B, 0x00, 0x00, 0x95, 0x27, 0x00, 0x00, 0x90, 0x4E, 0x00, + 0x00, 0x91, 0x4B, 0x00, 0x00, 0x92, 0x42, 0x00, 0x00, 0x90, 0x50, 0x78, 0x00, 0x91, 0x47, 0x72, + 0x00, 0x92, 0x44, 0x65, 0x00, 0x95, 0x28, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, + 0x0C, 0x9B, 0x27, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x93, 0x40, 0x72, 0x00, 0x94, 0x3B, 0x72, + 0x00, 0x9E, 0x2C, 0x4C, 0x0C, 0x90, 0x50, 0x00, 0x00, 0x91, 0x47, 0x00, 0x00, 0x92, 0x44, 0x00, + 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x95, 0x28, 0x00, 0x00, 0x93, 0x40, 0x00, 0x00, 0x94, 0x3B, 0x00, + 0x00, 0x90, 0x55, 0x78, 0x00, 0x91, 0x4C, 0x72, 0x00, 0x92, 0x49, 0x65, 0x00, 0x93, 0x44, 0x72, + 0x00, 0x94, 0x40, 0x72, 0x00, 0x95, 0x25, 0x7B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, + 0x0C, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, + 0x0C, 0x90, 0x55, 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, 0x9B, 0x27, 0x00, + 0x00, 0x9E, 0x2C, 0x00, 0x0C, 0x93, 0x44, 0x00, 0x00, 0x94, 0x40, 0x00, 0x00, 0x95, 0x25, 0x00, + 0x00, 0x90, 0x52, 0x78, 0x00, 0x91, 0x49, 0x72, 0x00, 0x92, 0x46, 0x65, 0x00, 0x93, 0x42, 0x72, + 0x00, 0x94, 0x40, 0x72, 0x00, 0x95, 0x2A, 0x7B, 0x00, 0x9C, 0x29, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, + 0x0C, 0x9C, 0x29, 0x00, 0x00, 0x9E, 0x2C, 0x00, 0x0E, 0x9B, 0x27, 0x6B, 0x00, 0x9E, 0x2C, 0x4C, + 0x0E, 0x90, 0x52, 0x00, 0x00, 0x91, 0x49, 0x00, 0x00, 0x92, 0x46, 0x00, 0x00, 0x9B, 0x27, 0x00, + 0x00, 0x9E, 0x2C, 0x00, 0x0E, 0x93, 0x42, 0x00, 0x00, 0x94, 0x40, 0x00, 0x00, 0x95, 0x2A, 0x00, + 0x00, 0x90, 0x53, 0x78, 0x00, 0x91, 0x4B, 0x72, 0x00, 0x92, 0x47, 0x65, 0x00, 0x93, 0x42, 0x72, + 0x00, 0x94, 0x3F, 0x72, 0x00, 0x95, 0x23, 0x7B, 0x00, 0x9B, 0x27, 0x6B, 0x00, 0x9C, 0x29, 0x6B, + 0x00, 0x9E, 0x2C, 0x4C, 0x00, 0x9F, 0x2B, 0x58, 0x0E, 0x9B, 0x27, 0x00, 0x00, 0x9C, 0x29, 0x00, + 0x00, 0x9E, 0x2C, 0x00, 0x00, 0x9F, 0x2B, 0x00, 0x52, 0x90, 0x53, 0x00, 0x00, 0x91, 0x4B, 0x00, + 0x00, 0x92, 0x47, 0x00, 0x00, 0x93, 0x42, 0x00, 0x00, 0x94, 0x3F, 0x00, 0x00, 0x95, 0x23, 0x00, + 0x00, 0xFF, 0x2F, 0x00, 0xFF}; + +// lemmings_012_tim7.vgz - Lemmings: Ronda Alla Turca +const unsigned char TIM7_VGZ[0x065B] = +{ 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xED, 0x5B, 0xCD, 0x6F, 0x1B, 0x45, + 0x14, 0x7F, 0x6B, 0x77, 0x9D, 0x28, 0x87, 0xBA, 0xB2, 0x95, 0x9E, 0xDD, 0x18, 0x28, 0x0A, 0xD0, + 0xAC, 0x93, 0x12, 0x04, 0xA7, 0x36, 0x31, 0x02, 0x09, 0xAA, 0xF2, 0x51, 0x15, 0xE9, 0x69, 0x57, + 0xAA, 0x93, 0xB8, 0xAE, 0x45, 0xD2, 0x4A, 0xFD, 0xE0, 0xEB, 0x54, 0x28, 0x48, 0x48, 0xAC, 0x4F, + 0x9C, 0x7C, 0x41, 0xA4, 0xFD, 0x07, 0x9C, 0xCD, 0x25, 0x7B, 0x40, 0x6A, 0x81, 0x7B, 0xFE, 0x00, + 0x5F, 0x5A, 0x08, 0x12, 0x11, 0x17, 0x2E, 0xDC, 0x79, 0x33, 0xDE, 0xD9, 0xEC, 0xDA, 0xEB, 0xCD, + 0x8E, 0xD7, 0x9B, 0x38, 0x81, 0xB5, 0x62, 0xAF, 0x67, 0xF6, 0xCD, 0xBE, 0xDF, 0x7B, 0x33, 0xBF, + 0xDF, 0xCE, 0xB3, 0x72, 0xB5, 0xB6, 0x56, 0x58, 0x78, 0x15, 0xE0, 0x7D, 0x05, 0xDC, 0xE3, 0xD7, + 0x39, 0x80, 0xBF, 0xBF, 0x9F, 0x86, 0x35, 0xE8, 0x7C, 0x76, 0x1F, 0xEF, 0x42, 0xFF, 0xA3, 0xF9, + 0xC3, 0x3C, 0xC8, 0x1C, 0xA8, 0x14, 0xF0, 0xA7, 0xC7, 0x38, 0x0E, 0x78, 0x6D, 0x07, 0x97, 0xFF, + 0xC1, 0xFB, 0x19, 0x7C, 0x30, 0x86, 0x4F, 0x53, 0xF8, 0x3B, 0xE0, 0xE3, 0x2C, 0x16, 0x00, 0x8B, + 0x19, 0xBC, 0x00, 0xB8, 0x08, 0xD8, 0x3A, 0x89, 0x3F, 0xD6, 0xB0, 0x35, 0x85, 0x95, 0x5D, 0x5C, + 0xD9, 0xC5, 0x2F, 0x55, 0xFC, 0x5A, 0xC5, 0x67, 0x29, 0xDC, 0x49, 0xE1, 0x13, 0x05, 0xCF, 0x28, + 0xF8, 0x9C, 0x82, 0x17, 0x01, 0xCB, 0x80, 0x1B, 0x59, 0x5C, 0x6F, 0xE3, 0xC6, 0x39, 0x5C, 0xDA, + 0xC5, 0xEA, 0x2E, 0x7E, 0xA5, 0xE2, 0x37, 0x2A, 0xFE, 0x96, 0xC2, 0x3F, 0x52, 0xF8, 0xB3, 0x82, + 0x53, 0x0A, 0x3E, 0xAF, 0xE0, 0x02, 0xE0, 0x9B, 0x80, 0x56, 0x1A, 0x1F, 0xB6, 0xD1, 0x9A, 0xC5, + 0xCD, 0x4C, 0xC5, 0x98, 0xC4, 0x8D, 0x93, 0xB8, 0x6E, 0x31, 0x43, 0x2B, 0x87, 0x0F, 0x2D, 0x6A, + 0xAF, 0x68, 0x93, 0xD8, 0x4A, 0x39, 0xF7, 0x65, 0xBD, 0x35, 0xD1, 0x5B, 0x63, 0xBD, 0x7A, 0x90, + 0x49, 0x69, 0x12, 0xAF, 0xED, 0xE2, 0xF2, 0x2E, 0xDE, 0x57, 0xF1, 0x41, 0x0A, 0x9F, 0x2A, 0x1C, + 0x4B, 0x06, 0x0B, 0x0A, 0x16, 0x15, 0xBC, 0x30, 0x8E, 0x8B, 0xE3, 0x62, 0xCC, 0x17, 0xC4, 0x98, + 0xB3, 0xDC, 0x9C, 0x79, 0x52, 0xD9, 0x9A, 0xC6, 0x56, 0xA6, 0xD3, 0x5B, 0x69, 0x4C, 0x47, 0x08, + 0x4B, 0x46, 0xB8, 0x97, 0xC3, 0xF5, 0xF3, 0xB8, 0x31, 0xC3, 0x87, 0x3A, 0x8F, 0xD6, 0x1C, 0x73, + 0xDE, 0x0D, 0x85, 0x95, 0x77, 0xC6, 0x37, 0x64, 0xDD, 0xF3, 0xA3, 0x73, 0x20, 0xB7, 0x45, 0x63, + 0xDB, 0x81, 0x2C, 0x7C, 0xF6, 0x23, 0x6A, 0x32, 0x37, 0xCC, 0x38, 0x88, 0xB6, 0x39, 0x22, 0x72, + 0x7E, 0x9B, 0x0D, 0xA5, 0x77, 0x10, 0x35, 0x45, 0x23, 0x1F, 0x3F, 0x2A, 0xA2, 0x26, 0xB6, 0x8A, + 0xDC, 0xBC, 0x2A, 0xCC, 0xAB, 0x9E, 0x28, 0xF9, 0xC7, 0xA4, 0x1B, 0xB5, 0xD2, 0x02, 0x51, 0x96, + 0x07, 0x61, 0x96, 0xF7, 0x52, 0x10, 0xE6, 0x19, 0x5E, 0xE6, 0x5B, 0x07, 0xE6, 0x69, 0x3E, 0x19, + 0xE6, 0xB9, 0x89, 0x1B, 0x04, 0xEA, 0x2D, 0x8B, 0xDE, 0x32, 0xEB, 0x35, 0x82, 0x4C, 0xE8, 0xD6, + 0x72, 0xD1, 0xB0, 0x84, 0xB9, 0xE5, 0xDC, 0xB1, 0x77, 0x4C, 0xB9, 0xE9, 0xD7, 0xE3, 0xA7, 0xDE, + 0xC7, 0x4F, 0x1F, 0xB4, 0xB6, 0xE8, 0x6D, 0xB3, 0x5E, 0x3B, 0x4E, 0x7E, 0x7D, 0x37, 0x62, 0xCB, + 0x90, 0x66, 0x8B, 0x9C, 0xFF, 0x7E, 0x67, 0x28, 0xCE, 0xDD, 0x6E, 0x53, 0xDE, 0xCF, 0xF2, 0x2B, + 0x3B, 0x29, 0x16, 0xD1, 0x23, 0x57, 0x5B, 0x63, 0xA2, 0x37, 0xDB, 0x0D, 0x39, 0x12, 0x90, 0x31, + 0x0F, 0x90, 0xA2, 0x30, 0x2F, 0x32, 0xF3, 0xAD, 0x88, 0x28, 0x2C, 0x81, 0xA2, 0x27, 0x89, 0xDD, + 0xF3, 0x6A, 0x20, 0x14, 0x66, 0x3C, 0x14, 0x8D, 0xE9, 0xC8, 0x2B, 0xEB, 0x6C, 0x00, 0x8A, 0xEE, + 0x5C, 0x8C, 0xF5, 0x45, 0x61, 0x8F, 0x02, 0x0A, 0x37, 0x17, 0xEE, 0x2A, 0x53, 0x45, 0x84, 0xBD, + 0xF1, 0x77, 0x07, 0x77, 0xC9, 0xCD, 0xF1, 0x5C, 0x6E, 0xCE, 0xEC, 0xAF, 0x62, 0xB9, 0xC8, 0x2A, + 0x96, 0xDF, 0x47, 0xC5, 0xF4, 0x50, 0x15, 0xD3, 0x82, 0x4C, 0x8C, 0x78, 0x2A, 0x66, 0x0E, 0x4F, + 0xC5, 0xF4, 0x20, 0x15, 0x2B, 0x25, 0xA0, 0x62, 0x5A, 0xA8, 0x8A, 0xD9, 0xC3, 0x53, 0x31, 0x6D, + 0x34, 0x54, 0xCC, 0x88, 0x42, 0xF5, 0x11, 0xD4, 0xA1, 0x94, 0x80, 0x8A, 0x19, 0xF1, 0x54, 0x4C, + 0x8B, 0x22, 0xD0, 0x7E, 0xE1, 0x68, 0x1C, 0x4D, 0x15, 0xB3, 0x93, 0x54, 0x31, 0xF3, 0x58, 0xA8, + 0x98, 0x1D, 0x4F, 0xC5, 0xB4, 0x49, 0xDF, 0xB4, 0x0C, 0x51, 0xB1, 0xC6, 0x91, 0x55, 0xB1, 0xAD, + 0x44, 0x55, 0x8C, 0xC7, 0x21, 0x92, 0x8A, 0x65, 0xC8, 0x19, 0xB6, 0x7C, 0x24, 0x1E, 0x3C, 0x8A, + 0x1E, 0xB0, 0x79, 0x47, 0x32, 0x18, 0xD8, 0xB4, 0x67, 0x71, 0x65, 0x70, 0x63, 0x6E, 0x8F, 0x15, + 0x3D, 0xDA, 0x84, 0x1B, 0xF9, 0xEE, 0x5E, 0xB9, 0xB5, 0x9F, 0xE7, 0x6A, 0xD5, 0x31, 0x57, 0x05, + 0x9B, 0xB9, 0x63, 0x9E, 0x96, 0xE4, 0x76, 0xD7, 0x25, 0x3F, 0x1C, 0x1F, 0x9B, 0x39, 0x51, 0xEA, + 0x61, 0x33, 0x97, 0xF9, 0xB9, 0xA6, 0x0F, 0xCA, 0x66, 0x92, 0x91, 0xCF, 0x7A, 0x54, 0x52, 0x78, + 0xD5, 0x9B, 0x0E, 0x6D, 0x32, 0x2C, 0x1D, 0x76, 0x92, 0xE9, 0xD0, 0x82, 0xD2, 0xA1, 0xC7, 0x4B, + 0x47, 0x49, 0x2A, 0x1D, 0x56, 0x9C, 0x74, 0xB0, 0xB5, 0x10, 0x75, 0x2B, 0x91, 0x11, 0x19, 0x51, + 0x45, 0x46, 0x6A, 0xCE, 0x93, 0xD2, 0x9E, 0x3E, 0x7A, 0x1F, 0x08, 0x29, 0x23, 0xAA, 0x9F, 0xB7, + 0x45, 0x12, 0x1D, 0x6F, 0xBB, 0x7A, 0x05, 0x52, 0x73, 0x7A, 0x20, 0xB9, 0x17, 0xD1, 0x33, 0x82, + 0xA2, 0xA7, 0x45, 0xCC, 0x88, 0xEA, 0x97, 0xFB, 0xFC, 0xDE, 0xC3, 0x2A, 0x6B, 0xF4, 0x03, 0x77, + 0x74, 0xD3, 0x35, 0xD9, 0x16, 0x4F, 0x95, 0x65, 0x4F, 0x46, 0x58, 0x2F, 0x8F, 0xF0, 0xD0, 0x17, + 0x48, 0x9F, 0x74, 0x68, 0x41, 0xE9, 0x30, 0x42, 0xD3, 0x61, 0x86, 0xA6, 0xA3, 0x31, 0x7A, 0xE9, + 0xD0, 0x83, 0xD2, 0x61, 0x84, 0xA6, 0xC3, 0x83, 0x51, 0x0E, 0xD1, 0x30, 0x8B, 0x81, 0xB9, 0xBD, + 0x6D, 0x54, 0xBF, 0xCA, 0x9E, 0x6C, 0x31, 0x50, 0x8B, 0xB7, 0x8D, 0xF2, 0xF2, 0x86, 0x3D, 0x7A, + 0xC5, 0x40, 0x2D, 0x68, 0x1B, 0xA5, 0x87, 0x6E, 0xA3, 0x1A, 0x09, 0x17, 0x03, 0xB5, 0x78, 0xDB, + 0x28, 0xE3, 0x40, 0x8A, 0x81, 0x5A, 0xBF, 0x2D, 0xCF, 0xB0, 0xB7, 0x51, 0x5A, 0xBC, 0x6D, 0x94, + 0x21, 0x5F, 0x0C, 0x8C, 0x35, 0x63, 0x87, 0xBD, 0x8D, 0x3A, 0x62, 0x65, 0xB4, 0x3E, 0xDB, 0x28, + 0xBD, 0x6B, 0x03, 0x72, 0x34, 0x8B, 0x81, 0x5E, 0x14, 0xA5, 0xD0, 0x6D, 0x94, 0x19, 0x8A, 0xC2, + 0xFE, 0xBF, 0x18, 0x18, 0xB9, 0x18, 0x38, 0x2C, 0x15, 0xD3, 0x87, 0xA7, 0x62, 0xC7, 0xA3, 0x18, + 0x18, 0x8B, 0xE5, 0x22, 0x14, 0x03, 0xF5, 0x78, 0x2A, 0x56, 0x3A, 0x5E, 0xC5, 0xC0, 0x24, 0x54, + 0x2C, 0x7A, 0x31, 0xD0, 0x8C, 0xAD, 0x62, 0x8D, 0xD1, 0xF8, 0x49, 0x6B, 0xEB, 0x50, 0x55, 0x4C, + 0x8B, 0xAC, 0x62, 0x66, 0x92, 0x25, 0xCD, 0x21, 0xAA, 0x98, 0x11, 0x43, 0xC5, 0x1A, 0x87, 0xAA, + 0x62, 0x66, 0xA8, 0x8A, 0x79, 0x66, 0x91, 0x9C, 0xDA, 0x8E, 0x66, 0x31, 0xD0, 0x0C, 0x2D, 0x06, + 0x36, 0x0E, 0xBC, 0x18, 0xA8, 0xC5, 0xAB, 0x3E, 0x19, 0x07, 0x58, 0x0C, 0x34, 0xE3, 0x15, 0x03, + 0x0D, 0xF9, 0x62, 0x60, 0x78, 0x3A, 0xEC, 0x63, 0x57, 0x0C, 0x34, 0x25, 0x8B, 0x81, 0x31, 0xAB, + 0x4F, 0xBA, 0x7C, 0xF5, 0x29, 0xD1, 0x62, 0x60, 0xBF, 0xE8, 0x0D, 0xBD, 0xFA, 0x54, 0x8A, 0x5C, + 0x7D, 0xB2, 0xFF, 0xC3, 0xC5, 0x40, 0x3D, 0x28, 0x1D, 0xA5, 0x04, 0xD2, 0xA1, 0x47, 0xAE, 0xCD, + 0xCA, 0x21, 0xCA, 0xB2, 0x41, 0x24, 0x25, 0xC3, 0x27, 0x10, 0x95, 0x3A, 0xAE, 0xD4, 0xB9, 0x84, + 0x9D, 0xC0, 0x67, 0x69, 0xDC, 0x49, 0xE3, 0x93, 0x09, 0x3C, 0x03, 0x5C, 0xC2, 0x4E, 0x61, 0x79, + 0x7C, 0x8F, 0xE5, 0x22, 0x95, 0x13, 0x89, 0x61, 0x66, 0xF0, 0x46, 0x1D, 0x3F, 0xAE, 0xE3, 0xB7, + 0x2A, 0x7E, 0x77, 0x02, 0xFF, 0x4C, 0xE3, 0x5F, 0x69, 0xFC, 0x65, 0x02, 0x5F, 0x04, 0x7C, 0x49, + 0xC1, 0xB7, 0x4F, 0xE1, 0x3B, 0xE3, 0xE4, 0x30, 0x3E, 0x6A, 0xE2, 0x26, 0x27, 0x4C, 0x2B, 0xCB, + 0x73, 0x31, 0xE3, 0x7B, 0xF6, 0x66, 0x8D, 0x19, 0xD6, 0xA8, 0x7B, 0x2E, 0x90, 0xAA, 0x17, 0x31, + 0x2B, 0xE6, 0x8C, 0x7C, 0x45, 0xA5, 0x33, 0x19, 0x1C, 0x73, 0xDC, 0xCC, 0x3B, 0xAE, 0x96, 0x3C, + 0x9E, 0xE8, 0x41, 0xAE, 0x6A, 0x7E, 0x57, 0x7D, 0x17, 0x70, 0x4F, 0x64, 0x95, 0x48, 0x6C, 0x5A, + 0xE5, 0x1F, 0xA7, 0x39, 0x04, 0xD7, 0x73, 0x7B, 0x40, 0xFA, 0x8D, 0x7B, 0xDF, 0x2E, 0x15, 0x1E, + 0x4C, 0x02, 0x9C, 0xF0, 0x76, 0x50, 0x88, 0x5D, 0xBC, 0x57, 0x59, 0xE4, 0x96, 0x6A, 0x53, 0xAC, + 0x3B, 0x95, 0x57, 0x0C, 0xC8, 0x55, 0x0B, 0x37, 0x39, 0x2F, 0x05, 0x2E, 0x55, 0x71, 0x25, 0x9F, + 0xA8, 0x1E, 0x12, 0x93, 0xF0, 0xDF, 0x23, 0x2B, 0xD1, 0x5D, 0x75, 0x14, 0x9C, 0x3B, 0x13, 0x4C, + 0xE9, 0x5E, 0x6A, 0xED, 0x60, 0xC9, 0xE1, 0xA3, 0x0C, 0x0B, 0xBB, 0x1E, 0x82, 0x65, 0x30, 0xCE, + 0xE4, 0x41, 0x73, 0x63, 0x35, 0x98, 0x04, 0xB8, 0xEE, 0x39, 0xE4, 0xA3, 0x7A, 0x66, 0x85, 0x3A, + 0x50, 0x9D, 0x44, 0x15, 0xAC, 0xEB, 0xA6, 0x32, 0xE3, 0xCC, 0x76, 0x89, 0x38, 0x3B, 0x94, 0x25, + 0x16, 0xAF, 0xDA, 0x15, 0xB4, 0x58, 0x1E, 0x32, 0x66, 0xCE, 0x4B, 0x3D, 0x52, 0xFA, 0x7E, 0xC7, + 0xDF, 0x9F, 0x99, 0x73, 0x09, 0x30, 0x73, 0x3E, 0x80, 0x99, 0xA3, 0xD0, 0xDD, 0x21, 0x32, 0xB3, + 0x94, 0x88, 0x94, 0x82, 0x98, 0x59, 0x8A, 0x15, 0xED, 0xE1, 0x31, 0x73, 0x63, 0x04, 0x98, 0xD9, + 0x8E, 0xCD, 0xCC, 0x8D, 0x24, 0x99, 0x59, 0x9F, 0x0C, 0xFE, 0x81, 0x7B, 0x88, 0xCC, 0x6C, 0x0F, + 0xC4, 0xCC, 0x5E, 0xC9, 0x30, 0xA2, 0x31, 0xB3, 0x76, 0x74, 0x98, 0xD9, 0x4E, 0x80, 0x99, 0x1B, + 0x03, 0x31, 0xB3, 0xEE, 0x61, 0x66, 0xAD, 0x2F, 0x33, 0x5F, 0x7F, 0x6B, 0x65, 0xAE, 0x00, 0x0A, + 0xC0, 0x12, 0xFD, 0x5D, 0x81, 0x3A, 0xAC, 0x41, 0x01, 0x5E, 0x83, 0x37, 0xE8, 0xFD, 0x03, 0xB8, + 0x05, 0x37, 0x61, 0x05, 0x2A, 0x74, 0x7E, 0x11, 0x56, 0xE9, 0xC5, 0xCE, 0xAE, 0xC0, 0x3D, 0xB8, + 0x0D, 0xCB, 0x74, 0xDE, 0xF9, 0x0F, 0x9F, 0x2A, 0x59, 0xAC, 0x91, 0xDD, 0x4D, 0xA8, 0xC1, 0x1D, + 0xDE, 0xF6, 0x1E, 0x2C, 0xD2, 0x75, 0x33, 0xF4, 0x77, 0x09, 0x3E, 0x84, 0x57, 0xA0, 0x0C, 0x97, + 0xE9, 0x93, 0x1D, 0x97, 0x68, 0xC4, 0x2F, 0xC8, 0xF2, 0x36, 0xDC, 0xE5, 0xDF, 0x4B, 0xF0, 0x3A, + 0xBD, 0x4A, 0x74, 0x76, 0x95, 0x5A, 0xD9, 0x1D, 0xAA, 0xF0, 0x39, 0xD9, 0x2D, 0xD0, 0x27, 0xFB, + 0x06, 0x34, 0x12, 0xF3, 0xE1, 0x13, 0xFA, 0xCE, 0x6C, 0xAA, 0xE4, 0x4D, 0x01, 0xAE, 0xD3, 0xF9, + 0x2D, 0xEE, 0x27, 0xF3, 0xA7, 0x4C, 0x7E, 0x5E, 0xE6, 0xAD, 0x75, 0x6E, 0xFF, 0x32, 0x9D, 0xAF, + 0x52, 0x7F, 0x8D, 0x5E, 0x9D, 0xEB, 0x3F, 0xA5, 0x9E, 0xBB, 0x70, 0x83, 0x5F, 0x7B, 0x8B, 0x7C, + 0x5C, 0xA0, 0xF7, 0xCF, 0xE0, 0x1C, 0x4C, 0xD0, 0x6B, 0x81, 0xC6, 0xB8, 0xE3, 0x5C, 0xC7, 0xEE, + 0x54, 0x80, 0xA9, 0x7D, 0x71, 0x4F, 0xD1, 0xB7, 0x25, 0xEE, 0xE7, 0x47, 0x74, 0xE5, 0x2A, 0xDD, + 0xB9, 0x46, 0xAD, 0x0C, 0x3F, 0xBB, 0x7E, 0x8D, 0xCE, 0x57, 0x68, 0xC4, 0x7B, 0x34, 0x6E, 0xA1, + 0x07, 0xF1, 0xBF, 0x46, 0x65, 0x12, 0x63, 0x46, 0x35, 0x00, 0x00}; + +// lem_xmas_001_jb.dro - Xmas Lemmings: Jingle Bells +const unsigned char JB_DRO[0x3100] = +{ 0x44, 0x42, 0x52, 0x41, 0x57, 0x4F, 0x50, 0x4C, 0x02, 0x00, 0x00, 0x00, 0x36, 0x18, 0x00, 0x00, + 0xC3, 0xAB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x7B, 0x7A, 0x01, 0x04, 0x05, 0x08, 0xBD, 0x20, + 0x40, 0x60, 0x80, 0xE0, 0x21, 0x41, 0x61, 0x81, 0xE1, 0x22, 0x42, 0x62, 0x82, 0xE2, 0x23, 0x43, + 0x63, 0x83, 0xE3, 0x24, 0x44, 0x64, 0x84, 0xE4, 0x25, 0x45, 0x65, 0x85, 0xE5, 0x28, 0x48, 0x68, + 0x88, 0xE8, 0x29, 0x49, 0x69, 0x89, 0xE9, 0x2A, 0x4A, 0x6A, 0x8A, 0xEA, 0x2B, 0x4B, 0x6B, 0x8B, + 0xEB, 0x2C, 0x4C, 0x6C, 0x8C, 0xEC, 0x2D, 0x4D, 0x6D, 0x8D, 0xED, 0x30, 0x50, 0x70, 0x90, 0xF0, + 0x31, 0x51, 0x71, 0x91, 0xF1, 0x32, 0x52, 0x72, 0x92, 0xF2, 0x33, 0x53, 0x73, 0x93, 0xF3, 0x34, + 0x54, 0x74, 0x94, 0xF4, 0x35, 0x55, 0x75, 0x95, 0xF5, 0xA0, 0xB0, 0xC0, 0xA1, 0xB1, 0xC1, 0xA2, + 0xB2, 0xC2, 0xA3, 0xB3, 0xC3, 0xA4, 0xB4, 0xC4, 0xA5, 0xB5, 0xC5, 0xA6, 0xB6, 0xC6, 0xA7, 0xB7, + 0xC7, 0xA8, 0xB8, 0xC8, 0x4B, 0x02, 0x5A, 0x03, 0x4D, 0x99, 0x5C, 0x59, 0x4E, 0x06, 0x5D, 0x07, + 0x77, 0x23, 0x79, 0x0B, 0x4F, 0x01, 0x5E, 0x02, 0x78, 0x32, 0x77, 0x44, 0x7A, 0x0C, 0x77, 0x67, + 0x7A, 0x0C, 0x77, 0x8B, 0x7A, 0x0D, 0x77, 0xB2, 0x7A, 0x0D, 0x77, 0xDB, 0x7A, 0x0D, 0x78, 0x12, + 0x7A, 0xA3, 0x77, 0x65, 0x7A, 0x00, 0x78, 0x2F, 0x7A, 0x0C, 0x77, 0x99, 0x7A, 0x0D, 0x77, 0xCF, + 0x7A, 0x0D, 0x77, 0x05, 0x78, 0x32, 0x7A, 0x0C, 0x77, 0x23, 0x7A, 0x0D, 0x77, 0x44, 0x7A, 0x0D, + 0x78, 0x12, 0x77, 0xCF, 0x78, 0x2F, 0x7A, 0x0D, 0x77, 0x99, 0x7A, 0x0C, 0x77, 0x65, 0x7A, 0x0D, + 0x77, 0x34, 0x7A, 0x0D, 0x77, 0x06, 0x7A, 0x0C, 0x77, 0xDB, 0x7A, 0x00, 0x78, 0x2E, 0x7A, 0x1A, + 0x77, 0xB2, 0x7A, 0x0D, 0x77, 0x8B, 0x7A, 0x0C, 0x77, 0x67, 0x7A, 0x0D, 0x77, 0x44, 0x7A, 0x0D, + 0x77, 0x23, 0x7A, 0x0D, 0x78, 0x0E, 0x7B, 0x02, 0x7A, 0x7B, 0x5C, 0x89, 0x4E, 0x36, 0x5D, 0x47, + 0x4F, 0x03, 0x5E, 0x00, 0x79, 0x00, 0x4B, 0x22, 0x5A, 0x21, 0x7A, 0x00, 0x77, 0xB2, 0x78, 0x22, + 0x7A, 0x6C, 0x77, 0xDB, 0x7A, 0x0D, 0x77, 0x06, 0x78, 0x23, 0x7A, 0x0C, 0x77, 0x34, 0x7A, 0x0D, + 0x77, 0x65, 0x7A, 0x0D, 0x77, 0x99, 0x7A, 0x0D, 0x77, 0xCF, 0x7A, 0x0C, 0x77, 0x05, 0x78, 0x26, + 0x7A, 0x1B, 0x77, 0x23, 0x7A, 0x0D, 0x77, 0x44, 0x7A, 0x0C, 0x77, 0x67, 0x7A, 0x0D, 0x77, 0x8B, + 0x7A, 0x0D, 0x77, 0xB2, 0x7A, 0x0C, 0x77, 0xDB, 0x7A, 0x0D, 0x77, 0x06, 0x78, 0x27, 0x7A, 0x0D, + 0x78, 0x07, 0x7B, 0x03, 0x7A, 0x13, 0x00, 0x20, 0x04, 0xC0, 0x07, 0xF2, 0x16, 0xF2, 0x08, 0xF2, + 0x17, 0xF2, 0x61, 0x08, 0x05, 0x27, 0x14, 0x32, 0x7A, 0x00, 0x06, 0x4F, 0x15, 0x08, 0x5F, 0x65, + 0x60, 0x33, 0x0C, 0x53, 0x1B, 0xAA, 0x0D, 0x5A, 0x1C, 0x1A, 0x64, 0x0C, 0x0A, 0x21, 0x19, 0x21, + 0x0B, 0x97, 0x1A, 0x80, 0x11, 0x53, 0x20, 0xAA, 0x12, 0x5A, 0x21, 0x1A, 0x67, 0x0C, 0x7A, 0x00, + 0x0F, 0x21, 0x1E, 0x21, 0x10, 0x97, 0x1F, 0x80, 0x25, 0x53, 0x34, 0xAA, 0x26, 0x5A, 0x35, 0x1A, + 0x6A, 0x0C, 0x23, 0x21, 0x32, 0x21, 0x24, 0x97, 0x33, 0x80, 0x68, 0xB2, 0x69, 0x2A, 0x2A, 0xE2, + 0x39, 0xE3, 0x2B, 0xF3, 0x7A, 0x00, 0x3A, 0xF3, 0x2C, 0x02, 0x6D, 0x0E, 0x28, 0x26, 0x37, 0x32, + 0x29, 0x42, 0x38, 0x0E, 0x6B, 0xB2, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x45, 0x60, 0x13, 0x60, 0x33, + 0x62, 0x05, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0A, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x2F, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDB, + 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, + 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, + 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, + 0x69, 0x0F, 0x7A, 0xDB, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, + 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, + 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x7A, 0x00, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xD9, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x16, 0x5F, 0xB2, + 0x60, 0x32, 0x68, 0xB2, 0x69, 0x2A, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, + 0x63, 0x0E, 0x62, 0xB2, 0x63, 0x2A, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x0A, 0x63, 0x2E, 0x65, 0x05, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDA, + 0x63, 0x2E, 0x66, 0x2E, 0x7A, 0x00, 0x69, 0x2F, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, + 0x7A, 0x00, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x62, 0x44, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, + 0x69, 0x0B, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0E, + 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x69, 0x2B, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x69, 0x0B, 0x7A, 0x6D, 0x60, 0x13, 0x63, 0x2E, 0x66, 0x2E, + 0x69, 0x2F, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x7A, 0x36, 0x63, 0x0E, + 0x66, 0x0E, 0x7A, 0x6D, 0x60, 0x13, 0x60, 0x33, 0x62, 0x65, 0x63, 0x2B, 0x69, 0x0F, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0B, 0x62, 0xB2, + 0x63, 0x2E, 0x65, 0x05, 0x66, 0x2E, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, + 0x69, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, + 0x60, 0x33, 0x62, 0x05, 0x63, 0x2E, 0x7A, 0x00, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, + 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x68, 0x06, 0x69, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x7A, 0x00, 0x62, 0xCF, 0x63, 0x2B, 0x65, 0x44, + 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x63, 0x0B, + 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x13, 0x60, 0x33, 0x69, 0x2B, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0x65, 0x7A, 0x00, 0x60, 0x33, + 0x63, 0x2B, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x63, 0x0B, + 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x68, 0x05, 0x69, 0x2A, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x13, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x5F, 0x05, 0x60, 0x36, 0x62, 0x05, 0x63, 0x2E, 0x65, 0x8B, 0x66, 0x2E, + 0x7A, 0x00, 0x69, 0x0A, 0x68, 0x06, 0x69, 0x2F, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x5F, 0x65, 0x60, 0x33, + 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, 0x7A, 0x00, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0A, + 0x68, 0x65, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, + 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDA, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x2E, 0x66, 0x2E, + 0x69, 0x2F, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0xB2, + 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, + 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xDB, 0x60, 0x13, 0x60, 0x33, 0x63, 0x2E, + 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, + 0x60, 0x13, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, + 0x69, 0x0F, 0x68, 0xB2, 0x69, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, + 0x5F, 0x05, 0x60, 0x36, 0x63, 0x2E, 0x7A, 0x00, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, 0x69, 0x2F, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0x6D, + 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x68, 0xB2, 0x69, 0x2A, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x2E, 0x7A, 0x00, 0x66, 0x2E, 0x69, 0x0A, 0x68, 0x65, + 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x7A, 0xA4, + 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0E, 0x62, 0xB2, 0x63, 0x2A, 0x66, 0x0E, 0x69, 0x0F, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x0A, 0x63, 0x2E, 0x7A, 0x00, 0x65, 0x05, + 0x66, 0x2E, 0x69, 0x2F, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0x6C, 0x63, 0x0E, 0x66, 0x0E, + 0x69, 0x0F, 0x7A, 0xDB, 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, + 0x5F, 0x99, 0x60, 0x33, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, + 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x62, 0x44, 0x63, 0x2E, + 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x69, 0x2B, + 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xDA, 0x69, 0x0B, 0x7A, 0x6D, 0x60, 0x13, + 0x63, 0x2E, 0x66, 0x2E, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x33, 0x7A, 0x35, + 0x63, 0x0E, 0x66, 0x0E, 0x7A, 0x6D, 0x60, 0x13, 0x60, 0x33, 0x62, 0x65, 0x63, 0x2B, 0x7A, 0x00, + 0x69, 0x0F, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, + 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x2E, 0x65, 0x05, 0x66, 0x2E, 0x68, 0x65, 0x69, 0x2F, 0x7A, 0x00, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA3, + 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x69, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x13, 0x60, 0x33, 0x62, 0x05, 0x63, 0x2E, 0x65, 0xB2, 0x66, 0x2E, 0x69, 0x0B, + 0x69, 0x2F, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, + 0x63, 0x0E, 0x66, 0x0E, 0x69, 0x0F, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x5F, 0x05, 0x60, 0x36, + 0x63, 0x2A, 0x65, 0x05, 0x66, 0x2E, 0x68, 0x05, 0x69, 0x32, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x0A, 0x66, 0x0E, 0x69, 0x12, 0x7A, 0xA4, 0x60, 0x16, 0x60, 0x36, 0x63, 0x2A, 0x66, 0x2E, + 0x69, 0x32, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0A, 0x66, 0x0E, + 0x69, 0x12, 0x7A, 0xA4, 0x60, 0x16, 0x5F, 0x99, 0x60, 0x33, 0x62, 0x99, 0x63, 0x27, 0x65, 0x99, + 0x66, 0x2B, 0x68, 0x99, 0x69, 0x2F, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, + 0x63, 0x07, 0x66, 0x0B, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x62, 0x06, + 0x63, 0x27, 0x65, 0x06, 0x66, 0x2B, 0x7A, 0x00, 0x68, 0x06, 0x69, 0x2F, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x07, 0x66, 0x0B, 0x69, 0x0F, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0xB2, 0x60, 0x32, + 0x62, 0xB2, 0x63, 0x26, 0x65, 0xB2, 0x66, 0x2A, 0x68, 0xB2, 0x69, 0x2E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x06, + 0x66, 0x0A, 0x69, 0x0E, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x07, 0x55, 0x16, 0x43, 0x08, 0x75, 0x17, 0x65, 0x09, 0x01, + 0x18, 0x01, 0x61, 0x0B, 0x05, 0x22, 0x14, 0x22, 0x06, 0x00, 0x15, 0x00, 0x60, 0x12, 0x5F, 0x05, + 0x60, 0x2A, 0x7A, 0x00, 0x0C, 0x52, 0x1B, 0x32, 0x0D, 0x18, 0x1C, 0x18, 0x64, 0x09, 0x0A, 0x64, + 0x19, 0x24, 0x0B, 0x0A, 0x1A, 0x0A, 0x62, 0x05, 0x63, 0x2A, 0x11, 0x52, 0x20, 0x32, 0x12, 0x18, + 0x21, 0x18, 0x67, 0x09, 0x0F, 0x64, 0x1E, 0x24, 0x7A, 0x00, 0x10, 0x0A, 0x1F, 0x0A, 0x66, 0x2A, + 0x25, 0x52, 0x34, 0x32, 0x26, 0x18, 0x35, 0x18, 0x6A, 0x09, 0x23, 0x64, 0x32, 0x24, 0x24, 0x0A, + 0x33, 0x0A, 0x68, 0x65, 0x69, 0x2B, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x45, + 0x60, 0x0A, 0x5F, 0x65, 0x7A, 0x00, 0x60, 0x2B, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x60, 0x0A, 0x7A, 0xA4, 0x5F, 0x65, 0x60, 0x27, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, + 0x60, 0x07, 0x5F, 0x99, 0x60, 0x27, 0x7A, 0xA3, 0x60, 0x07, 0x5F, 0x05, 0x60, 0x2A, 0x6C, 0x16, + 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x16, + 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, + 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, + 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, + 0x63, 0x0A, 0x62, 0x44, 0x63, 0x2A, 0x66, 0x0A, 0x66, 0x2A, 0x69, 0x0B, 0x68, 0x99, 0x69, 0x2B, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x8B, 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x8B, 0x63, 0x2A, + 0x66, 0x0A, 0x65, 0x06, 0x66, 0x2B, 0x69, 0x0B, 0x68, 0x05, 0x69, 0x2E, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x38, 0x0E, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, + 0x60, 0x0E, 0x7A, 0xA4, 0x60, 0x2E, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x0A, 0x7A, 0x00, 0x62, 0xB2, 0x63, 0x2A, 0x66, 0x0B, + 0x65, 0x65, 0x66, 0x2B, 0x69, 0x0E, 0x69, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0B, 0x5F, 0x05, + 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x69, 0x0E, + 0x68, 0x65, 0x69, 0x2B, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0A, + 0x5F, 0x65, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0A, + 0x5F, 0x05, 0x60, 0x2A, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x60, 0x2A, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x0A, 0x62, 0x44, + 0x63, 0x2A, 0x66, 0x0A, 0x7A, 0x00, 0x66, 0x2A, 0x69, 0x0B, 0x68, 0x99, 0x69, 0x2B, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x49, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0A, 0x60, 0x2A, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0xA4, + 0x60, 0x0B, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x0A, 0x62, 0x8B, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x06, + 0x66, 0x2B, 0x69, 0x0B, 0x68, 0x05, 0x69, 0x2E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0E, 0x60, 0x2E, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0E, 0x60, 0x2E, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA4, 0x60, 0x0E, 0x60, 0x2E, 0x63, 0x0A, 0x62, 0x05, 0x63, 0x2E, 0x25, 0xF2, + 0x34, 0xF2, 0x26, 0xF2, 0x35, 0xF2, 0x7A, 0x00, 0x6A, 0x08, 0x23, 0x27, 0x32, 0x32, 0x24, 0x4F, + 0x33, 0x08, 0x69, 0x0E, 0x69, 0x36, 0x7A, 0xA3, 0x60, 0x0E, 0x5F, 0x44, 0x60, 0x2E, 0x63, 0x0E, + 0x62, 0x44, 0x63, 0x2E, 0x66, 0x0B, 0x69, 0x16, 0x68, 0x44, 0x69, 0x36, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0E, 0x5F, 0x05, 0x60, 0x2E, 0x7A, 0x00, 0x63, 0x0E, 0x62, 0x05, + 0x63, 0x2E, 0x69, 0x16, 0x68, 0x05, 0x69, 0x36, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x0E, 0x62, 0x99, 0x63, 0x2B, 0x69, 0x16, + 0x68, 0x99, 0x69, 0x33, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, + 0x60, 0x0B, 0x7A, 0x00, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x0B, 0x62, 0x06, 0x63, 0x2B, 0x69, 0x13, + 0x68, 0x06, 0x69, 0x33, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x0B, 0x5F, 0xB2, + 0x60, 0x2A, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x2A, 0x69, 0x13, 0x68, 0xB2, 0x69, 0x32, 0x6C, 0x16, + 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x63, 0x0A, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x60, 0x0A, 0x69, 0x12, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x07, 0xF2, 0x16, 0xF2, + 0x08, 0xF2, 0x17, 0xF2, 0x09, 0x00, 0x18, 0x00, 0x61, 0x08, 0x7A, 0x00, 0x05, 0x27, 0x14, 0x32, + 0x06, 0x4F, 0x15, 0x08, 0x5F, 0x65, 0x60, 0x33, 0x0C, 0xE9, 0x1B, 0xE9, 0x0D, 0x05, 0x1C, 0x04, + 0x0E, 0x01, 0x64, 0x06, 0x0A, 0x01, 0x19, 0x01, 0x0B, 0x08, 0x1A, 0x08, 0x63, 0x26, 0x7A, 0x00, + 0x1F, 0x16, 0x65, 0xB2, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x23, 0x00, 0x32, 0x00, 0x24, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, + 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, + 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, + 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, + 0x63, 0x06, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, + 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0x05, + 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x66, 0x2E, + 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, + 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x7A, 0x00, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, + 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, 0x5F, 0x06, + 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, + 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x65, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, + 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, + 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, + 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, + 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, + 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, + 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x7A, 0x00, 0x66, 0x2B, + 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, + 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x62, 0xB2, 0x63, 0x26, + 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0A, + 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, + 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x07, + 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x62, 0x06, + 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x7A, 0x00, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, + 0x38, 0x16, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, + 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, + 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, + 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, 0x7A, 0x00, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x06, + 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x7A, 0xA4, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, + 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x25, 0xE4, 0x34, 0xF5, 0x7A, 0x00, 0x26, 0x06, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, + 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x5F, 0x05, 0x60, 0x36, + 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x05, + 0x63, 0x2A, 0x7A, 0x00, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x16, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0A, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, + 0x65, 0xB2, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x7A, 0x00, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, + 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x66, 0x0E, + 0x7A, 0x00, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, + 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x33, 0x63, 0x06, 0x7A, 0x00, 0x62, 0xB2, 0x63, 0x26, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x16, 0x5F, 0xB2, 0x7A, 0x00, 0x60, 0x32, 0x63, 0x26, + 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x12, + 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x06, + 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x7A, 0x00, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, + 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, + 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x44, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, + 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, 0x62, 0xB2, + 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, + 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x7A, 0x00, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, 0x63, 0x27, + 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x07, 0x62, 0x99, + 0x63, 0x27, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x07, 0x62, 0x05, 0x63, 0x2A, + 0x66, 0x0A, 0x65, 0x06, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x60, 0x36, 0x63, 0x0A, 0x63, 0x2E, 0x66, 0x0B, + 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x16, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x0E, 0x62, 0x99, + 0x63, 0x27, 0x7A, 0x00, 0x66, 0x0A, 0x65, 0x8B, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, + 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x5F, 0x06, + 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x2B, 0x66, 0x0A, 0x65, 0x44, 0x66, 0x2A, 0x25, 0xE4, + 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, + 0x5F, 0xB2, 0x60, 0x32, 0x7A, 0x00, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x05, + 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x47, 0x66, 0x0A, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x07, 0x55, 0x16, 0x43, 0x08, 0x75, + 0x17, 0x65, 0x09, 0x01, 0x18, 0x01, 0x61, 0x0B, 0x05, 0x22, 0x14, 0x22, 0x06, 0x00, 0x15, 0x00, + 0x60, 0x12, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x7A, 0x00, 0x1F, 0x0A, 0x66, 0x2A, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA4, + 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x7A, 0x00, 0x66, 0x2B, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, + 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, + 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, + 0x7A, 0x00, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x5F, 0x05, + 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA4, 0x63, 0x26, 0x69, 0x23, 0x7A, 0xA3, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x7A, 0x00, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x26, 0x69, 0x02, + 0x7A, 0xA4, 0x63, 0x06, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0A, 0x66, 0x0A, 0x69, 0x03, 0x7A, 0xA4, 0x5F, 0x65, 0x60, 0x27, + 0x63, 0x06, 0x65, 0x65, 0x66, 0x27, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x60, 0x07, 0x5F, 0x99, 0x60, 0x27, 0x66, 0x07, 0x65, 0x99, 0x66, 0x27, 0x69, 0x02, 0x7A, 0x00, + 0x69, 0x22, 0x7A, 0xA3, 0x60, 0x07, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x66, 0x07, 0x65, 0x05, + 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, + 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0B, + 0x65, 0x06, 0x66, 0x2B, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, + 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x62, 0x99, 0x7A, 0x00, 0x63, 0x27, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x07, 0x66, 0x0A, + 0x65, 0x44, 0x66, 0x2A, 0x7A, 0x00, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x27, + 0x69, 0x23, 0x7A, 0xA4, 0x63, 0x07, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x27, 0x69, 0x02, 0x7A, 0xA3, 0x63, 0x07, 0x63, 0x27, + 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, + 0x69, 0x03, 0x7A, 0xA4, 0x60, 0x0A, 0x66, 0x0A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x60, 0x2A, 0x63, 0x27, + 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, + 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0A, 0x65, 0x99, + 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x7A, 0x00, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x27, + 0x66, 0x0B, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x07, + 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x62, 0x05, 0x63, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, + 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x8B, 0x60, 0x2A, 0x63, 0x0A, 0x66, 0x0B, 0x65, 0x8B, 0x66, 0x2A, + 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x63, 0x2A, 0x69, 0x23, 0x7A, 0xA3, 0x63, 0x0A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x2A, 0x69, 0x02, 0x7A, 0xA4, 0x63, 0x0A, 0x63, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x03, 0x7A, 0xA3, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, + 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x0A, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, + 0x60, 0x0A, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, + 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0E, 0x63, 0x0A, + 0x66, 0x0E, 0x7A, 0xA3, 0x60, 0x2E, 0x7A, 0x00, 0x63, 0x2A, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x0A, 0x7A, 0xA4, + 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x2A, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x7A, 0x00, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, + 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x63, 0x0A, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x62, 0xB2, + 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x65, + 0x60, 0x2B, 0x63, 0x06, 0x66, 0x0B, 0x65, 0x65, 0x66, 0x2B, 0x69, 0x03, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x69, 0x23, 0x7A, 0xA4, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x26, 0x7A, 0x00, + 0x69, 0x02, 0x7A, 0xA3, 0x63, 0x06, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0x00, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA3, 0x60, 0x0B, + 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0x05, 0x66, 0x2A, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, + 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE4, + 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, + 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, + 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x7A, 0x00, 0x5F, 0x05, + 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x69, 0x23, 0x7A, 0xA4, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x26, 0x69, 0x02, 0x7A, 0xA4, + 0x63, 0x06, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x06, 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x60, 0x0A, 0x60, 0x2A, 0x63, 0x26, + 0x66, 0x0A, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x7A, 0x00, + 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, + 0x63, 0x26, 0x66, 0x0B, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, + 0x63, 0x06, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x62, 0x99, 0x63, 0x27, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x7A, 0xA3, 0x60, 0x0A, 0x5F, 0x44, 0x60, 0x2A, 0x63, 0x07, 0x66, 0x0A, 0x65, 0x44, + 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA4, 0x63, 0x27, 0x69, 0x23, 0x7A, 0xA3, + 0x63, 0x07, 0x7A, 0x00, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x27, 0x69, 0x02, 0x7A, 0xA4, 0x63, 0x07, 0x63, 0x27, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, + 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x03, 0x7A, 0xA4, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x69, 0x02, 0x69, 0x22, + 0x7A, 0xA4, 0x60, 0x0A, 0x60, 0x2A, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2A, 0x25, 0xE8, 0x7A, 0x00, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x0A, + 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0A, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, + 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, + 0x60, 0x0B, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x27, 0x66, 0x0B, 0x7A, 0x00, 0x65, 0x65, 0x66, 0x2B, + 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, + 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x47, 0x63, 0x07, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, + 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0B, 0x65, 0x06, + 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x7A, 0xA2, 0x60, 0x0B, 0x5F, 0x05, + 0x60, 0x2E, 0x63, 0x0A, 0x66, 0x0B, 0x7A, 0x00, 0x65, 0x05, 0x66, 0x2E, 0x69, 0x03, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x2A, 0x69, 0x23, 0x7A, 0xA4, 0x60, 0x0E, 0x60, 0x2E, 0x63, 0x0A, + 0x66, 0x0E, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x2A, 0x69, 0x02, 0x7A, 0xA3, 0x60, 0x0E, 0x7A, 0x00, 0x60, 0x2E, + 0x63, 0x0A, 0x63, 0x2A, 0x66, 0x0E, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x69, 0x03, 0x7A, 0xA4, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x0E, 0x60, 0x2E, 0x63, 0x0A, 0x66, 0x0E, 0x66, 0x2E, + 0x69, 0x02, 0x7A, 0xA4, 0x60, 0x0E, 0x5F, 0x44, 0x60, 0x2E, 0x62, 0x44, 0x63, 0x2A, 0x66, 0x0E, + 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x47, 0x60, 0x0E, 0x5F, 0x05, 0x60, 0x2E, 0x63, 0x0A, 0x62, 0x05, 0x63, 0x2A, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0E, 0x5F, 0x99, 0x60, 0x2B, 0x63, 0x0A, + 0x62, 0x99, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0x06, + 0x60, 0x2B, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0B, 0x65, 0x06, 0x7A, 0x00, 0x66, 0x2B, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x69, 0x02, 0x7A, 0xA4, + 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, + 0x7A, 0x00, 0x33, 0x04, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x69, 0x02, 0x69, 0x22, 0x7A, 0xA4, 0x63, 0x06, 0x69, 0x02, 0x38, 0x0E, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0A, 0x66, 0x0A, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0x6C, 0x69, 0x02, 0x69, 0x22, 0x7A, 0x6D, 0x69, 0x02, 0x69, 0x22, 0x7A, 0x6D, + 0x07, 0xF2, 0x16, 0xF2, 0x08, 0xF2, 0x17, 0xF2, 0x09, 0x00, 0x18, 0x00, 0x61, 0x08, 0x05, 0x27, + 0x14, 0x32, 0x06, 0x4F, 0x15, 0x08, 0x5F, 0x65, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x26, 0x7A, 0x00, + 0x1F, 0x16, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA1, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x7A, 0x00, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, + 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x66, 0x0E, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x7A, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, 0x7A, 0x00, + 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, + 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x33, 0x63, 0x06, 0x7A, 0x00, 0x62, 0xB2, 0x63, 0x26, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x16, 0x7A, 0x00, 0x5F, 0xB2, 0x60, 0x32, + 0x63, 0x26, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x7A, 0x00, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x60, 0x12, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, + 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, + 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x65, 0x63, 0x27, + 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, + 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, + 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x7A, 0x00, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, + 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x7A, 0x00, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x7A, 0x00, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x33, + 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x7A, 0x00, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, + 0x66, 0x0E, 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, + 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, + 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, 0x63, 0x07, + 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x60, 0x13, 0x7A, 0x00, 0x60, 0x33, 0x63, 0x27, 0x7A, 0xA3, 0x60, 0x13, + 0x60, 0x33, 0x63, 0x07, 0x63, 0x27, 0x66, 0x0A, 0x7A, 0x00, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, + 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x06, + 0x60, 0x33, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, + 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x07, 0x7A, 0xA3, + 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, + 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, + 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x60, 0x13, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x7A, 0xA3, 0x66, 0x0A, + 0x66, 0x2E, 0x7A, 0x00, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x60, 0x13, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x7A, 0xA4, 0x5F, 0x05, + 0x60, 0x36, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, + 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, + 0x62, 0x05, 0x63, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x16, 0x5F, 0x65, 0x60, 0x33, 0x63, 0x0A, 0x62, 0xB2, 0x63, 0x26, + 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x7A, 0x00, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x05, 0x7A, 0x00, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x26, + 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x06, 0x62, 0x05, 0x63, 0x26, + 0x66, 0x0B, 0x7A, 0x00, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, + 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x60, 0x33, 0x7A, 0x00, 0x63, 0x06, 0x66, 0x0E, + 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x63, 0x26, + 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x7A, 0x00, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x63, 0x06, + 0x62, 0x05, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x33, 0x63, 0x06, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, + 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x7A, 0x00, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x26, 0x66, 0x0E, 0x65, 0x05, + 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x7A, 0x00, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA2, 0x63, 0x06, 0x7A, 0xA4, 0x60, 0x16, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x26, 0x66, 0x0E, + 0x65, 0x65, 0x7A, 0x00, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x06, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x60, 0x12, + 0x7A, 0x00, 0x5F, 0x06, 0x60, 0x33, 0x63, 0x26, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, + 0x63, 0x06, 0x62, 0x99, 0x63, 0x27, 0x7A, 0x00, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, + 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x07, 0x62, 0x65, 0x63, 0x27, + 0x66, 0x0E, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x7A, 0x00, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0x06, 0x63, 0x27, 0x66, 0x0E, + 0x7A, 0x00, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x63, 0x07, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0B, + 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, + 0x7A, 0x00, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x7A, 0x00, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, + 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x7A, 0x00, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, + 0x65, 0x44, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x7A, 0x00, 0x35, 0x07, 0x27, 0x02, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, + 0x7A, 0x00, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x27, + 0x66, 0x0E, 0x65, 0x99, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x7A, 0x00, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x63, 0x07, 0x66, 0x0B, 0x65, 0xB2, + 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x7A, 0x00, 0x27, 0x02, 0x6A, 0x0F, + 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x60, 0x33, 0x62, 0xB2, 0x63, 0x26, 0x7A, 0xA4, 0x60, 0x13, 0x60, 0x33, 0x63, 0x06, 0x62, 0x99, + 0x63, 0x27, 0x66, 0x0A, 0x66, 0x2E, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x32, 0x00, 0x7A, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, + 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x65, 0x60, 0x33, 0x62, 0x65, + 0x63, 0x27, 0x66, 0x0E, 0x7A, 0x00, 0x65, 0x05, 0x66, 0x2E, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, + 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA2, 0x63, 0x07, 0x7A, 0xA4, 0x60, 0x13, + 0x60, 0x33, 0x63, 0x27, 0x66, 0x0E, 0x65, 0x65, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, + 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, + 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x13, 0x60, 0x33, + 0x63, 0x07, 0x7A, 0x00, 0x63, 0x27, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, + 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, + 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, 0x60, 0x13, 0x5F, 0x99, 0x60, 0x33, 0x63, 0x07, + 0x62, 0x99, 0x63, 0x27, 0x7A, 0xA4, 0x60, 0x13, 0x5F, 0x05, 0x60, 0x36, 0x63, 0x07, 0x62, 0x05, + 0x63, 0x2A, 0x66, 0x0A, 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, + 0x27, 0x01, 0x7A, 0x00, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x16, 0x7A, 0x00, 0x60, 0x36, 0x63, 0x0A, + 0x63, 0x2E, 0x66, 0x0B, 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, + 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, + 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x16, 0x5F, 0x99, 0x60, 0x33, + 0x63, 0x0E, 0x62, 0x99, 0x63, 0x27, 0x66, 0x0A, 0x65, 0x8B, 0x66, 0x2A, 0x25, 0xE8, 0x34, 0xE9, + 0x26, 0x07, 0x7A, 0x00, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, + 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x60, 0x13, + 0x5F, 0x06, 0x60, 0x33, 0x63, 0x07, 0x62, 0x06, 0x63, 0x2B, 0x66, 0x0A, 0x65, 0x44, 0x66, 0x2A, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, + 0x69, 0x03, 0x68, 0x44, 0x7A, 0x00, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, + 0x60, 0x13, 0x5F, 0xB2, 0x60, 0x32, 0x63, 0x0B, 0x62, 0xB2, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x05, + 0x66, 0x2A, 0x25, 0xE8, 0x7A, 0x00, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, + 0x7A, 0x48, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x7A, 0x00, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, + 0x7B, 0x00, 0x7A, 0x47, 0x66, 0x0A, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, + 0x6A, 0x06, 0x7A, 0x00, 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x38, 0x0E, + 0x6C, 0x16, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x48, 0x63, 0x06, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, + 0x35, 0x07, 0x27, 0x02, 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, + 0x6C, 0x16, 0x7A, 0x00, 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x07, 0x55, 0x16, 0x43, 0x08, 0x75, + 0x17, 0x65, 0x09, 0x01, 0x7A, 0x00, 0x18, 0x01, 0x61, 0x0B, 0x05, 0x22, 0x14, 0x22, 0x06, 0x00, + 0x15, 0x00, 0x60, 0x12, 0x5F, 0x05, 0x60, 0x2A, 0x63, 0x26, 0x1F, 0x0A, 0x7A, 0x00, 0x66, 0x2A, + 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, 0x32, 0x00, 0x33, 0x04, + 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA1, 0x63, 0x06, + 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x65, 0x60, 0x2B, 0x63, 0x26, 0x66, 0x0A, 0x65, 0x65, 0x66, 0x2B, + 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, 0x6A, 0x0F, 0x32, 0x06, + 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x38, 0x16, 0x6C, 0x16, 0x6C, 0x36, 0x7A, 0xA3, + 0x63, 0x06, 0x7A, 0xA3, 0x60, 0x0B, 0x5F, 0x06, 0x60, 0x2B, 0x7A, 0x00, 0x63, 0x26, 0x66, 0x0B, + 0x65, 0x06, 0x66, 0x2B, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0x00, 0x38, 0x0E, 0x6C, 0x16, + 0x6C, 0x36, 0x7B, 0x00, 0x7A, 0x47, 0x60, 0x0B, 0x5F, 0xB2, 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0B, + 0x65, 0xB2, 0x66, 0x2A, 0x25, 0xE4, 0x34, 0xF5, 0x26, 0x06, 0x35, 0x07, 0x27, 0x02, 0x7A, 0x00, + 0x6A, 0x0F, 0x32, 0x06, 0x33, 0x00, 0x69, 0x03, 0x68, 0x44, 0x69, 0x22, 0x6C, 0x16, 0x6C, 0x36, + 0x7A, 0xA3, 0x63, 0x26, 0x25, 0xE8, 0x34, 0xE9, 0x26, 0x07, 0x35, 0x08, 0x27, 0x01, 0x6A, 0x06, + 0x32, 0x00, 0x33, 0x04, 0x69, 0x02, 0x68, 0x65, 0x69, 0x23, 0x7A, 0xA4, 0x60, 0x0A, 0x5F, 0x05, + 0x60, 0x2A, 0x63, 0x06, 0x66, 0x0A, 0x65, 0x05, 0x66, 0x2A, 0x69, 0x03, 0x6C, 0x16, 0x6C, 0x36}; + +// rudolph.dro - Rudolph, the red-nosed Reindeer (Recording of RUDOLPH.CMF) +const unsigned char RODOLPH_DRO[0x1451] = +{ 0x44, 0x42, 0x52, 0x41, 0x57, 0x4F, 0x50, 0x4C, 0xE2, 0x36, 0x01, 0x00, 0x40, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x01, 0x20, 0x02, 0x08, 0x04, 0x08, 0x0B, 0x08, 0x0D, 0x08, 0x0F, 0x08, 0x16, + 0x08, 0x18, 0x08, 0x1A, 0x08, 0x20, 0x01, 0x21, 0x01, 0x22, 0x01, 0x23, 0x11, 0x24, 0x11, 0x25, + 0x11, 0x28, 0x01, 0x29, 0x01, 0x2A, 0x01, 0x2B, 0x11, 0x2C, 0x11, 0x2D, 0x11, 0x30, 0x01, 0x31, + 0x01, 0x32, 0x01, 0x33, 0x11, 0x34, 0x11, 0x35, 0x11, 0x40, 0x4F, 0x41, 0x4F, 0x42, 0x4F, 0x48, + 0x4F, 0x49, 0x4F, 0x4A, 0x4F, 0x50, 0x4F, 0x51, 0x4F, 0x52, 0x4F, 0x60, 0xF1, 0x61, 0xF1, 0x62, + 0xF1, 0x63, 0xD2, 0x64, 0xF2, 0x65, 0xF2, 0x68, 0xF1, 0x69, 0xF1, 0x6A, 0xF1, 0x6B, 0xF2, 0x6C, + 0xF2, 0x6D, 0xF2, 0x70, 0xF1, 0x71, 0xF1, 0x72, 0xF1, 0x73, 0xF2, 0x74, 0xF2, 0x75, 0xF2, 0x80, + 0x53, 0x81, 0x53, 0x82, 0x53, 0x83, 0x74, 0x84, 0x74, 0x85, 0x74, 0x88, 0x53, 0x89, 0x53, 0x8A, + 0x53, 0x8B, 0x74, 0x8C, 0x74, 0x8D, 0x74, 0x90, 0x53, 0x91, 0x53, 0x92, 0x53, 0x93, 0x74, 0x94, + 0x74, 0x95, 0x74, 0xA0, 0x02, 0xA1, 0x81, 0xA2, 0x87, 0xBD, 0xE0, 0xB0, 0x36, 0x21, 0x01, 0x24, + 0x11, 0x41, 0x4F, 0x61, 0xF1, 0x64, 0xD2, 0x81, 0x53, 0x84, 0x74, 0xE1, 0x00, 0x00, 0x05, 0xE4, + 0x00, 0xC1, 0x00, 0x44, 0x00, 0xA1, 0x81, 0xB1, 0x35, 0x22, 0x01, 0x25, 0x11, 0x42, 0x4F, 0x00, + 0x05, 0x62, 0xF1, 0x65, 0xD2, 0x82, 0x53, 0x85, 0x74, 0xE2, 0x00, 0xE5, 0x00, 0xC2, 0x00, 0x45, + 0x00, 0xA2, 0x87, 0x00, 0x05, 0xB2, 0x32, 0x00, 0xCB, 0xA0, 0x02, 0xB0, 0x16, 0x43, 0x00, 0xA0, + 0x41, 0xB0, 0x36, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x16, 0x43, 0x00, 0xA0, 0x02, 0xB0, 0x36, 0x01, + 0xBF, 0x01, 0xA1, 0x81, 0xB1, 0x15, 0xA2, 0x87, 0xB2, 0x12, 0xA0, 0x02, 0xB0, 0x16, 0x43, 0x00, + 0xA0, 0xCA, 0xB0, 0x35, 0x00, 0x05, 0x44, 0x00, 0xA1, 0x57, 0xB1, 0x35, 0x45, 0x00, 0xA2, 0x41, + 0xB2, 0x32, 0x00, 0xD3, 0xA0, 0xCA, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x02, 0xB0, 0x36, 0x00, 0xDA, + 0xA0, 0x02, 0xB0, 0x16, 0x43, 0x00, 0xA0, 0xCA, 0xB0, 0x35, 0x01, 0xBF, 0x01, 0xA1, 0x57, 0xB1, + 0x15, 0xA2, 0x41, 0xB2, 0x12, 0xA0, 0xCA, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0xB0, 0x00, 0x05, 0xB0, + 0x35, 0x44, 0x00, 0xA1, 0x87, 0xB1, 0x32, 0x45, 0x00, 0xA2, 0x02, 0xB2, 0x32, 0x00, 0xD4, 0xA0, + 0xB0, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0xCA, 0xB0, 0x35, 0x00, 0xE4, 0xA0, 0xCA, 0xB0, 0x15, 0x43, + 0x00, 0xA0, 0xB0, 0xB0, 0x35, 0x01, 0xB4, 0x01, 0xA1, 0x87, 0xB1, 0x12, 0xA2, 0x02, 0xB2, 0x12, + 0xA0, 0xB0, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x81, 0x00, 0x05, 0xB0, 0x35, 0x44, 0x00, 0xA1, 0x41, + 0xB1, 0x32, 0x45, 0x00, 0xA2, 0xCA, 0xB2, 0x31, 0x01, 0x79, 0x03, 0xA0, 0x81, 0xB0, 0x15, 0xA1, + 0x41, 0xB1, 0x12, 0xA2, 0xCA, 0xB2, 0x11, 0x43, 0x00, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x44, + 0x00, 0xA1, 0x02, 0xB1, 0x32, 0x45, 0x00, 0xA2, 0xB0, 0xB2, 0x31, 0x00, 0xD4, 0xA0, 0x57, 0xB0, + 0x15, 0x43, 0x00, 0xA0, 0x81, 0xB0, 0x35, 0x00, 0xD9, 0xA0, 0x81, 0xB0, 0x15, 0x43, 0x00, 0xA0, + 0x57, 0xB0, 0x35, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x12, 0xA2, 0xB0, 0xB2, 0x11, 0xA0, 0x57, + 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x44, 0x00, 0xA1, 0xE5, 0xB1, 0x31, + 0x45, 0x00, 0xA2, 0x98, 0xB2, 0x31, 0x00, 0xD4, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x00, 0xA0, 0x57, + 0xB0, 0x35, 0x00, 0xE4, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x00, 0xA0, 0x87, 0xB0, 0x32, 0x01, 0xB5, + 0x01, 0xA1, 0xE5, 0xB1, 0x11, 0xA2, 0x98, 0xB2, 0x11, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x00, 0xA0, + 0x41, 0xB0, 0x32, 0x00, 0x05, 0x44, 0x00, 0xA1, 0xCA, 0xB1, 0x31, 0x45, 0x00, 0xA2, 0x81, 0xB2, + 0x31, 0x01, 0xB9, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x00, 0xA0, 0x81, 0xB0, 0x35, 0x01, 0xBE, + 0x01, 0xA1, 0xCA, 0xB1, 0x11, 0xA2, 0x81, 0xB2, 0x11, 0xA0, 0x81, 0xB0, 0x15, 0x43, 0x00, 0xA0, + 0x02, 0x00, 0x05, 0xB0, 0x32, 0x44, 0x00, 0xA1, 0xCA, 0xB1, 0x31, 0x45, 0x00, 0xA2, 0x87, 0xB2, + 0x2E, 0x01, 0xAF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA1, 0xCA, 0xB1, 0x11, 0xA2, 0x87, 0xB2, 0x0E, + 0x20, 0x32, 0x23, 0x61, 0x00, 0x05, 0x40, 0x9A, 0x60, 0x51, 0x63, 0xA2, 0x80, 0x1B, 0x83, 0x3B, + 0xE0, 0x00, 0xE3, 0x00, 0xC0, 0x00, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD8, + 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x87, 0xB0, 0x12, + 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x21, 0x31, 0x24, 0x61, 0x41, 0x1C, 0x61, 0x41, 0x00, 0x05, + 0x64, 0x92, 0x81, 0x0B, 0x84, 0x3B, 0xE1, 0x00, 0xE4, 0x00, 0xC1, 0x00, 0x44, 0x80, 0xA1, 0xB0, + 0x00, 0x05, 0xB1, 0x31, 0x22, 0x31, 0x25, 0x61, 0x42, 0x1C, 0x62, 0x41, 0x65, 0x92, 0x82, 0x0B, + 0x85, 0x3B, 0xE2, 0x00, 0x00, 0x05, 0xE5, 0x00, 0xC2, 0x00, 0x45, 0x80, 0xA2, 0x41, 0xB2, 0x2E, + 0x28, 0x01, 0x2B, 0x11, 0x48, 0x4F, 0x00, 0x05, 0x68, 0xF1, 0x6B, 0xD2, 0x88, 0x53, 0x8B, 0x74, + 0xE8, 0x00, 0xEB, 0x00, 0xC3, 0x00, 0x4B, 0x00, 0xA3, 0xCA, 0x00, 0x05, 0xB3, 0x2D, 0x00, 0xC6, + 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0xB0, 0xB1, 0x11, 0xA2, 0x41, 0xB2, + 0x0E, 0xA3, 0xCA, 0xB3, 0x0D, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, + 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2E, 0x00, 0x05, 0x4B, + 0x00, 0xA3, 0xB0, 0xB3, 0x2D, 0x00, 0xD8, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, + 0x32, 0x01, 0xB4, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x00, 0xE4, + 0xA1, 0x81, 0xB1, 0x11, 0xA2, 0x02, 0xB2, 0x0E, 0xA3, 0xB0, 0xB3, 0x0D, 0xA0, 0xB0, 0xB0, 0x11, + 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, + 0xA2, 0xCA, 0x00, 0x05, 0xB2, 0x2D, 0x4B, 0x00, 0xA3, 0x81, 0xB3, 0x2D, 0x00, 0xCE, 0xA0, 0x41, + 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0xCA, 0xB0, 0x11, 0x43, + 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0x57, 0xB1, 0x11, 0xA2, 0xCA, 0xB2, 0x0D, 0xA3, + 0x81, 0xB3, 0x0D, 0xA0, 0x81, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x41, 0xB0, 0x32, 0x44, + 0x80, 0xA1, 0x87, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x00, 0x05, 0x4B, 0x00, 0xA3, + 0x57, 0xB3, 0x2D, 0x00, 0xCD, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, + 0x9A, 0x02, 0xA1, 0x87, 0xB1, 0x0E, 0xA2, 0xB0, 0xB2, 0x0D, 0xA3, 0x57, 0xB3, 0x0D, 0xA0, 0x02, + 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x44, 0x80, 0xA1, 0xB0, 0xB1, 0x31, + 0x45, 0x80, 0xA2, 0x41, 0xB2, 0x2E, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0xCA, 0xB3, 0x2D, 0x00, 0xD8, + 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xB5, 0x01, 0xA0, 0x41, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xE4, 0xA1, 0xB0, 0xB1, 0x11, 0xA2, 0x41, 0xB2, + 0x0E, 0xA3, 0xCA, 0xB3, 0x0D, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, + 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2E, 0x00, 0x05, 0x4B, + 0x00, 0xA3, 0xB0, 0xB3, 0x2D, 0x00, 0xCE, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, + 0x32, 0x01, 0xBE, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x00, 0xDA, + 0xA1, 0x81, 0xB1, 0x11, 0xA2, 0x02, 0xB2, 0x0E, 0xA3, 0xB0, 0xB3, 0x0D, 0xA0, 0xB0, 0xB0, 0x11, + 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, + 0xA2, 0xCA, 0xB2, 0x2D, 0x00, 0x05, 0x4B, 0x00, 0xA3, 0x81, 0xB3, 0x2D, 0x00, 0xCE, 0xA0, 0x41, + 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0xCA, 0xB0, 0x11, 0x43, + 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x00, 0xDA, 0xA1, 0x57, 0xB1, 0x11, 0xA2, 0xCA, 0xB2, 0x0D, 0xA3, + 0x81, 0xB3, 0x0D, 0xA0, 0x81, 0xB0, 0x11, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x41, 0xB0, 0x32, 0x44, + 0x80, 0xA1, 0x87, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x00, 0x05, 0x4B, 0x00, 0xA3, + 0x57, 0xB3, 0x2D, 0x00, 0xD8, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, + 0x99, 0x02, 0xA1, 0x87, 0xB1, 0x0E, 0xA2, 0xB0, 0xB2, 0x0D, 0xA3, 0x57, 0xB3, 0x0D, 0xA0, 0x02, + 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x29, 0xB2, 0x2C, 0xB1, 0x49, 0xCD, + 0x69, 0x91, 0x6C, 0x91, 0x00, 0x05, 0x89, 0x2A, 0x8C, 0x2A, 0xE9, 0x00, 0xEC, 0x00, 0xC4, 0x00, + 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x41, 0xB1, 0x2E, 0x45, 0x80, + 0xA2, 0x57, 0xB2, 0x2D, 0x01, 0xAD, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x43, + 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xB9, 0x01, + 0xA1, 0x41, 0xB1, 0x0E, 0xA2, 0x57, 0xB2, 0x0D, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0xB0, 0xB4, 0x11, + 0x00, 0x05, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x81, 0xB4, 0x31, 0x44, 0x80, + 0xA1, 0x20, 0xB1, 0x2E, 0x00, 0x05, 0x45, 0x80, 0xA2, 0x87, 0xB2, 0x2A, 0x01, 0xB3, 0x01, 0xA0, + 0xB0, 0xB0, 0x11, 0xA4, 0x81, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0x00, + 0x05, 0xA4, 0x81, 0xB4, 0x31, 0x01, 0xAE, 0x01, 0xA1, 0x20, 0xB1, 0x0E, 0xA2, 0x87, 0xB2, 0x0A, + 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x81, 0xB4, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, + 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x2A, 0xB2, 0x2D, 0xB1, 0x00, 0x05, 0x4A, 0xCD, 0x6A, 0x91, + 0x6D, 0x91, 0x8A, 0x2A, 0x8D, 0x2A, 0xEA, 0x00, 0xED, 0x00, 0xC5, 0x00, 0x4D, 0x80, 0x00, 0x05, + 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2A, 0x01, 0xB8, 0x01, 0xA1, 0x41, 0xB1, + 0x0A, 0x44, 0x80, 0xA1, 0xB0, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA1, 0xB0, 0xB1, 0x0D, 0x44, 0x80, + 0xA1, 0x41, 0xB1, 0x2E, 0x01, 0xB4, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0xA5, + 0x57, 0xB5, 0x11, 0xA1, 0x41, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, + 0xDE, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x87, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x00, 0x05, 0x4D, + 0x80, 0xA5, 0xB0, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x41, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x57, + 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x01, 0xB4, 0x01, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, + 0xB0, 0xB5, 0x11, 0xA1, 0x41, 0xB1, 0x0A, 0xA0, 0x57, 0xB0, 0x15, 0x00, 0x05, 0x43, 0x80, 0xA0, + 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0xB0, 0x00, 0x05, 0xB5, + 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xD8, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, + 0x11, 0xA5, 0xB0, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, + 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x01, 0xAF, 0x01, 0xA0, 0x87, 0xB0, 0x12, + 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x41, 0x00, 0x05, 0xB0, 0x32, + 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x00, 0xDE, 0xA1, 0x81, + 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0x00, 0x05, + 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, + 0xB5, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xA8, 0x01, 0xA5, 0x57, 0xB5, + 0x11, 0x4D, 0x80, 0xA5, 0x41, 0xB5, 0x2E, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0xA5, 0x41, + 0xB5, 0x0E, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x02, 0xB1, 0x2A, + 0x45, 0x80, 0xA2, 0xCA, 0xB2, 0x2D, 0x01, 0x6F, 0x03, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, + 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0xA2, 0xCA, 0x00, 0x05, 0xB2, 0x0D, 0x43, + 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0x00, 0x05, 0xA1, + 0x57, 0xB1, 0x2D, 0x00, 0xD8, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, + 0xDA, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x57, 0xB1, 0x0D, 0x00, 0xD9, 0xA0, 0x41, 0xB0, 0x12, 0x43, + 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, + 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, + 0xAF, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, + 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x01, 0xB9, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, + 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, + 0xA1, 0x57, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0x00, 0x05, + 0xB4, 0x31, 0x01, 0xAE, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA4, 0xB0, + 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xBF, 0x01, 0xA0, + 0x02, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, + 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD4, 0xA0, 0x02, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD9, 0xA1, 0x57, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE5, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x41, 0xB0, 0x32, 0x00, 0xD9, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, + 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x00, 0x05, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xB9, 0x01, + 0xA4, 0xB0, 0xB4, 0x11, 0xA0, 0x02, 0xB0, 0x12, 0xA2, 0xB0, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x57, + 0x00, 0x05, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x45, 0x80, 0xA2, 0x98, 0xB2, 0x2D, + 0x01, 0xAF, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, 0x11, 0xA2, + 0x98, 0xB2, 0x0D, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, + 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, + 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xBE, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x44, + 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB5, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x02, + 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, + 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0x05, 0x4C, + 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xD3, 0xA0, 0xCA, 0xB0, + 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE5, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x81, 0xB1, + 0x0D, 0x00, 0xD9, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xDA, 0xA0, + 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x2E, 0x00, + 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, + 0xB4, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x01, 0xB9, + 0x01, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, + 0x2D, 0x01, 0xB4, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x81, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, + 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, + 0x01, 0xB9, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, + 0x01, 0xB5, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0x43, + 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, + 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x00, 0xD8, 0xA0, 0x02, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xD9, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x02, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, + 0xE4, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, + 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0x98, 0xB4, 0x31, 0x4D, + 0x80, 0xA5, 0x87, 0x00, 0x05, 0xB5, 0x2E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xA9, 0x01, + 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA4, 0x98, 0xB4, + 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0x00, + 0x05, 0xA0, 0xB0, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x57, + 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x41, 0xB5, 0x2E, 0x01, 0xB4, + 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA4, 0x57, 0xB4, 0x11, 0xA5, 0x41, + 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x57, 0x00, 0x05, 0xB4, 0x31, 0x4D, 0x80, + 0xA5, 0x41, 0xB5, 0x2E, 0x01, 0xB9, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA5, + 0x41, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, + 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD3, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x57, 0xB1, 0x0D, 0x00, 0xE4, 0xA0, + 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, + 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0x05, 0x44, 0x80, 0xA1, + 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x02, + 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x01, 0xAE, 0x01, 0xA0, 0x57, 0xB0, + 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xBF, 0x01, + 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x57, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, + 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xB9, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, + 0xB5, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, + 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, + 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0x00, 0x05, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, + 0xD3, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xE4, 0xA1, 0x57, 0xB1, + 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x02, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x02, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x00, 0x05, 0x45, 0x80, 0xA2, 0xB0, 0xB2, + 0x2D, 0x01, 0xB9, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA0, 0x02, 0xB0, 0x12, 0xA2, 0xB0, 0xB2, 0x0D, + 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x45, 0x80, + 0xA2, 0x98, 0xB2, 0x2D, 0x01, 0xB9, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0xA4, + 0xE5, 0xB4, 0x11, 0xA2, 0x98, 0xB2, 0x0D, 0x43, 0x80, 0x00, 0x05, 0xA0, 0x87, 0xB0, 0x32, 0x44, + 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xAE, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, + 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xBF, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA5, + 0xCA, 0xB5, 0x11, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA1, 0x02, 0xB1, 0x0A, + 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0xB4, 0x01, 0xA0, + 0x87, 0xB0, 0x12, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0xCA, 0x00, + 0x05, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, + 0xDF, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xD9, 0xA4, 0x57, 0xB4, + 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, + 0x31, 0x00, 0xE4, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, + 0x87, 0xB4, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xAF, 0x01, 0xA0, 0x81, + 0xB0, 0x11, 0xA4, 0x87, 0xB4, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, + 0x01, 0xBE, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, + 0x81, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x81, 0xB1, 0x0D, 0x43, 0x80, + 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0x00, 0x05, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, + 0xB5, 0x2E, 0x01, 0xAF, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA1, 0x02, + 0xB1, 0x0A, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x87, 0xB5, + 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, + 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x00, 0xCD, 0xA0, + 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, + 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, + 0x32, 0x00, 0xDA, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, + 0x41, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, + 0x31, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, + 0xB4, 0x11, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x35, 0x01, 0xB4, 0x01, 0xA0, + 0x81, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x01, 0xBF, 0x01, 0x4C, 0x80, 0xA4, 0xB0, + 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x44, 0x80, 0xA1, 0x57, 0x00, 0x05, 0xB1, 0x2D, + 0x01, 0xB9, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0xA1, 0x57, 0xB1, 0x0D, 0x4C, + 0x80, 0xA4, 0xCA, 0x00, 0x05, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x01, 0xAF, 0x01, + 0xA4, 0xCA, 0xB4, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x44, 0x80, + 0xA1, 0xB0, 0x00, 0x05, 0xB1, 0x2D, 0x01, 0xB8, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0x02, 0xB4, + 0x12, 0xA1, 0xB0, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x41, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, + 0x02, 0xB4, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, 0x00, 0x05, 0xA2, 0xCA, 0xB2, + 0x2D, 0x01, 0xB3, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xB5, + 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x57, 0xB0, 0x35, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, + 0x31, 0x01, 0xB9, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, + 0x01, 0xA2, 0xCA, 0xB2, 0x0D, 0xA4, 0xCA, 0xB4, 0x11, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x02, 0xB0, 0x32, 0x00, 0x05, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xAE, 0x01, 0xA0, 0x02, + 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA1, 0x81, 0xB1, 0x11, 0xA0, + 0xB0, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x6B, 0x00, 0x05, 0xB1, + 0x31, 0x01, 0x93, 0x02, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x00, 0xDA, 0xA2, 0xB0, 0xB2, 0x0D, + 0xA0, 0x02, 0xB0, 0x12, 0xA1, 0x6B, 0xB1, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x00, 0x05, 0x43, 0x80, + 0xA0, 0xCA, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0x81, 0xB2, 0x2D, + 0x01, 0xB9, 0x01, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, + 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, + 0x00, 0x05, 0xA1, 0x87, 0xB1, 0x2E, 0x01, 0xAE, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0xCA, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA2, 0x81, 0xB2, 0x0D, 0xA1, 0x87, 0xB1, 0x0E, 0xA0, 0xCA, + 0xB0, 0x11, 0x43, 0x80, 0xA0, 0xB0, 0x00, 0x05, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, + 0x45, 0x80, 0xA2, 0x57, 0xB2, 0x2D, 0x00, 0xD4, 0xA1, 0x02, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x41, + 0xB1, 0x2E, 0x00, 0xE4, 0xA1, 0x41, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xB5, + 0x01, 0xA1, 0x02, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xBF, 0x01, 0xA2, 0x57, + 0xB2, 0x0D, 0xA1, 0x02, 0xB1, 0x0E, 0x44, 0x80, 0xA1, 0x63, 0xB1, 0x2E, 0x45, 0x80, 0xA2, 0x6B, + 0x00, 0x05, 0xB2, 0x2D, 0x01, 0xB8, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA1, 0x63, 0xB1, 0x0E, 0xA2, + 0x6B, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x81, 0x00, 0x05, 0xB0, 0x31, 0x44, 0x80, 0xA1, 0x87, 0xB1, + 0x2E, 0x45, 0x80, 0xA2, 0x02, 0xB2, 0x2E, 0x01, 0xAF, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0x43, 0x80, + 0xA0, 0xB0, 0xB0, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x02, 0xB0, + 0x32, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xB4, + 0x01, 0xA1, 0x87, 0xB1, 0x0E, 0xA2, 0x02, 0xB2, 0x0E, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, + 0x87, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, + 0x31, 0x44, 0x80, 0xA1, 0x02, 0x00, 0x05, 0xB1, 0x2E, 0x01, 0xB3, 0x01, 0xA0, 0x87, 0xB0, 0x12, + 0xA4, 0xB0, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0xA1, 0x02, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, + 0xA0, 0x87, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, + 0x00, 0x05, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xB3, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, + 0xB0, 0xB4, 0x11, 0xA5, 0x81, 0xB5, 0x11, 0xA1, 0x02, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, + 0x87, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x00, + 0x05, 0x44, 0x80, 0xA1, 0x20, 0xB1, 0x2E, 0x01, 0xA8, 0x01, 0xA1, 0x20, 0xB1, 0x0E, 0x44, 0x80, + 0xA1, 0xB0, 0xB1, 0x2D, 0x01, 0xBF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, + 0x81, 0xB5, 0x11, 0xA1, 0xB0, 0xB1, 0x0D, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, + 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xB0, 0x00, 0x05, 0xB5, 0x31, 0x44, 0x80, 0xA1, + 0x41, 0xB1, 0x2E, 0x01, 0xB3, 0x01, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xB0, + 0xB5, 0x11, 0xA1, 0x41, 0xB1, 0x0E, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, + 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xB0, 0xB5, 0x31, 0x01, 0xAF, 0x01, 0xA0, 0x57, 0xB0, + 0x15, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0xB0, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x00, + 0x05, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x44, 0x80, 0xA1, + 0x81, 0x00, 0x05, 0xB1, 0x2D, 0x01, 0xB2, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, + 0xA5, 0x81, 0xB5, 0x11, 0x43, 0x80, 0xA0, 0x41, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xE5, + 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x01, 0xB9, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, + 0x41, 0xB0, 0x12, 0xA4, 0xE5, 0xB4, 0x11, 0xA5, 0x57, 0xB5, 0x11, 0x00, 0x05, 0x43, 0x80, 0xA0, + 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0xCA, 0x00, 0x05, 0xB1, + 0x2D, 0x45, 0x80, 0xA2, 0x81, 0xB2, 0x2D, 0x01, 0xA9, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0x57, + 0xB4, 0x11, 0xA1, 0xCA, 0xB1, 0x0D, 0xA2, 0x81, 0xB2, 0x0D, 0x43, 0x80, 0x00, 0x05, 0xA0, 0xCA, + 0xB0, 0x31, 0x00, 0xDE, 0xA0, 0xCA, 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, + 0xA4, 0x87, 0xB4, 0x2E, 0x00, 0x05, 0x44, 0x80, 0xA1, 0xCA, 0xB1, 0x2D, 0x45, 0x80, 0xA2, 0x02, + 0xB2, 0x2A, 0x01, 0x94, 0x02, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, 0xB4, 0x0E, 0xA1, 0xCA, 0xB1, + 0x0D, 0xA2, 0x02, 0xB2, 0x0A, 0x44, 0x80, 0x00, 0x05, 0xA1, 0x02, 0xB1, 0x2E, 0x01, 0xAE, 0x01, + 0xA1, 0x02, 0xB1, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, + 0x00, 0x05, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xDE, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, + 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x57, 0xB1, 0x0D, 0x00, 0xDA, + 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, + 0x43, 0x80, 0xA0, 0xB0, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0x00, 0x05, + 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xAE, 0x01, 0xA0, 0xB0, 0xB0, 0x11, 0xA4, 0x57, 0xB4, 0x11, 0xA1, + 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x57, 0x00, 0x05, 0xB0, 0x35, 0x01, 0xB9, 0x01, 0xA0, 0x57, + 0xB0, 0x15, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x01, 0xBF, + 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x57, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x4C, + 0x80, 0x00, 0x05, 0xA4, 0xB0, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x81, 0xB5, 0x31, 0x01, 0xAE, 0x01, + 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBF, 0x01, 0xA4, 0xB0, 0xB4, 0x11, 0xA1, 0x02, 0xB1, + 0x0A, 0x4C, 0x80, 0xA4, 0xB0, 0xB4, 0x31, 0x01, 0xBF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA5, 0x81, + 0xB5, 0x11, 0xA4, 0xB0, 0xB4, 0x11, 0x43, 0x80, 0xA0, 0x02, 0x00, 0x05, 0xB0, 0x32, 0x4C, 0x80, + 0xA4, 0xB0, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x57, 0xB1, 0x2D, 0x00, 0xD4, 0xA0, 0x02, 0xB0, 0x12, + 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, + 0xB0, 0x32, 0x00, 0xE4, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x00, 0xDA, + 0xA1, 0x57, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x44, 0x80, + 0xA1, 0x57, 0x00, 0x05, 0xB1, 0x31, 0x45, 0x80, 0xA2, 0xB0, 0xB2, 0x2D, 0x01, 0xB8, 0x01, 0xA4, + 0xB0, 0xB4, 0x11, 0xA0, 0x02, 0xB0, 0x12, 0xA2, 0xB0, 0xB2, 0x0D, 0x43, 0x80, 0xA0, 0x57, 0x00, + 0x05, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0xE5, 0xB4, 0x31, 0x45, 0x80, 0xA2, 0x98, 0xB2, 0x2D, 0x01, + 0xAF, 0x01, 0xA1, 0x57, 0xB1, 0x11, 0xA0, 0x57, 0xB0, 0x15, 0xA4, 0xE5, 0xB4, 0x11, 0xA2, 0x98, + 0xB2, 0x0D, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x87, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, + 0x01, 0xB9, 0x01, 0xA1, 0x81, 0xB1, 0x0D, 0x4C, 0x80, 0xA4, 0x02, 0xB4, 0x32, 0x4D, 0x80, 0xA5, + 0x87, 0xB5, 0x2E, 0x01, 0xBF, 0x01, 0xA4, 0x02, 0xB4, 0x12, 0xA5, 0x87, 0xB5, 0x0E, 0x44, 0x80, + 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB4, 0x01, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0x02, 0xB4, + 0x32, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xBF, 0x01, 0xA0, 0x87, 0xB0, 0x12, 0xA4, 0x02, + 0xB4, 0x12, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0xCA, 0x00, 0x05, 0xB0, 0x31, 0x4C, 0x80, + 0xA4, 0x57, 0xB4, 0x31, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, 0x00, 0xD4, 0xA0, 0xCA, 0xB0, 0x11, + 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0xE4, 0xA4, 0x57, 0xB4, 0x11, 0xA1, 0x81, 0xB1, 0x0D, + 0x00, 0xDA, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0xCA, 0xB0, 0x31, 0x00, 0xD9, 0xA0, 0xCA, + 0xB0, 0x11, 0x43, 0x80, 0xA0, 0x81, 0xB0, 0x31, 0x4C, 0x80, 0xA4, 0x87, 0xB4, 0x2E, 0x00, 0x05, + 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xB9, 0x01, 0xA0, 0x81, 0xB0, 0x11, 0xA4, 0x87, 0xB4, + 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x43, 0x80, 0xA0, 0x87, 0x00, 0x05, 0xB0, 0x32, 0x01, 0xB9, 0x01, + 0xA0, 0x87, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x44, 0x80, 0xA1, 0x81, 0xB1, 0x2D, + 0x01, 0xB5, 0x01, 0xA0, 0x41, 0xB0, 0x12, 0xA1, 0x81, 0xB1, 0x0D, 0x43, 0x80, 0xA0, 0x02, 0xB0, + 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0x00, 0x05, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, + 0xB9, 0x01, 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0xBE, 0x01, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, + 0x87, 0xB5, 0x0E, 0xA1, 0x02, 0xB1, 0x0A, 0x4C, 0x80, 0xA4, 0xCA, 0x00, 0x05, 0xB4, 0x31, 0x4D, + 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x01, 0xAF, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, 0x11, + 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x00, 0x05, 0x4C, 0x80, 0xA4, 0xCA, + 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x57, 0xB5, 0x31, 0x44, 0x80, 0xA1, 0x81, 0x00, 0x05, 0xB1, 0x2D, + 0x01, 0xB3, 0x01, 0xA0, 0x02, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, + 0xA0, 0x41, 0xB0, 0x12, 0x43, 0x80, 0xA0, 0x02, 0xB0, 0x32, 0x01, 0xB4, 0x01, 0xA0, 0x02, 0xB0, + 0x12, 0x43, 0x80, 0xA0, 0x41, 0xB0, 0x32, 0x01, 0xBF, 0x01, 0xA4, 0xCA, 0xB4, 0x11, 0xA5, 0x57, + 0xB5, 0x11, 0xA1, 0x81, 0xB1, 0x0D, 0xA0, 0x41, 0xB0, 0x12, 0x00, 0x05, 0x43, 0x80, 0xA0, 0x02, + 0xB0, 0x32, 0x4C, 0x80, 0xA4, 0xCA, 0xB4, 0x31, 0x4D, 0x80, 0xA5, 0x87, 0xB5, 0x2E, 0x00, 0x05, + 0x44, 0x80, 0xA1, 0x02, 0xB1, 0x2A, 0x01, 0x68, 0x03, 0xA0, 0x02, 0xB0, 0x12, 0xA4, 0xCA, 0xB4, + 0x11, 0xA5, 0x87, 0xB5, 0x0E, 0x43, 0x80, 0xA0, 0x81, 0x00, 0x05, 0xB0, 0x35, 0x4C, 0x80, 0xA4, + 0x87, 0xB4, 0x32, 0x4D, 0x80, 0xA5, 0xCA, 0xB5, 0x31, 0x01, 0x79, 0x03, 0xA1, 0x02, 0xB1, 0x0A, + 0xA0, 0x81, 0xB0, 0x15, 0xA4, 0x87, 0xB4, 0x12, 0xA5, 0xCA, 0xB5, 0x11, 0x00, 0x05, 0x43, 0x80, + 0xA0, 0x57, 0xB0, 0x35, 0x4C, 0x80, 0xA4, 0x41, 0xB4, 0x32, 0x01, 0x2E, 0x05, 0xA0, 0x57, 0xB0, + 0x15, 0xA4, 0x41, 0xB4, 0x12, 0x85, 0x13, 0x84, 0x13, 0x83, 0x13, 0x82, 0x13, 0x00, 0x05, 0x81, + 0x13, 0x80, 0x13, 0xBD, 0xE0, 0x90, 0xFF, 0x91, 0xFF, 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, + 0xFF}; + +// clyde1_1.dro - Clyde's Adventure: Castle 1-3 +const unsigned char clyde1_1_dro[0x1DF3] = +{ 0x44, 0x42, 0x52, 0x41, 0x57, 0x4F, 0x50, 0x4C, 0x00, 0x00, 0x01, 0x00, 0xBC, 0xB7, 0x01, 0x00, + 0xDB, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x40, 0x4F, 0xC0, 0x06, + 0x60, 0xF1, 0x80, 0x51, 0x20, 0x01, 0xE0, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x41, 0x4F, 0xC1, 0x06, + 0x61, 0xF1, 0x81, 0x51, 0x21, 0x01, 0xE1, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x42, 0x4F, 0xC2, 0x06, + 0x62, 0xF1, 0x82, 0x51, 0x22, 0x01, 0xE2, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x43, 0x00, 0x63, 0xF1, + 0x83, 0x71, 0x23, 0x11, 0xE3, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x44, 0x00, 0x64, 0xF1, 0x84, 0x71, + 0x24, 0x11, 0xE4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x45, 0x00, 0x65, 0xF1, 0x85, 0x71, 0x25, 0x11, + 0xE5, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x48, 0x4F, 0xC3, 0x06, 0x68, 0xF1, 0x88, 0x51, 0x28, 0x01, + 0xE8, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x49, 0x4F, 0xC4, 0x06, 0x69, 0xF1, 0x89, 0x51, 0x29, 0x01, + 0xE9, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4A, 0x4F, 0xC5, 0x06, 0x6A, 0xF1, 0x8A, 0x51, 0x2A, 0x01, + 0xEA, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4B, 0x00, 0x6B, 0xF1, 0x8B, 0x71, 0x2B, 0x11, 0xEB, 0x00, + 0xBD, 0x00, 0x08, 0x00, 0x4C, 0x00, 0x6C, 0xF1, 0x8C, 0x71, 0x2C, 0x11, 0xEC, 0x00, 0x10, 0x05, + 0xBD, 0x00, 0x08, 0x00, 0x4D, 0x00, 0x6D, 0xF1, 0x8D, 0x71, 0x2D, 0x11, 0xED, 0x00, 0xBD, 0x00, + 0x08, 0x00, 0x50, 0x4F, 0xC6, 0x06, 0x70, 0xF1, 0x90, 0x51, 0x30, 0x01, 0xF0, 0x00, 0xBD, 0x00, + 0x08, 0x00, 0x51, 0x4F, 0xC7, 0x06, 0x71, 0xF1, 0x91, 0x51, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x00, + 0x08, 0x00, 0x52, 0x4F, 0xC8, 0x06, 0x72, 0xF1, 0x92, 0x51, 0x32, 0x01, 0xF2, 0x00, 0xBD, 0x00, + 0x08, 0x00, 0x53, 0x00, 0x73, 0xF1, 0x93, 0x71, 0x33, 0x11, 0xF3, 0x00, 0xBD, 0x00, 0x08, 0x00, + 0x54, 0x00, 0x74, 0xF1, 0x94, 0x71, 0x34, 0x11, 0xF4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x55, 0x00, + 0x75, 0xF1, 0x95, 0x71, 0x35, 0x11, 0xF5, 0x00, 0xBD, 0x00, 0xBD, 0x00, 0x08, 0x00, 0xA0, 0x00, + 0xB0, 0x00, 0xA1, 0x00, 0xB1, 0x00, 0xA2, 0x00, 0xB2, 0x00, 0xA3, 0x00, 0xB3, 0x00, 0xA4, 0x00, + 0xB4, 0x00, 0xA5, 0x00, 0xB5, 0x00, 0xA6, 0x00, 0xB6, 0x00, 0xA7, 0x00, 0xB7, 0x00, 0xA8, 0x00, + 0xB8, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE2, 0x00, 0xE3, 0x00, 0xE4, 0x00, 0xE5, 0x00, 0xE8, 0x00, + 0xE9, 0x00, 0xEA, 0x00, 0xEB, 0x00, 0xEC, 0x00, 0xED, 0x00, 0xF0, 0x00, 0xF1, 0x00, 0xF2, 0x00, + 0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0x04, 0x01, 0x20, 0x10, 0x31, 0xBD, 0x00, 0x08, 0x00, 0x40, + 0x4F, 0xC0, 0x06, 0x60, 0xF1, 0x80, 0x51, 0x20, 0x01, 0xE0, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x41, + 0x4F, 0xC1, 0x06, 0x61, 0xF1, 0x81, 0x51, 0x21, 0x01, 0xE1, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x42, + 0x4F, 0xC2, 0x06, 0x62, 0xF1, 0x82, 0x51, 0x22, 0x01, 0xE2, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x43, + 0x00, 0x63, 0xF1, 0x83, 0x71, 0x23, 0x11, 0xE3, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x44, 0x00, 0x64, + 0xF1, 0x84, 0x71, 0x24, 0x11, 0xE4, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x45, 0x00, 0x65, 0xF1, 0x85, + 0x71, 0x25, 0x11, 0xE5, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x48, 0x4F, 0xC3, 0x06, 0x68, 0xF1, 0x88, + 0x51, 0x28, 0x01, 0xE8, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x49, 0x4F, 0xC4, 0x06, 0x69, 0xF1, 0x89, + 0x51, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4A, 0x4F, 0xC5, 0x06, 0x6A, 0xF1, 0x8A, + 0x51, 0x2A, 0x01, 0xEA, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4B, 0x00, 0x6B, 0xF1, 0x8B, 0x71, 0x2B, + 0x11, 0x10, 0x05, 0xEB, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4C, 0x00, 0x6C, 0xF1, 0x8C, 0x71, 0x2C, + 0x11, 0xEC, 0x00, 0xBD, 0x00, 0x08, 0x00, 0x4D, 0x00, 0x6D, 0xF1, 0x8D, 0x71, 0x2D, 0x11, 0xED, + 0x00, 0xBD, 0x00, 0x08, 0x00, 0x50, 0x4F, 0xC6, 0x06, 0x70, 0xF1, 0x90, 0x51, 0x30, 0x01, 0xF0, + 0x00, 0xBD, 0x00, 0x08, 0x00, 0x51, 0x4F, 0xC7, 0x06, 0x71, 0xF1, 0x91, 0x51, 0x31, 0x01, 0xF1, + 0x00, 0xBD, 0x00, 0x08, 0x00, 0x52, 0x4F, 0xC8, 0x06, 0x72, 0xF1, 0x92, 0x51, 0x32, 0x01, 0xF2, + 0x00, 0xBD, 0x00, 0x08, 0x00, 0x53, 0x00, 0x73, 0xF1, 0x93, 0x71, 0x33, 0x11, 0xF3, 0x00, 0xBD, + 0x00, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF1, 0x94, 0x71, 0x34, 0x11, 0xF4, 0x00, 0xBD, 0x00, 0x08, + 0x00, 0x55, 0x00, 0x75, 0xF1, 0x95, 0x71, 0x35, 0x11, 0xF5, 0x00, 0xBD, 0x00, 0xBD, 0x00, 0x08, + 0x00, 0xA0, 0x00, 0xB0, 0x00, 0xA1, 0x00, 0xB1, 0x00, 0xA2, 0x00, 0xB2, 0x00, 0xA3, 0x00, 0xB3, + 0x00, 0xA4, 0x00, 0xB4, 0x00, 0xA5, 0x00, 0xB5, 0x00, 0xA6, 0x00, 0xB6, 0x00, 0xA7, 0x00, 0xB7, + 0x00, 0xA8, 0x00, 0xB8, 0x00, 0xE0, 0x00, 0xE1, 0x00, 0xE2, 0x00, 0xE3, 0x00, 0xE4, 0x00, 0xE5, + 0x00, 0xE8, 0x00, 0xE9, 0x00, 0xEA, 0x00, 0x10, 0x05, 0xEB, 0x00, 0xEC, 0x00, 0xED, 0x00, 0xF0, + 0x00, 0xF1, 0x00, 0xF2, 0x00, 0xF3, 0x00, 0xF4, 0x00, 0xF5, 0x00, 0x04, 0x01, 0x20, 0xA6, 0x00, + 0xB6, 0x00, 0xA7, 0x00, 0xB7, 0x00, 0xA8, 0x00, 0xB8, 0x00, 0xA8, 0x57, 0xB8, 0x09, 0xA7, 0x03, + 0xB7, 0x0A, 0xBD, 0x20, 0x08, 0x00, 0x40, 0x4F, 0xC0, 0x06, 0x60, 0xF1, 0x80, 0x51, 0x20, 0x01, + 0xE0, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x41, 0x4F, 0xC1, 0x06, 0x61, 0xF1, 0x81, 0x51, 0x21, 0x01, + 0xE1, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x42, 0x4F, 0xC2, 0x06, 0x62, 0xF1, 0x82, 0x51, 0x22, 0x01, + 0xE2, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x43, 0x00, 0x63, 0xF1, 0x83, 0x71, 0x23, 0x11, 0xE3, 0x00, + 0xBD, 0x20, 0x08, 0x00, 0x44, 0x00, 0x64, 0xF1, 0x84, 0x71, 0x24, 0x11, 0xE4, 0x00, 0xBD, 0x20, + 0x08, 0x00, 0x45, 0x00, 0x65, 0xF1, 0x85, 0x71, 0x25, 0x11, 0xE5, 0x00, 0xBD, 0x20, 0x08, 0x00, + 0x48, 0x4F, 0xC3, 0x06, 0x68, 0xF1, 0x88, 0x51, 0x28, 0x01, 0xE8, 0x00, 0xBD, 0x20, 0x08, 0x00, + 0x49, 0x4F, 0xC4, 0x06, 0x69, 0xF1, 0x89, 0x51, 0x29, 0x01, 0xE9, 0x00, 0xBD, 0x20, 0x08, 0x00, + 0x4A, 0x4F, 0xC5, 0x06, 0x6A, 0xF1, 0x8A, 0x51, 0x10, 0x05, 0x2A, 0x01, 0xEA, 0x00, 0xBD, 0x20, + 0x08, 0x00, 0x4B, 0x00, 0x6B, 0xF1, 0x8B, 0x71, 0x2B, 0x11, 0xEB, 0x00, 0xBD, 0x20, 0x08, 0x00, + 0x4C, 0x00, 0x6C, 0xF1, 0x8C, 0x71, 0x2C, 0x11, 0xEC, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4D, 0x00, + 0x6D, 0xF1, 0x8D, 0x71, 0x2D, 0x11, 0xED, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x50, 0x4F, 0xC6, 0x06, + 0x70, 0xF1, 0x90, 0x51, 0x30, 0x01, 0xF0, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x51, 0x4F, 0xC7, 0x06, + 0x71, 0xF1, 0x91, 0x51, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x52, 0x4F, 0xC8, 0x06, + 0x72, 0xF1, 0x92, 0x51, 0x32, 0x01, 0xF2, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x53, 0x00, 0x73, 0xF1, + 0x93, 0x71, 0x33, 0x11, 0xF3, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF1, 0x94, 0x71, + 0x34, 0x11, 0xF4, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x55, 0x00, 0x75, 0xF1, 0x95, 0x71, 0x35, 0x11, + 0xF5, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x50, 0x0B, 0xC6, 0x00, 0x70, 0xA8, 0x90, 0x4C, 0x30, 0x00, + 0xF0, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x53, 0x00, 0x10, 0x05, 0x73, 0xD6, 0x93, 0x4F, 0x33, 0x00, + 0xF3, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x54, 0x00, 0x74, 0xF8, 0x94, 0xB5, 0x34, 0x0C, 0xF4, 0x00, + 0xBD, 0x20, 0x08, 0x00, 0x52, 0x00, 0xC8, 0x01, 0x72, 0xF7, 0x92, 0xB5, 0x32, 0x04, 0xF2, 0x00, + 0xBD, 0x20, 0x08, 0x00, 0x55, 0x00, 0x75, 0xF5, 0x95, 0xB5, 0x35, 0x01, 0xF5, 0x00, 0xBD, 0x20, + 0x08, 0x00, 0x51, 0x00, 0xC7, 0x01, 0x71, 0xF7, 0x91, 0xB5, 0x31, 0x01, 0xF1, 0x00, 0xBD, 0x20, + 0xBD, 0x20, 0x08, 0x00, 0x41, 0x00, 0xC1, 0x00, 0x61, 0x70, 0x81, 0x04, 0x21, 0x00, 0xE1, 0x01, + 0xBD, 0x20, 0x08, 0x00, 0x44, 0x00, 0x64, 0x70, 0x84, 0x04, 0x24, 0x03, 0xE4, 0x00, 0xBD, 0x20, + 0x08, 0x00, 0x42, 0x00, 0xC2, 0x01, 0x62, 0x80, 0x82, 0x06, 0x22, 0x0F, 0xE2, 0x00, 0xBD, 0x20, + 0x08, 0x00, 0x45, 0x00, 0x65, 0x80, 0x85, 0x06, 0x25, 0x0F, 0xE5, 0x00, 0xBD, 0x20, 0x08, 0x00, + 0x48, 0x00, 0xC3, 0x06, 0x68, 0x10, 0x88, 0x02, 0x28, 0x10, 0xE8, 0x00, 0xBD, 0x20, 0x08, 0x00, + 0x4B, 0x00, 0x6B, 0x40, 0x8B, 0x02, 0x2B, 0x01, 0xEB, 0x00, 0x4C, 0x0B, 0x53, 0x0B, 0x54, 0x0B, + 0x01, 0x01, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x00, 0x69, 0xA0, 0x89, 0x04, 0x29, + 0x00, 0xE9, 0x00, 0xBD, 0x20, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xA0, 0x8C, 0x04, 0x2C, 0x00, 0xEC, + 0x00, 0x00, 0x54, 0xBD, 0x20, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x30, 0xA4, 0x57, 0xB4, 0x01, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, + 0x2E, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0x6D, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x01, 0x69, 0xD0, 0x89, 0x04, 0x29, + 0x01, 0xE9, 0x00, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xD0, 0x8C, 0x04, 0x2C, 0x02, 0xEC, + 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, + 0x2E, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0x6D, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x07, 0xC4, 0x05, 0x69, 0x80, 0x89, 0x04, 0x29, + 0x01, 0xE9, 0x01, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0x60, 0x8C, 0x02, 0x2C, 0x01, 0xEC, + 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0x81, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, + 0x2E, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0x6D, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x00, 0x69, 0xA0, 0x89, 0x04, 0x29, + 0x00, 0xE9, 0x00, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xA0, 0x8C, 0x04, 0x2C, 0x00, 0xEC, + 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0x81, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0x8A, 0xB4, 0x2E, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, 0x8A, 0xB4, + 0x2E, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x8A, 0xB4, 0x0E, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x03, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x43, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, + 0x43, 0xB4, 0x32, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x43, 0xB4, 0x12, 0xA4, 0x03, 0xB4, + 0x32, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x03, 0xB4, 0x12, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x57, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, + 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, 0x81, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x81, 0xB4, 0x11, 0xA4, + 0xCB, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, 0xCB, 0xB4, + 0x31, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xCB, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDB, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0xB1, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31, 0x00, 0xDA, 0xBD, 0x30, 0xBD, 0x38, 0xA4, 0xB1, 0xB4, 0x11, 0xA4, 0x57, 0xB4, + 0x31, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDA, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDA, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0xDB, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0x00, 0xDB, 0xBD, 0x30, 0xBD, + 0x38, 0x00, 0x6C, 0xBD, 0x38, 0x08, 0x00, 0x49, 0x00, 0xC4, 0x01, 0x69, 0xD0, 0x89, 0x04, 0x29, + 0x01, 0xE9, 0x00, 0xBD, 0x38, 0x08, 0x00, 0x4C, 0x0B, 0x6C, 0xD0, 0x8C, 0x04, 0x2C, 0x02, 0xEC, + 0x00, 0x00, 0x6D, 0xBD, 0x28, 0xA6, 0x57, 0xB6, 0x05, 0xBD, 0x38, 0xA4, 0x57, 0xB4, 0x11, 0xA4, + 0xB1, 0xB4, 0x31}; diff --git a/Frameworks/GME/vgmplay/XMasFiles/clyde1_1.dro b/Frameworks/GME/vgmplay/XMasFiles/clyde1_1.dro new file mode 100644 index 0000000000000000000000000000000000000000..7e3ab4d97584761a26da0ef21d39cbde9e73a49a GIT binary patch literal 7667 zcmd^DTWpj?6h714u9q6=`fgGX5)dTimh_QQOh9Y4+t&0IP!p?=bV1)+goK+3*LTzk zJc@3C2R9L{K*9@8KFAB#1Sll@T!N3DGiT?_%s;dHcSVdB5|Vy1|ID24obQ|S|GRe0 zVDCt2pdUcNkEaz}c^&k>8oUUrOFtDhB;(~SbrlHrmd+M7CKKhk>YB!DO6LljlI`Vr z>bk~#@ImrL<&EMEjn=}4$&SkW;!Ta-hL4h+l?BCH8m%k+T=+QIRbHrWYrMWRT^LPv zmlvr!8oyKerSM6zr~Ib6tMR+=X|lJnxHzLxKWt9+RhAU*L1#O~^&V_VzO1}eysu{m zO6Ln>$^LS;dZ2N+bfHj54wRRvMB~BI#lqI)V0pQEsPPbNOAb|56d!3c44);3D=UkS zHF_UDPmWYp6`vrYla9i7@D-edui<<627Z7^sKU1}1;^kN9Ea0z0?xoyxCYnZ2Hb>O za2xKxU6_G;a33B(0uSL4JccKbS6!XU{+TK6Y)>co-;iR8I+A3!E+e6D_=oZAR2^=YU|U3 zQ_J({AjG)WlOt2B6uJaa87`>G(*sl5Z(TIf3uj^D*hGFVTvLQGe`7oH^K*DJAX)?G z)TZ(6g?V%E7D0^p9N$@3Fb8i6MC$-=iq2ha3p??qK#bX1y|-<#nt}d~p^o7WrS$N6 zn1<1#yYq|CaP(Mz$L6E^@=H(?FkI_8Hgc-H);&2=Rg)*H%iyn!^9+*nWS-BZc`qIvi68F-MsL?oAOo7` z^@dL@E^l9s$L_=H9nA4k9^!m#yv(kihv&d&#N}Jh;|Q|$$8|jEdIWrEU6{X1lD|th zPe}5ID4k!14hLt%t?SPfAYB^$*!XB9jNj^m;wQ#jPf|N&6*Qk97U?x-ft25YmhBi{&$0zu5do;!Veq zrK8Mw(HGY@cz#Q*2ir7EtI^BYKj;M%G4_wk*gx8^e{3=Lyv|57KQJ%j2gytQ+>AJy z=*Q}VZv%7x7)4oqk$&Xd27Zt@&>gY+OV-zY(uhy$IeRYi6+ZSn6Yt;0C!#O$gTz&@ zPZl53KV+TGU)KHW>xFM0Q@?os$cUqfKT`LSRG?YDv;53A0QueY51)V7KO}zLQGEX1 z{pRSz(=F>Rt*10^hn>l1TwgZcaNavt{9^B?@c#Sj_(+}{pY?hE4dbQx=gG(S3+Ey1 zA1R&1{nMm>upS%t51W6^wyAIE7m8Q%5sxF#Q7Z4je5?C$|9D}#eRN#+4^{6U8}_v= zQPKWk`FZ-|__YdYyu~=g53)|)KbrZU6=&Wry>rD6y#D|5K4tkv^d*`XXANCQAQuE7UM^{pThp<#~=0&$(!ydVgIn}v%WvrN4fg6(Bz~?jN>Zv|je-d`9?0<9GP6KFE!Io^zNx{lm_i{$cqOKbcSIOLV~F&T}bV&bqk1_&lpNlW#j>^5NxM{Nn3N;*H$5 z&G^W=XZC+7(v5 literal 0 HcmV?d00001 diff --git a/Frameworks/GME/vgmplay/XMasFiles/lem_xmas_001_jb.dro b/Frameworks/GME/vgmplay/XMasFiles/lem_xmas_001_jb.dro new file mode 100644 index 0000000000000000000000000000000000000000..1e1e432e1b24b67319276485efbdfbd36f047e3d GIT binary patch literal 12544 zcmc&)TWp-g75?pH|8@SgH(-M?4kGbw7i{XZySAJ5sen<|s$t6%=x)PqlL{{5bZ*1qzO z_dI<0tN(oOqo4TNzuxzePk#O1wX>i4#$Ns0r@#5e%tt@-t^dqE_Vl;k{LYVFeE!G3 z-roI9`zJ5${Pefq{n_un_w(O>{}+Gw!7u;#VE6e$4{tsG@kjh8itA&~l+JWL*}YL) zpBKIBm+-SG`qp>xb6xap^o#cTvhX%e;`dwl-OKnXY}D|#G5l$sR;<5_Py6s`FIM)g z&)~<$pRZ%rv>0YfoA~=Bxt{iIl&^G)D~~_>$lMdB&m4Nvd&YZk^!$kM)^|5X#TuQv z?r)4?57{D{^5kBeToK3A$UP+F z!H0i4um^Ob^5SkZ$#-RIxLqR(fJs5!*~Q};SrZ;`(pWUituJixy(J!7tts;faGqB< zml#QDwXHmFhXcY3>#ZqiRa|S8#N}{+JwLfNNT`T5Y|qhp`ULitqp8+Wa{}L`guAfk zPAv5)xUoJ%;fy#+1_+Ni0N)fw_`ULtcdhvLZ5Tgq@#Sc0c^)WXmtxJesKiLJhpj0q z3OMNk@)B03mKL|7>he}|3b@2tDYm0x zvjiO_wE@EyeXQ}D$PD7JI>D@fdLe@=@N^O?Hmzc{U@{@{n?b9b(w>FlV}tkiJbl~p zk*=Yxi>1QoEu+47s|TekgwVp?v&U+ELw5`<9SN3hA1?*N0kUcG=Cdbi{X-)|myR4= zx^rB3HwDLo=H$fLnObFNc4+m;*ivnLJg5^3;ji-LfiU1ao-#g#CEU!;^#*>k8=CA3 z$cxln7L?Yt!$9mHbE}NTPFMt%Rj6H2?uz+^b3;B={pR#O=Ptn0`EW7{z{<%cZq_PE z#&U(RFg0j~f%Y=?w*f0#d^=VQ=2MA1v9eElqIT2$H~HjEI+@~)vkMeYeH(+`sd9|1 z$udHWqYvJc@iT4Khh^mt&R4aP@wfg4oFY_nu4|>`Wt?7)xzva)Dg5b1@vTdQIS>Oo znk8aktL#MoG(|s!2BL3aXvdynkQMueS#K~2gBW$%r=o4&(N?4s$E;XG3*BgUx-}w9 zITGX7A>Ps#T8WrQ*3akspB|brigim%!6VnVPuEq9%W~*QC*B$K5128Zd8i1J=LyNf6S|S<6iwR_cUXS z{_~MfiVF3vG|Qyd6R*v@S@CC!)$Y}!HD7pjuXP9rjYb`-B>-x4Oaf zG3zGiSRx;IR%9mFCkU_R>l4@?*eBhFPjymg*MDPOt)+G3yPxzOQHS^1a_oD&zLW2$ zd1G-D;Cs?**LLhR?kBw_|I?lWNrepW*p|41ZFA&{>>q4ojQ5)JsDM6B;LbeDe_$D( zVf+U+tUnX43D1tK#zy)tJ9)l=?B#u`!R{?^Z{M6i`=EHIqv9gEQLN2ik3K`+K&?mR z#_9xfGQDrWI-#j}9Ou)yX$x({pp7Ig7o zyhleu>YubuwsFF!Lch+O=e#2S8PEN=&;CH$>HU(rgUivF|HU|WX$Ly2gXAtSP2Iw^ zuWmt^Nc{%Cz@lQ3KQ?=0m7t!EP}TjM;gIl*25AJPhcOoDt6pFu_jYaa&biOpaN-SO@ht8Ygii`8IuovBzS+Qv(i?x1y4}cs+&pH zUZ9EY%|utRKUo1R{9W{OZov3w*GQW`++mTLXWl~BNvX`T2BUrz=?;W1^PAO+-f#JJ zQ?aSR(*aCWvQA5Oqr^Vs166(M$*Zs)EN?>Dt)ADQ=H7{F4l{8Id*Qp@o9Ra7TT?0$&gfW!ql%4jBiC1nfuWsPU@9M zk)U}&QDzY_Q=$=75sRIun8ZtV)iI0OSl3P6cvPKuJ*h=@B^KKh2Z~41qO9@i)MBd} zxbMKDYJPrFB~o1>&nU1q;BHtkKCgPIJv_ByJp=`Gd#Ia|+l_v+M9IoFD347$VAtdZHSaxQeQ};mBPfDXS#FJezDEjib-@6Ghuq!#Z{5C8p=jf z>So#1!OW~A&BnwUFFx!bEwq(Zp1L2$>xhowxH+4uft-c&_d0S4mbj6$P0ZpRn5W}B zUnf=O_eS=MOfyF9qN#bnuNp_o^{Yl_mBVg{j-x{yEEdT4y=*@&)&R1w2pN^eUK&jf)h3o0ryF8CE zbtE~D>6oV#Fe-B%FRMIovuCQ%ce*c4Ot`Kca3YYN$eSJ_jtqT|@PD1ha6dp3c}!$b zz3?_f#W>&%A_gXT;EM<8ICNe;izfUu>h1h?Z=;h4h zU6v}9ac9|>$RFqhM>&(4$-DO1qir>d=Sd9hp=t{)wYqgvxl~spt8KI{FnGNnkGx3j9{< zkiqOhhl#vI9=Vn5DD`~3z>%^}F!ORk$H`9J& zUGZP(Io^1_?ho{fz_8PqsKyl|t)BTAe2_j^=|6vyASZT`^;z#d%WO_vVxIq(x3baU@}X;%Aq5Y&CWzVzj|pM0XnoJ^BYZB8{G<^tC#dXIq#8t zz&ER?bGoD9?ltwDY$5~n_nC18klnvs=QDHIzI7sp?J8;Cfz%v%mNS*N-`%PP%ZWVI zC;G-Ip2(xGjv4D_9HGE5M1O-FIWu|v`qYXfcbJ(xCTsCTK3zBC8&RjA)jeY(uTj(e zo#YmGUCul{zdOZ_M&~$?O-vVKG*t z>P>Q~$BDf?a=C}|-TU^*%2>EBM;GJhB1d29H|5YiIk7IM*5$pnd^je*v{aXlOtxfy zOO8&+@rk(R0lg#EJgj%5B;BtL%1TG?c%b|F!3?wbi>G9Dk6gN6R;P8T`=vKS;ms7i zymWt)o7Vwd^U`cMbBSCEV{SFn|yEs}8$+dE$T=b&|szj2!)WdW|Bzgg%3qcvY_lNRO+%d$3I$v8pqn~vsa z1GgY~SA_>BG3qEIdHRaD>(8+W62BR=s1iBdar7y&Gj44(#1H znw2t^`4@{4P=K!mh4QEC=-D4t9)Jyj<`vtXrKyJ7u{cBZsI(6I>33N3G?8d#FP z(*~{P4#>ZWJ1WFlF&)%Y(~am3ny%&!_$Vv78r+d|e*@itPVF$QI+Qz@3%RXCC$_h3 z?L^=ajf5br=SQ4Wc}RswkapUAL?;GmH;hJ2v#N1RkXkS#RYzPCZp!kAiR9J6bX6y; znJUhxgBUi&*I>{Q{kVCAqiW%SEEzP)PLKw7Fd$)*)E%UiW_N^I+7$658%& zVjymmx`R44yQ89G5ngTSW*}lCZU$0?TGxP7+N6!@J0&-;8``KUZm^8WjZ(FL+D0+( zYHbvpl9hKR9*8O39j&)f#1@|{8ydri=W(>m(Tu3HjiSM8ZB(wo(a!hV3?!1&)}%&O zPGHp78Q?S2W?%q;v|6Swiu~)vh}&7T227N$nQIO2UrbNiC|aZ6n$d$gn(n`i6tRi$ zThK=BkOqVhom0&=v{CSE(tsejY%^*HHXtTj;!ReDrUil28YKi+ss*{gMv-pmI$1D? z=?H26OvLutqG6(S&zd;M3u&UxH7{V$=vB;WzG30>#Hq zW#XkQ7OYgV88{Nkr7RYB+1fW{$y|dsuo!FpLKe${`jj1Lw;DU(*4G7#LHJA-EADp^ zyeiqvFcwZJ6|OPlM6^(B!`Ku#8J5k+$wqQufw6_inb?63qfHBFw22m*<39m>8?-PB zr&=Iv5-rTyg)FvBT3AJqvRLT0Oh=>icE~#YWy{U|CIG+YC&AI&K1NL^ETP(KZ7KeL01z(U4D;xQ`S>^|1$0 z<4Y++yCW+ao83XG-C>)7wD3SQiy9Sdj5q8)11H4yB8=uCUhv5OZ8I>0htJ~Z4pwwIAYiu5a7Vx^ zo07Vt5^{xmM7#OCz93BqT>5@mB#7>k%rEJ?g*V8r<8pOezS||YI(@hBW|^DNx;m4> z0D~ZrX?bNyUR{z;+VbfQ^5?kxH7-Bwk~^K;)sn|$VMbrNi?8&A_Y(D?Mp>%* zIst)-%?-Q+3+MJ^P2YOudoNl%R8y5Fd8Gzwynyvgt_}5d8hubm?`NY571aAe7!xdk zbacB0_+FPia;3wZs!rLci%F6#Kbb|1X12;zBxU5aC>{elJo<@IK~&UWE0W5q9}K0g zRP|Xh<=YuPRg?(H8*Ogn7~*y)35m>mv++lfnYYVF=2%D!ZZ3kF8OR*ru8PbdiVV&A z!CgT{Y&9}hMTH+D&P3=)NhBnZ>0%s_8+%nC+De2hDJ-}0zQeLZH1CXN@qdQQRWu9E z)go#Ok+}>pA~F*(A|oIIBVuU1)PT&@DLWjQLsSffdx4m0$ec?Y=3mWC1^^I4k+~88 z8g(d^f`q3Zou8R>_?w=S`1ib9!r$^tx(D6E`2SgV&b{X5T^Gm4+!=g$^t`*^UU#Q( z%%3OR0%n}Wyw}{KdqdAX?T+KRY5aTAJ?HLmbM7hp=FgK@@m9K!Jn zILkXMxOXsb7Qgw;VI|7&Ccbmd0;HLAr*V8v<)l1wkn<@$yQK5)#_<`9FXH2`e+$x` zcdzKR=OE)OM&EY#x?T9p;=XT$>v*S0H#Jc2DO`0@<$Mt)kOBju4|l=N z&O-(Y5|aceWi_4zsa=#@-*_atAoRU33X3!gl0_QQ8HpCU@caYQ&pE%}^}S<;q(#jl zvhO|b^F7~lJO>XvS8LXvIed4oHTxeh{L9-Nx%{{C+wz6{xAPC?zmxxNeuv#@ciD%- z-Qk|_5&Nhu+3$ysh2`)EwqmRHhvARH*>HcoRzFaGqW)z4srobZXY0?^pC8SQj*O08 zKel#!?ZoJX(Tk(`wU^djUi-=Dr=y>Ze!kXP`^8$j(P^A)oNAqJooT(+c)jsPB zTW_`AZY(s;HqN!qx87+j=6ao_?uU1xy*ushuvVWN9lPFXoNj$)Ot519U=EsIr$4*P z?gks%{QAkpnbuviYW`>rnWe4?;;Q+0F&91fxLK@4d(AEWm(RIqul>lPynU$~ zPkDi1G;0D(Hv6+E*HHc2;xO9o#8Gu-kP_7EB?wEg=-NbUI9liSYh zy9+F|^=(=+^9WFV0$7|F&0l{FaD8sRFn=;XHW!B`xKcKM)!f_Sn|^xd!>D3z^&;}B z#l?%mD+M|iYKVk&unk};huSo6#snCOF!QMoHHVpj=1bfWW>ydm5iKHC;7Mo#-rx;0 zO*KOtaTzlm%zTmtAknB->MGGRF*A${h!uF^qH$)r{TZT(;<855#LP8sMxuE)2hxhX zE?jJ)N+&b|YDxr=MnIKRb*PC57F<1+v-Mc{pX%{RrXE+b^|*EadiX^CkM&4ECH45# z=JlXgkH(MnpS7V9H^JjCoeJL7L4uVfcQ3~CR%l5ZcTFDFgAZTzGlmjnZ4Cf0=)qznG^9kE&x4@-(+WQ$bS^*QBB@X){v8K?Rp06_9m~di>go2oGz^ zGg6OSe=8r0Jm@aZXor!aGebK3$`%`KrP)JLv%Cr_> zWT8owhhm+C142_O>4PFmfl?DCoFJ|=FC;r2L?rRz=}AndL6|_n;%$9o)j4*^U*HIb zNESqts5qQDi|mgy?wevb6Y*;HUHV)#iQX{|VTy4P6^2QocRfPn0+qZhTT&O1w30`N zLG}rgmStw>B09N2m0k>K{Z-4CDL#fiGuD#JDwQEOfrppFwXk-88LEqxU`!Ub&)lqP ziE%_O{}WPg)zG2w%bOBYgn zJNtA7j3OTAk9V!D)Zwz18&uw3>Q`Mpca8ljyg4Bo8F|kr8Bd#NtR!o znbiD-nR)-enVB(B`-&{ze`YShTZ*a4xL2e+n?$TUkUrOH-fL#$;-tzDte+Vz{Jmy| z{V+K*baUg(IJ{vuof&w4VrHOJwpV6m{+^kco8y_0vsVx&Gb4AbOci$tn%S9w%O++9 zl(GINC&nFGUpq0EQ;bP}6BhGaoI%r9C+1ofRcQyt{OcH6ENwn9SoF96V}49M#^lPfZn4M=17Fb*hIJ2F#R;3B$E7O6I9??LEmx!jk*Q9?<_wT)Gb4v2+M&#` zt2ox?<=D38axQOkYYEO5joy4;k0GTVAd&>k&jKM4j~7QdnNV4zx^+2VIe5Hl)FsGH zT#&oft5PGinoBM)bAzrF@xx8Vjm-hWXWq*17xzNGV24Z0KRBwoq(Dqmo4k5QIkDz- zZNazdz}?bZ_HupbnJsjPp|lS2#Z^fX(jRg~Iq-cwlRL%%gF8k7qi!>D?8)#6IJjpd zDyb-DJ{mp;ZdRE$g7vv%rC<}kO+>&M zN@&{6gH%i4F2k78i`$e=Ubav6%{>l3M-P;7e9oTO@OuV>!g#_4-SN@=>J|xH790=t v_|A|XOt+^$yz}<@!ur|ubL;2V-&y|;OV}ZK literal 0 HcmV?d00001 diff --git a/Frameworks/GME/vgmplay/chips/2151intf.c b/Frameworks/GME/vgmplay/chips/2151intf.c new file mode 100644 index 000000000..71b4a395c --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2151intf.c @@ -0,0 +1,195 @@ +/*************************************************************************** + + 2151intf.c + + Support interface YM2151(OPM) + +***************************************************************************/ + +#include +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "fm.h" +#include "2151intf.h" +#include "ym2151.h" + + +#define NULL ((void *)0) + +typedef struct _ym2151_state ym2151_state; +struct _ym2151_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + UINT8 lastreg; + //const ym2151_interface *intf; +}; + + +/*INLINE ym2151_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM2151); + return (ym2151_state *)device->token; +}*/ + +//static STREAM_UPDATE( ym2151_update ) +void ym2151_update(void *param, stream_sample_t **outputs, int samples) +{ + ym2151_state *info = (ym2151_state *)param; + ym2151_update_one(info->chip, outputs, samples); + //YM2151UpdateOne(0x00, outputs, samples); +} + + +//static STATE_POSTLOAD( ym2151intf_postload ) +/*static void ym2151intf_postload(UINT8 ChipID) +{ + //ym2151_state *info = (ym2151_state *)param; + ym2151_state *info = &YM2151Data[ChipID]; + ym2151_postload(info->chip); +}*/ + + +//static DEVICE_START( ym2151 ) +int device_start_ym2151(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //static const ym2151_interface dummy = { 0 }; + + //ym2151_state *info = get_safe_token(device); + ym2151_state *info; + int rate; + + info = (ym2151_state *) calloc(1, sizeof(ym2151_state)); + *_info = (void *) info; + + rate = clock/64; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + //info->intf = device->static_config ? (const ym2151_interface *)device->static_config : &dummy; + //info->intf = &dummy; + + /* stream setup */ + //info->stream = stream_create(device,0,2,rate,info,ym2151_update); + + info->chip = ym2151_init(clock,rate); + //assert_always(info->chip != NULL, "Error creating YM2151 chip"); + + //state_save_register_postload(device->machine, ym2151intf_postload, info); + + //ym2151_set_irq_handler(info->chip,info->intf->irqhandler); + //ym2151_set_port_write_handler(info->chip,info->intf->portwritehandler); + + return rate; +} + + +//static DEVICE_STOP( ym2151 ) +void device_stop_ym2151(void *_info) +{ + //ym2151_state *info = get_safe_token(device); + ym2151_state *info = (ym2151_state *)_info; + ym2151_shutdown(info->chip); + //YM2151Shutdown(); + free(info); +} + +//static DEVICE_RESET( ym2151 ) +void device_reset_ym2151(void *_info) +{ + //ym2151_state *info = get_safe_token(device); + ym2151_state *info = (ym2151_state *)_info; + ym2151_reset_chip(info->chip); + //YM2151ResetChip(0x00); +} + + +//READ8_DEVICE_HANDLER( ym2151_r ) +UINT8 ym2151_r(void *_info, offs_t offset) +{ + //ym2151_state *token = get_safe_token(device); + ym2151_state *token = (ym2151_state *)_info; + + if (offset & 1) + { + //stream_update(token->stream); + return ym2151_read_status(token->chip); + //return YM2151ReadStatus(0x00); + } + else + return 0xff; /* confirmed on a real YM2151 */ +} + +//WRITE8_DEVICE_HANDLER( ym2151_w ) +void ym2151_w(void *_info, offs_t offset, UINT8 data) +{ + //ym2151_state *token = get_safe_token(device); + ym2151_state *token = (ym2151_state *)_info; + + if (offset & 1) + { + //stream_update(token->stream); + ym2151_write_reg(token->chip, token->lastreg, data); + //YM2151WriteReg(0x00, token->lastreg, data); + } + else + token->lastreg = data; +} + + +/*READ8_DEVICE_HANDLER( ym2151_status_port_r ) { return ym2151_r(device, 1); } + +WRITE8_DEVICE_HANDLER( ym2151_register_port_w ) { ym2151_w(device, 0, data); } +WRITE8_DEVICE_HANDLER( ym2151_data_port_w ) { ym2151_w(device, 1, data); }*/ +UINT8 ym2151_status_port_r(void *info, offs_t offset) +{ + return ym2151_r(info, 1); +} + +void ym2151_register_port_w(void *info, offs_t offset, UINT8 data) +{ + ym2151_w(info, 0, data); +} +void ym2151_data_port_w(void *info, offs_t offset, UINT8 data) +{ + ym2151_w(info, 1, data); +} + + +void ym2151_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ym2151_state *info = (ym2151_state *)_info; + ym2151_set_mutemask(info->chip, MuteMask); +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym2151 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2151_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2151 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2151 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2151 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2151"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/2151intf.h b/Frameworks/GME/vgmplay/chips/2151intf.h new file mode 100644 index 000000000..b4ffdbf18 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2151intf.h @@ -0,0 +1,33 @@ +#pragma once + +/*typedef struct _ym2151_interface ym2151_interface; +struct _ym2151_interface +{ + //void (*irqhandler)(const device_config *device, int irq); + void (*irqhandler)(int irq); + write8_device_func portwritehandler; +};*/ + +/*READ8_DEVICE_HANDLER( ym2151_r ); +WRITE8_DEVICE_HANDLER( ym2151_w ); + +READ8_DEVICE_HANDLER( ym2151_status_port_r ); +WRITE8_DEVICE_HANDLER( ym2151_register_port_w ); +WRITE8_DEVICE_HANDLER( ym2151_data_port_w ); + +DEVICE_GET_INFO( ym2151 ); +#define SOUND_YM2151 DEVICE_GET_INFO_NAME( ym2151 )*/ +void ym2151_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ym2151(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym2151(void *chip); +void device_reset_ym2151(void *chip); + +UINT8 ym2151_r(void *chip, offs_t offset); +void ym2151_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym2151_status_port_r(void *chip, offs_t offset); +void ym2151_register_port_w(void *chip, offs_t offset, UINT8 data); +void ym2151_data_port_w(void *chip, offs_t offset, UINT8 data); + +void ym2151_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/2203intf.c b/Frameworks/GME/vgmplay/chips/2203intf.c new file mode 100644 index 000000000..f5c2143b0 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2203intf.c @@ -0,0 +1,462 @@ +#include +#include // for memset +#include // for free +#include // for NULL +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "2203intf.h" +#include "fm.h" + + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // AY8910 core from MAME +#endif +#define EC_EMU2149 0x00 + +typedef struct _ym2203_state ym2203_state; +struct _ym2203_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + void * psg; + ym2203_interface intf; + int AY_EMU_CORE; + //const device_config *device; +}; + +#define CHTYPE_YM2203 0x20 + + +/*INLINE ym2203_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM2203); + return (ym2203_state *)device->token; +}*/ + + +static void psg_set_clock(void *param, int clock) +{ + ym2203_state *info = (ym2203_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_clock_ym(info->psg, clock); + break; +#endif + case EC_EMU2149: + PSG_set_clock((PSG*)info->psg, clock); + break; + } + } +} + +static void psg_write(void *param, int address, int data) +{ + ym2203_state *info = (ym2203_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_write_ym(info->psg, address, data); + break; +#endif + case EC_EMU2149: + PSG_writeIO((PSG*)info->psg, address, data); + break; + } + } +} + +static int psg_read(void *param) +{ + ym2203_state *info = (ym2203_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + return ay8910_read_ym(info->psg); +#endif + case EC_EMU2149: + return PSG_readIO((PSG*)info->psg); + } + } + return 0x00; +} + +static void psg_reset(void *param) +{ + ym2203_state *info = (ym2203_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_reset_ym(info->psg); + break; +#endif + case EC_EMU2149: + PSG_reset((PSG*)info->psg); + break; + } + } +} + +static const ssg_callbacks psgintf = +{ + psg_set_clock, + psg_write, + psg_read, + psg_reset +}; + +/* IRQ Handler */ +/*static void IRQHandler(void *param,int irq) +{ + ym2203_state *info = (ym2203_state *)param; + if (info->intf.handler != NULL) + //(*info->intf->handler)(info->device, irq); + (*info->intf.handler)(irq); +}*/ + +/* Timer overflow callback from timer.c */ +/*static TIMER_CALLBACK( timer_callback_2203_0 ) +{ + ym2203_state *info = (ym2203_state *)ptr; + ym2203_timer_over(info->chip,0); +} + +static TIMER_CALLBACK( timer_callback_2203_1 ) +{ + ym2203_state *info = (ym2203_state *)ptr; + ym2203_timer_over(info->chip,1); +}*/ + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +/* update request from fm.c */ +void ym2203_update_request(void *param) +{ + ym2203_state *info = (ym2203_state *)param; + //stream_update(info->stream); + + ym2203_update_one(info->chip, DUMMYBUF, 0); + // We really don't need this. + /*if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_update_one(info->psg, DUMMYBUF, 0); + break; +#endif + case EC_EMU2149: + PSG_calc_stereo((PSG*)info->psg, DUMMYBUF, 0); + break; + } + }*/ +} + + +/*static void timer_handler(void *param,int c,int count,int clock) +{ + ym2203_state *info = (ym2203_state *)param; + if( count == 0 ) + { // Reset FM Timer + //timer_enable(info->timer[c], 0); + } + else + { // Start FM Timer + //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); + //if (!timer_enable(info->timer[c], 1)) + // timer_adjust_oneshot(info->timer[c], period, 0); + } +}*/ + +//static STREAM_UPDATE( ym2203_stream_update ) +void ym2203_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + //ym2203_state *info = (ym2203_state *)param; + ym2203_state *info = (ym2203_state *)_info; + ym2203_update_one(info->chip, outputs, samples); +} + +void ym2203_stream_update_ay(void *_info, stream_sample_t **outputs, int samples) +{ + //ym2203_state *info = (ym2203_state *)param; + ym2203_state *info = (ym2203_state *)_info; + + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_update_one(info->psg, outputs, samples); + break; +#endif + case EC_EMU2149: + PSG_calc_stereo((PSG*)info->psg, outputs, samples); + break; + } + } + else + { + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + } +} + + +//static STATE_POSTLOAD( ym2203_intf_postload ) +/*static void ym2203_intf_postload(UINT8 ChipID) +{ + //ym2203_state *info = (ym2203_state *)param; + ym2203_state *info = &YM2203Data[ChipID]; + ym2203_postload(info->chip); +}*/ + + +//static DEVICE_START( ym2203 ) +int device_start_ym2203(void **_info, int AY_EMU_CORE, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + static const ym2203_interface generic_2203 = + { + { + AY8910_LEGACY_OUTPUT, + AY8910_DEFAULT_LOADS + //DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL + }, + NULL + }; + //const ym2203_interface *intf = device->static_config ? (const ym2203_interface *)device->static_config : &generic_2203; + ym2203_interface* intf; + //ym2203_state *info = get_safe_token(device); + ym2203_state *info; + int rate; + int ay_clock; + +#ifdef ENABLE_ALL_CORES + if (AY_EMU_CORE >= 0x02) + AY_EMU_CORE = EC_EMU2149; +#else + AY_EMU_CORE = EC_EMU2149; +#endif + + info = (ym2203_state *) calloc(1, sizeof(ym2203_state)); + *_info = (void *)info; + + info->AY_EMU_CORE = AY_EMU_CORE; + rate = clock/72; /* ??? */ + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + + info->intf = generic_2203; + intf = &info->intf; + if (AYFlags) + intf->ay8910_intf.flags = AYFlags; + //info->device = device; + //info->psg = ay8910_start_ym(NULL, SOUND_YM2203, device, device->clock, &intf->ay8910_intf); + if (! AYDisable) + { + ay_clock = clock / 2; + *AYrate = ay_clock / 8; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + *AYrate = CHIP_SAMPLE_RATE; + + switch(AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + // fits in the most common cases + // TODO: remove after being able to change the resampler's sampling rate + info->psg = ay8910_start_ym(NULL, CHTYPE_YM2203, ay_clock, &intf->ay8910_intf); + break; +#endif + case EC_EMU2149: + info->psg = PSG_new(ay_clock, *AYrate); + if (info->psg == NULL) + return 0; + PSG_setVolumeMode((PSG*)info->psg, 1); // YM2149 volume mode + break; + } + } + else + { + info->psg = NULL; + *AYrate = 0; + } + //assert_always(info->psg != NULL, "Error creating YM2203/AY8910 chip"); + + /* Timer Handler set */ + //info->timer[0] = timer_alloc(device->machine, timer_callback_2203_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_2203_1, info); + + /* stream system initialize */ + //info->stream = stream_create(device,0,1,rate,info,ym2203_stream_update); + + /* Initialize FM emurator */ + //info->chip = ym2203_init(info,clock,rate,timer_handler,IRQHandler,&psgintf); + info->chip = ym2203_init(info,clock,rate,NULL,NULL,&psgintf); + //assert_always(info->chip != NULL, "Error creating YM2203 chip"); + + //state_save_register_postload(device->machine, ym2203_intf_postload, info); + + return rate; +} + +//static DEVICE_STOP( ym2203 ) +void device_stop_ym2203(void *_info) +{ + //ym2203_state *info = get_safe_token(device); + ym2203_state *info = (ym2203_state *)_info; + ym2203_shutdown(info->chip); + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_stop_ym(info->psg); + break; +#endif + case EC_EMU2149: + PSG_delete((PSG*)info->psg); + break; + } + info->psg = NULL; + } + free(info); +} + +//static DEVICE_RESET( ym2203 ) +void device_reset_ym2203(void *_info) +{ + //ym2203_state *info = get_safe_token(device); + ym2203_state *info = (ym2203_state *)_info; + ym2203_reset_chip(info->chip); // also resets the AY clock + //psg_reset(info); // already done as a callback in ym2203_reset_chip +} + + + +//READ8_DEVICE_HANDLER( ym2203_r ) +UINT8 ym2203_r(void *_info, offs_t offset) +{ + //ym2203_state *info = get_safe_token(device); + ym2203_state *info = (ym2203_state *)_info; + return ym2203_read(info->chip, offset & 1); +} + +//WRITE8_DEVICE_HANDLER( ym2203_w ) +void ym2203_w(void *_info, offs_t offset, UINT8 data) +{ + //ym2203_state *info = get_safe_token(device); + ym2203_state *info = (ym2203_state *)_info; + ym2203_write(info->chip, offset & 1, data); +} + + +//READ8_DEVICE_HANDLER( ym2203_status_port_r ) +UINT8 ym2203_status_port_r(void *info, offs_t offset) +{ + return ym2203_r(info, 0); +} +//READ8_DEVICE_HANDLER( ym2203_read_port_r ) +UINT8 ym2203_read_port_r(void *info, offs_t offset) +{ + return ym2203_r(info, 1); +} +//WRITE8_DEVICE_HANDLER( ym2203_control_port_w ) +void ym2203_control_port_w(void *info, offs_t offset, UINT8 data) +{ + ym2203_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ym2203_write_port_w ) +void ym2203_write_port_w(void *info, offs_t offset, UINT8 data) +{ + ym2203_w(info, 1, data); +} + + +void ym2203_set_mute_mask(void *_info, UINT32 MuteMaskFM, UINT32 MuteMaskAY) +{ + ym2203_state *info = (ym2203_state *)_info; + ym2203_set_mutemask(info->chip, MuteMaskFM); + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); + break; +#endif + case EC_EMU2149: + PSG_setMask((PSG*)info->psg, MuteMaskAY); + break; + } + } +} + +void ym2203_set_srchg_cb(void *_info, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr) +{ + ym2203_state *info = (ym2203_state *)_info; + + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_srchg_cb_ym(info->psg, CallbackFunc, AYDataPtr); + break; +#endif + case EC_EMU2149: + break; + } + } + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym2203 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2203_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2203 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2203 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2203 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2203"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/2203intf.h b/Frameworks/GME/vgmplay/chips/2203intf.h new file mode 100644 index 000000000..3e1139534 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2203intf.h @@ -0,0 +1,43 @@ +#pragma once + +#include "ay8910.h" +#include "emu2149.h" + +void ym2203_update_request(void *param); + +typedef struct _ym2203_interface ym2203_interface; +struct _ym2203_interface +{ + ay8910_interface ay8910_intf; + //void (*handler)(const device_config *device, int irq); + void (*handler)(int irq); +}; + +/*READ8_DEVICE_HANDLER( ym2203_r ); +WRITE8_DEVICE_HANDLER( ym2203_w ); + +READ8_DEVICE_HANDLER( ym2203_status_port_r ); +READ8_DEVICE_HANDLER( ym2203_read_port_r ); +WRITE8_DEVICE_HANDLER( ym2203_control_port_w ); +WRITE8_DEVICE_HANDLER( ym2203_write_port_w ); + +DEVICE_GET_INFO( ym2203 ); +#define SOUND_YM2203 DEVICE_GET_INFO_NAME( ym2203 )*/ + +void ym2203_stream_update(void *chip, stream_sample_t **outputs, int samples); +void ym2203_stream_update_ay(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ym2203(void **chip, int core, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym2203(void *chip); +void device_reset_ym2203(void *chip); + +UINT8 ym2203_r(void *chip, offs_t offset); +void ym2203_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym2203_status_port_r(void *chip, offs_t offset); +UINT8 ym2203_read_port_r(void *chip, offs_t offset); +void ym2203_control_port_w(void *chip, offs_t offset, UINT8 data); +void ym2203_write_port_w(void *chip, offs_t offset, UINT8 data); + +void ym2203_set_mute_mask(void *chip, UINT32 MuteMaskFM, UINT32 MuteMaskAY); +void ym2203_set_srchg_cb(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr); diff --git a/Frameworks/GME/vgmplay/chips/2413intf.c b/Frameworks/GME/vgmplay/chips/2413intf.c new file mode 100644 index 000000000..4dc30b4c6 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2413intf.c @@ -0,0 +1,313 @@ +/**************************************************************** + + MAME / MESS functions + +****************************************************************/ + +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#ifdef ENABLE_ALL_CORES +#include "ym2413.h" +#endif +#include "emu2413.h" +#include "2413intf.h" + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // YM2413 core from MAME +#endif +#define EC_EMU2413 0x00 // EMU2413 core from in_vgm, value 0 because it's better than MAME + +#define NULL ((void *)0) + +/* for stream system */ +typedef struct _ym2413_state ym2413_state; +struct _ym2413_state +{ + //sound_stream * stream; + void * chip; + int EMU_CORE; + UINT8 Mode; +}; + +static unsigned char vrc7_inst[(16 + 3) * 8] = +{ +#include "vrc7tone.h" +}; + +/*INLINE ym2413_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM2413); + return (ym2413_state *)device->token; +}*/ + +#ifdef UNUSED_FUNCTION +void YM2413DAC_update(int chip,stream_sample_t **inputs, stream_sample_t **_buffer,int length) +{ + INT16 *buffer = _buffer[0]; + static int out = 0; + + if ( ym2413[chip].reg[0x0F] & 0x01 ) + { + out = ((ym2413[chip].reg[0x10] & 0xF0) << 7); + } + while (length--) *(buffer++) = out; +} +#endif + +//static STREAM_UPDATE( ym2413_stream_update ) +void ym2413_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + ym2413_state *info = (ym2413_state*)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym2413_update_one(info->chip, outputs, samples); + break; +#endif + case EC_EMU2413: + OPLL_calc_stereo(info->chip, outputs, samples, -1); + break; + } +} + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +static void _stream_update(void *param, int interval) +{ + ym2413_state *info = (ym2413_state *)param; + /*stream_update(info->stream);*/ + + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym2413_update_one(info->chip, DUMMYBUF, 0); + break; +#endif + case EC_EMU2413: + OPLL_calc_stereo(info->chip, DUMMYBUF, 0, -1); + break; + } +} + +//static DEVICE_START( ym2413 ) +int device_start_ym2413(void **_info, int EMU_CORE, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //ym2413_state *info = get_safe_token(device); + ym2413_state *info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_EMU2413; +#else + EMU_CORE = EC_EMU2413; +#endif + + info = (ym2413_state *) calloc(1, sizeof(ym2413_state)); + *_info = (void*) info; + + info->EMU_CORE = EMU_CORE; + info->Mode = (clock & 0x80000000) >> 31; + clock &= 0x7FFFFFFF; + + rate = clock/72; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + /* emulator create */ + switch(EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + info->chip = ym2413_init(clock, rate); + if (info->chip == NULL) + return 0; + //assert_always(info->chip != NULL, "Error creating YM2413 chip"); + ym2413_set_chip_mode(info->chip, info->Mode); + + /* stream system initialize */ + //info->stream = stream_create(device,0,2,rate,info,ym2413_stream_update); + + ym2413_set_update_handler(info->chip, _stream_update, info); + break; +#endif + case EC_EMU2413: + info->chip = OPLL_new(clock, rate); + if (info->chip == NULL) + return 0; + + OPLL_SetChipMode(info->chip, info->Mode); + if (info->Mode) + OPLL_setPatch(info->chip, vrc7_inst); + break; + } + // Note: VRC7 instruments are set in device_reset if necessary. + + + +/*#if 0 + int i, tst; + char name[40]; + + num = intf->num; + + tst = YM3812_sh_start (msound); + if (tst) + return 1; + + for (i=0;iclock/72, i, YM2413DAC_update); + + if (ym2413[i].DAC_stream == -1) + return 1; + } + return 0; +#endif*/ + + return rate; +} + +//static DEVICE_STOP( ym2413 ) +void device_stop_ym2413(void *_info) +{ + //ym2413_state *info = get_safe_token(device); + ym2413_state *info = (ym2413_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym2413_shutdown(info->chip); + break; +#endif + case EC_EMU2413: + OPLL_delete(info->chip); + break; + } + + free(info); +} + +//static DEVICE_RESET( ym2413 ) +void device_reset_ym2413(void *_info) +{ + //ym2413_state *info = get_safe_token(device); + ym2413_state *info = (ym2413_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym2413_reset_chip(info->chip); + if (info->Mode) + ym2413_override_patches(info->chip, vrc7_inst); + break; +#endif + case EC_EMU2413: + OPLL_reset(info->chip); + // EMU2413 doesn't reset the patch data in OPLL_reset + //if (info->Mode) + // OPLL_setPatch(info->chip, vrc7_inst); + break; + } +} + + +//WRITE8_DEVICE_HANDLER( ym2413_w ) +void ym2413_w(void *_info, offs_t offset, UINT8 data) +{ + //ym2413_state *info = get_safe_token(device); + ym2413_state *info = (ym2413_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym2413_write(info->chip, offset & 1, data); + break; +#endif + case EC_EMU2413: + OPLL_writeIO(info->chip, offset & 1, data); + break; + } +} + +//WRITE8_DEVICE_HANDLER( ym2413_register_port_w ) +void ym2413_register_port_w(void *_info, offs_t offset, UINT8 data) +{ + ym2413_w(_info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ym2413_data_port_w ) +void ym2413_data_port_w(void *_info, offs_t offset, UINT8 data) +{ + ym2413_w(_info, 1, data); +} + + +void ym2413_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ym2413_state *info = (ym2413_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym2413_set_mutemask(info->chip, MuteMask); + break; +#endif + case EC_EMU2413: + OPLL_SetMuteMask(info->chip, MuteMask); + break; + } + + return; +} + +void ym2413_set_panning(void *_info, INT16* PanVals) +{ + ym2413_state *info = (ym2413_state *)_info; + UINT8 CurChn; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + break; +#endif + case EC_EMU2413: + for (CurChn = 0x00; CurChn < 0x0E; CurChn ++) + OPLL_set_pan(info->chip, CurChn, PanVals[CurChn]); + break; + } + + return; +} + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym2413 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2413_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2413 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2413 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2413 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2413"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/2413intf.h b/Frameworks/GME/vgmplay/chips/2413intf.h new file mode 100644 index 000000000..23125cb7d --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2413intf.h @@ -0,0 +1,22 @@ +#pragma once + +/*WRITE8_DEVICE_HANDLER( ym2413_w ); + +WRITE8_DEVICE_HANDLER( ym2413_register_port_w ); +WRITE8_DEVICE_HANDLER( ym2413_data_port_w ); + +DEVICE_GET_INFO( ym2413 ); +#define SOUND_YM2413 DEVICE_GET_INFO_NAME( ym2413 )*/ + +void ym2413_stream_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ym2413(void **chip, int core, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym2413(void *chip); +void device_reset_ym2413(void *chip); + +void ym2413_w(void *chip, offs_t offset, UINT8 data); +void ym2413_register_port_w(void *chip, offs_t offset, UINT8 data); +void ym2413_data_port_w(void *chip, offs_t offset, UINT8 data); + +void ym2413_set_mute_mask(void *chip, UINT32 MuteMask); +void ym2413_set_panning(void *chip, INT16* PanVals); diff --git a/Frameworks/GME/vgmplay/chips/2413tone.h b/Frameworks/GME/vgmplay/chips/2413tone.h new file mode 100644 index 000000000..fb42e3f10 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2413tone.h @@ -0,0 +1,20 @@ +/* YM2413 tone by okazaki@angel.ne.jp */ +0x49,0x4c,0x4c,0x32,0x00,0x00,0x00,0x00, +0x61,0x61,0x1e,0x17,0xf0,0x7f,0x00,0x17, +0x13,0x41,0x16,0x0e,0xfd,0xf4,0x23,0x23, +0x03,0x01,0x9a,0x04,0xf3,0xf3,0x13,0xf3, +0x11,0x61,0x0e,0x07,0xfa,0x64,0x70,0x17, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, +0x21,0x22,0x16,0x05,0xf0,0x71,0x00,0x18, +0x21,0x61,0x1d,0x07,0x82,0x80,0x17,0x17, +0x23,0x21,0x2d,0x16,0x90,0x90,0x00,0x07, +0x21,0x21,0x1b,0x06,0x64,0x65,0x10,0x17, +0x21,0x21,0x0b,0x1a,0x85,0xa0,0x70,0x07, +0x23,0x01,0x83,0x10,0xff,0xb4,0x10,0xf4, +0x97,0xc1,0x20,0x07,0xff,0xf4,0x22,0x22, +0x61,0x00,0x0c,0x05,0xc2,0xf6,0x40,0x44, +0x01,0x01,0x56,0x03,0x94,0xc2,0x03,0x12, +0x21,0x01,0x89,0x03,0xf1,0xe4,0xf0,0x23, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, \ No newline at end of file diff --git a/Frameworks/GME/vgmplay/chips/2608intf.c b/Frameworks/GME/vgmplay/chips/2608intf.c new file mode 100644 index 000000000..512d6e544 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2608intf.c @@ -0,0 +1,486 @@ +/*************************************************************************** + + 2608intf.c + + The YM2608 emulator supports up to 2 chips. + Each chip has the following connections: + - Status Read / Control Write A + - Port Read / Data Write A + - Control Write B + - Data Write B + +***************************************************************************/ + +#include // for memset +#include // for free +#include // for NULL +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "2608intf.h" +//#include "fm.h" + + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // AY8910 core from MAME +#endif +#define EC_EMU2149 0x00 + +typedef struct _ym2608_state ym2608_state; +struct _ym2608_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + void * psg; + ym2608_interface intf; + int AY_EMU_CORE; + //const device_config *device; +}; + +#define CHTYPE_YM2608 0x21 + + +/*INLINE ym2608_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM2608); + return (ym2608_state *)device->token; +}*/ + + + +static void psg_set_clock(void *param, int clock) +{ + ym2608_state *info = (ym2608_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_clock_ym(info->psg, clock); + break; +#endif + case EC_EMU2149: + PSG_set_clock((PSG*)info->psg, clock); + break; + } + } +} + +static void psg_write(void *param, int address, int data) +{ + ym2608_state *info = (ym2608_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_write_ym(info->psg, address, data); + break; +#endif + case EC_EMU2149: + PSG_writeIO((PSG*)info->psg, address, data); + break; + } + } +} + +static int psg_read(void *param) +{ + ym2608_state *info = (ym2608_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + return ay8910_read_ym(info->psg); +#endif + case EC_EMU2149: + return PSG_readIO((PSG*)info->psg); + } + } + return 0x00; +} + +static void psg_reset(void *param) +{ + ym2608_state *info = (ym2608_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_reset_ym(info->psg); + break; +#endif + case EC_EMU2149: + PSG_reset((PSG*)info->psg); + break; + } + } +} + +static const ssg_callbacks psgintf = +{ + psg_set_clock, + psg_write, + psg_read, + psg_reset +}; + + +/* IRQ Handler */ +/*static void IRQHandler(void *param,int irq) +{ + ym2608_state *info = (ym2608_state *)param; + //if(info->intf->handler) info->intf->handler(info->device, irq); + if(info->intf.handler) info->intf.handler(irq); +}*/ + +/* Timer overflow callback from timer.c */ +/*static TIMER_CALLBACK( timer_callback_2608_0 ) +{ + ym2608_state *info = (ym2608_state *)ptr; + ym2608_timer_over(info->chip,0); +} + +static TIMER_CALLBACK( timer_callback_2608_1 ) +{ + ym2608_state *info = (ym2608_state *)ptr; + ym2608_timer_over(info->chip,1); +}*/ + +/*static void timer_handler(void *param,int c,int count,int clock) +{ + ym2608_state *info = (ym2608_state *)param; + if( count == 0 ) + { // Reset FM Timer + //timer_enable(info->timer[c], 0); + } + else + { // Start FM Timer + //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); + //if (!timer_enable(info->timer[c], 1)) + // timer_adjust_oneshot(info->timer[c], period, 0); + } +}*/ + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +/* update request from fm.c */ +void ym2608_update_request(void *param) +{ + ym2608_state *info = (ym2608_state *)param; + //stream_update(info->stream); + + ym2608_update_one(info->chip, DUMMYBUF, 0); + // Not necessary. + //if (info->psg != NULL) + // ay8910_update_one(info->psg, DUMMYBUF, 0); +} + +//static STREAM_UPDATE( ym2608_stream_update ) +void ym2608_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + //ym2608_state *info = (ym2608_state *)param; + ym2608_state *info = (ym2608_state *)_info; + ym2608_update_one(info->chip, outputs, samples); +} + +void ym2608_stream_update_ay(void *_info, stream_sample_t **outputs, int samples) +{ + //ym2608_state *info = (ym2608_state *)param; + ym2608_state *info = (ym2608_state *)_info; + + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_update_one(info->psg, outputs, samples); + break; +#endif + case EC_EMU2149: + PSG_calc_stereo((PSG*)info->psg, outputs, samples); + break; + } + } + else + { + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + } +} + + +//static STATE_POSTLOAD( ym2608_intf_postload ) +/*static void ym2608_intf_postload(UINT8 ChipID) +{ + //ym2608_state *info = (ym2608_state *)param; + ym2608_state *info = &YM2608Data[ChipID]; + ym2608_postload(info->chip); +}*/ + + +//static DEVICE_START( ym2608 ) +int device_start_ym2608(void **_info, int AY_EMU_CORE, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + static const ym2608_interface generic_2608 = + { + { + AY8910_LEGACY_OUTPUT | AY8910_SINGLE_OUTPUT, + AY8910_DEFAULT_LOADS + //DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL + }, + NULL + }; + //const ym2608_interface *intf = device->static_config ? (const ym2608_interface *)device->static_config : &generic_2608; + ym2608_interface *intf; + int rate; + int ay_clock; + //void *pcmbufa; + //int pcmsizea; + +#ifdef ENABLE_ALL_CORES + if (AY_EMU_CORE >= 0x02) + AY_EMU_CORE = EC_EMU2149; +#else + AY_EMU_CORE = EC_EMU2149; +#endif + + //ym2608_state *info = get_safe_token(device); + ym2608_state *info; + + info = (ym2608_state *) calloc(1, sizeof(ym2608_state)); + *_info = (void *) info; + + info->AY_EMU_CORE = AY_EMU_CORE; + rate = clock/72; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + info->intf = generic_2608; + intf = &info->intf; + if (AYFlags) + intf->ay8910_intf.flags = AYFlags; + //info->device = device; + + /* FIXME: Force to use single output */ + //info->psg = ay8910_start_ym(NULL, SOUND_YM2608, clock, &intf->ay8910_intf); + if (! AYDisable) + { + ay_clock = clock / 4; + *AYrate = ay_clock / 8; + switch(AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + info->psg = ay8910_start_ym(NULL, CHTYPE_YM2608, ay_clock, &intf->ay8910_intf); + break; +#endif + case EC_EMU2149: + info->psg = PSG_new(ay_clock, *AYrate); + if (info->psg == NULL) + return 0; + PSG_setVolumeMode((PSG*)info->psg, 1); // YM2149 volume mode + break; + } + } + else + { + info->psg = NULL; + *AYrate = 0; + } + //assert_always(info->psg != NULL, "Error creating YM2608/AY8910 chip"); + + /* Timer Handler set */ + //info->timer[0] = timer_alloc(device->machine, timer_callback_2608_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_2608_1, info); + + /* stream system initialize */ + //info->stream = stream_create(device,0,2,rate,info,ym2608_stream_update); + /* setup adpcm buffers */ + //pcmbufa = device->region; + //pcmsizea = device->regionbytes; + + /* initialize YM2608 */ + //info->chip = ym2608_init(info,device,device->clock,rate, + // pcmbufa,pcmsizea, + // timer_handler,IRQHandler,&psgintf); + info->chip = ym2608_init(info, clock, rate, NULL, NULL, &psgintf); + //assert_always(info->chip != NULL, "Error creating YM2608 chip"); + + //state_save_register_postload(device->machine, ym2608_intf_postload, info); + + return rate; +} + +//static DEVICE_STOP( ym2608 ) +void device_stop_ym2608(void *_info) +{ + //ym2608_state *info = get_safe_token(device); + ym2608_state *info = (ym2608_state *)_info; + ym2608_shutdown(info->chip); + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_stop_ym(info->psg); + break; +#endif + case EC_EMU2149: + PSG_delete((PSG*)info->psg); + break; + } + info->psg = NULL; + } + free(info); +} + +//static DEVICE_RESET( ym2608 ) +void device_reset_ym2608(void *_info) +{ + //ym2608_state *info = get_safe_token(device); + ym2608_state *info = (ym2608_state *)_info; + ym2608_reset_chip(info->chip); // also resets the AY clock + //psg_reset(info); // already done as a callback in ym2608_reset_chip +} + + +//READ8_DEVICE_HANDLER( ym2608_r ) +UINT8 ym2608_r(void *_info, offs_t offset) +{ + //ym2608_state *info = get_safe_token(device); + ym2608_state *info = (ym2608_state *)_info; + return ym2608_read(info->chip, offset & 3); +} + +//WRITE8_DEVICE_HANDLER( ym2608_w ) +void ym2608_w(void *_info, offs_t offset, UINT8 data) +{ + //ym2608_state *info = get_safe_token(device); + ym2608_state *info = (ym2608_state *)_info; + ym2608_write(info->chip, offset & 3, data); +} + +//READ8_DEVICE_HANDLER( ym2608_read_port_r ) +UINT8 ym2608_read_port_r(void *info, offs_t offset) +{ + return ym2608_r(info, 1); +} +//READ8_DEVICE_HANDLER( ym2608_status_port_a_r ) +UINT8 ym2608_status_port_a_r(void *info, offs_t offset) +{ + return ym2608_r(info, 0); +} +//READ8_DEVICE_HANDLER( ym2608_status_port_b_r ) +UINT8 ym2608_status_port_b_r(void *info, offs_t offset) +{ + return ym2608_r(info, 2); +} + +//WRITE8_DEVICE_HANDLER( ym2608_control_port_a_w ) +void ym2608_control_port_a_w(void *info, offs_t offset, UINT8 data) +{ + ym2608_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ym2608_control_port_b_w ) +void ym2608_control_port_b_w(void *info, offs_t offset, UINT8 data) +{ + ym2608_w(info, 2, data); +} +//WRITE8_DEVICE_HANDLER( ym2608_data_port_a_w ) +void ym2608_data_port_a_w(void *info, offs_t offset, UINT8 data) +{ + ym2608_w(info, 1, data); +} +//WRITE8_DEVICE_HANDLER( ym2608_data_port_b_w ) +void ym2608_data_port_b_w(void *info, offs_t offset, UINT8 data) +{ + ym2608_w(info, 3, data); +} + + +void ym2608_write_data_pcmrom(void *_info, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData) +{ + ym2608_state* info = (ym2608_state *)_info; + ym2608_write_pcmrom(info->chip, rom_id, ROMSize, DataStart, DataLength, ROMData); +} + +void ym2608_set_mute_mask(void *_info, UINT32 MuteMaskFM, UINT32 MuteMaskAY) +{ + ym2608_state* info = (ym2608_state *)_info; + ym2608_set_mutemask(info->chip, MuteMaskFM); + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); + break; +#endif + case EC_EMU2149: + PSG_setMask((PSG*)info->psg, MuteMaskAY); + break; + } + } +} + +void ym2608_set_srchg_cb(void *_info, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr) +{ + ym2608_state* info = (ym2608_state *)_info; + + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_srchg_cb_ym(info->psg, CallbackFunc, AYDataPtr); + break; +#endif + case EC_EMU2149: + break; + } + } + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym2608 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2608_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2608 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2608 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2608 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2608"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/2608intf.h b/Frameworks/GME/vgmplay/chips/2608intf.h new file mode 100644 index 000000000..d2df097bc --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2608intf.h @@ -0,0 +1,54 @@ +#pragma once + +#include "fm.h" +#include "ay8910.h" +#include "emu2149.h" + +void ym2608_update_request(void *param); + +typedef struct _ym2608_interface ym2608_interface; +struct _ym2608_interface +{ + ay8910_interface ay8910_intf; + //void ( *handler )( const device_config *device, int irq ); /* IRQ handler for the YM2608 */ + void ( *handler )( int irq ); /* IRQ handler for the YM2608 */ +}; + +/*READ8_DEVICE_HANDLER( ym2608_r ); +WRITE8_DEVICE_HANDLER( ym2608_w ); + +READ8_DEVICE_HANDLER( ym2608_read_port_r ); +READ8_DEVICE_HANDLER( ym2608_status_port_a_r ); +READ8_DEVICE_HANDLER( ym2608_status_port_b_r ); + +WRITE8_DEVICE_HANDLER( ym2608_control_port_a_w ); +WRITE8_DEVICE_HANDLER( ym2608_control_port_b_w ); +WRITE8_DEVICE_HANDLER( ym2608_data_port_a_w ); +WRITE8_DEVICE_HANDLER( ym2608_data_port_b_w ); + +DEVICE_GET_INFO( ym2608 ); +#define SOUND_YM2608 DEVICE_GET_INFO_NAME( ym2608 )*/ + +void ym2608_stream_update(void *chip, stream_sample_t **outputs, int samples); +void ym2608_stream_update_ay(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ym2608(void **chip, int core, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym2608(void *chip); +void device_reset_ym2608(void *chip); + +UINT8 ym2608_r(void *chip, offs_t offset); +void ym2608_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym2608_read_port_r(void *chip, offs_t offset); +UINT8 ym2608_status_port_a_r(void *chip, offs_t offset); +UINT8 ym2608_status_port_b_r(void *chip, offs_t offset); + +void ym2608_control_port_a_w(void *chip, offs_t offset, UINT8 data); +void ym2608_control_port_b_w(void *chip, offs_t offset, UINT8 data); +void ym2608_data_port_a_w(void *chip, offs_t offset, UINT8 data); +void ym2608_data_port_b_w(void *chip, offs_t offset, UINT8 data); + +void ym2608_write_data_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData); +void ym2608_set_mute_mask(void *chip, UINT32 MuteMaskFM, UINT32 MuteMaskAY); +void ym2608_set_srchg_cb(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr); diff --git a/Frameworks/GME/vgmplay/chips/2610intf.c b/Frameworks/GME/vgmplay/chips/2610intf.c new file mode 100644 index 000000000..44c03015e --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2610intf.c @@ -0,0 +1,492 @@ +/*************************************************************************** + + 2610intf.c + + The YM2610 emulator supports up to 2 chips. + Each chip has the following connections: + - Status Read / Control Write A + - Port Read / Data Write A + - Control Write B + - Data Write B + +***************************************************************************/ + +#include // for memset +#include // for free +#include // for NULL +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "2610intf.h" +#include "fm.h" + + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // AY8910 core from MAME +#endif +#define EC_EMU2149 0x00 + +typedef struct _ym2610_state ym2610_state; +struct _ym2610_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + void * psg; + int AY_EMU_CORE; + //const ym2610_interface *intf; + //const device_config *device; +}; + +#define CHTYPE_YM2610 0x22 + + +/*INLINE ym2610_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM2610 || sound_get_type(device) == SOUND_YM2610B); + return (ym2610_state *)device->token; +}*/ + + +static void psg_set_clock(void *param, int clock) +{ + ym2610_state *info = (ym2610_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_clock_ym(info->psg, clock); + break; +#endif + case EC_EMU2149: + PSG_set_clock((PSG*)info->psg, clock); + break; + } + } +} + +static void psg_write(void *param, int address, int data) +{ + ym2610_state *info = (ym2610_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_write_ym(info->psg, address, data); + break; +#endif + case EC_EMU2149: + PSG_writeIO((PSG*)info->psg, address, data); + break; + } + } +} + +static int psg_read(void *param) +{ + ym2610_state *info = (ym2610_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + return ay8910_read_ym(info->psg); +#endif + case EC_EMU2149: + return PSG_readIO((PSG*)info->psg); + } + } + return 0x00; +} + +static void psg_reset(void *param) +{ + ym2610_state *info = (ym2610_state *)param; + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_reset_ym(info->psg); + break; +#endif + case EC_EMU2149: + PSG_reset((PSG*)info->psg); + break; + } + } +} + +static const ssg_callbacks psgintf = +{ + psg_set_clock, + psg_write, + psg_read, + psg_reset +}; + +/*------------------------- TM2610 -------------------------------*/ +/* IRQ Handler */ +/*static void IRQHandler(void *param,int irq) +{ + ym2610_state *info = (ym2610_state *)param; + //if(info->intf->handler) info->intf->handler(info->device, irq); + //if(info->intf->handler) info->intf->handler(irq); +}*/ + +/* Timer overflow callback from timer.c */ +/*static TIMER_CALLBACK( timer_callback_0 ) +{ + ym2610_state *info = (ym2610_state *)ptr; + ym2610_timer_over(info->chip,0); +} + +static TIMER_CALLBACK( timer_callback_1 ) +{ + ym2610_state *info = (ym2610_state *)ptr; + ym2610_timer_over(info->chip,1); +}*/ + +/*static void timer_handler(void *param,int c,int count,int clock) +{ + ym2610_state *info = (ym2610_state *)param; + if( count == 0 ) + { // Reset FM Timer + //timer_enable(info->timer[c], 0); + } + else + { // Start FM Timer + //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); + + //if (!timer_enable(info->timer[c], 1)) + // timer_adjust_oneshot(info->timer[c], period, 0); + } +}*/ + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +/* update request from fm.c */ +void ym2610_update_request(void *param) +{ + ym2610_state *info = (ym2610_state *)param; + //stream_update(info->stream); + + ym2610b_update_one(info->chip, DUMMYBUF, 0); + // Not necessary. + //if (info->psg != NULL) + // ay8910_update_one(info->psg, DUMMYBUF, 0); +} + + +//static STREAM_UPDATE( ym2610_stream_update ) +void ym2610_stream_update(void *param, stream_sample_t **outputs, int samples) +{ + ym2610_state *info = (ym2610_state *)param; + ym2610_update_one(info->chip, outputs, samples); +} + +//static STREAM_UPDATE( ym2610b_stream_update ) +void ym2610b_stream_update(void *param, stream_sample_t **outputs, int samples) +{ + ym2610_state *info = (ym2610_state *)param; + ym2610b_update_one(info->chip, outputs, samples); +} + +void ym2610_stream_update_ay(void *param, stream_sample_t **outputs, int samples) +{ + ym2610_state *info = (ym2610_state *)param; + + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_update_one(info->psg, outputs, samples); + break; +#endif + case EC_EMU2149: + PSG_calc_stereo((PSG*)info->psg, outputs, samples); + break; + } + } + else + { + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + } +} + + +//static STATE_POSTLOAD( ym2610_intf_postload ) +/*static void ym2610_intf_postload(UINT8 ChipID) +{ + //ym2610_state *info = (ym2610_state *)param; + ym2610_state *info = &YM2610Data[ChipID]; + ym2610_postload(info->chip); +}*/ + + +//static DEVICE_START( ym2610 ) +int device_start_ym2610(void **_info, int AY_EMU_CORE, int clock, UINT8 AYDisable, int* AYrate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + // clock bit 31: 0 - YM2610 + // 1 - YM2610B + + //static const ym2610_interface generic_2610 = { 0 }; + static const ay8910_interface generic_ay8910 = + { + AY8910_LEGACY_OUTPUT | AY8910_SINGLE_OUTPUT, + AY8910_DEFAULT_LOADS + //DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL + }; + //const ym2610_interface *intf = device->static_config ? (const ym2610_interface *)device->static_config : &generic_2610; + //const ym2610_interface *intf = &generic_2610; + int rate; + int ay_clock; + //void *pcmbufa,*pcmbufb; + //int pcmsizea,pcmsizeb; + //ym2610_state *info = get_safe_token(device); + ym2610_state *info; + //astring *name = astring_alloc(); + //sound_type type = sound_get_type(device); + unsigned char ChipType; + +#ifdef ENABLE_ALL_CORES + if (AY_EMU_CORE >= 0x02) + AY_EMU_CORE = EC_EMU2149; +#else + AY_EMU_CORE = EC_EMU2149; +#endif + + info = (ym2610_state *) calloc(1, sizeof(ym2610_state)); + *_info = (void *) info; + + info->AY_EMU_CORE = AY_EMU_CORE; + ChipType = (clock & 0x80000000) ? 0x01 : 0x00; + clock &= 0x7FFFFFFF; + rate = clock/72; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + //info->intf = intf; + //info->device = device; + //info->psg = ay8910_start_ym(NULL, sound_get_type(device), device, device->clock, &generic_ay8910); + if (! AYDisable) + { + ay_clock = clock / 4; + *AYrate = ay_clock / 8; + switch(AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + info->psg = ay8910_start_ym(NULL, CHTYPE_YM2610 + ChipType, ay_clock, &generic_ay8910); + break; +#endif + case EC_EMU2149: + info->psg = PSG_new(ay_clock, *AYrate); + if (info->psg == NULL) + return 0; + PSG_setVolumeMode((PSG*)info->psg, 1); // YM2149 volume mode + break; + } + } + else + { + info->psg = NULL; + *AYrate = 0; + } + //assert_always(info->psg != NULL, "Error creating YM2610/AY8910 chip"); + + /* Timer Handler set */ + //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); + + /* stream system initialize */ + //info->stream = stream_create(device,0,2,rate,info,(type == SOUND_YM2610) ? ym2610_stream_update : ym2610b_stream_update); + /* setup adpcm buffers */ + //pcmbufa = device->region; + //pcmsizea = device->regionbytes; + //astring_printf(name, "%s.deltat", device->tag); + //pcmbufb = (void *)(memory_region(device->machine, astring_c(name))); + //pcmsizeb = memory_region_length(device->machine, astring_c(name)); + //astring_free(name); + /*if (pcmbufb == NULL || pcmsizeb == 0) + { + pcmbufb = pcmbufa; + pcmsizeb = pcmsizea; + }*/ + + /**** initialize YM2610 ****/ + //info->chip = ym2610_init(info,device,device->clock,rate, + // pcmbufa,pcmsizea,pcmbufb,pcmsizeb, + // timer_handler,IRQHandler,&psgintf); + info->chip = ym2610_init(info, clock & 0x7FFFFFFF, rate, NULL, NULL, &psgintf); + //assert_always(info->chip != NULL, "Error creating YM2610 chip"); + + //state_save_register_postload(device->machine, ym2610_intf_postload, info); + + return rate; +} + +//static DEVICE_STOP( ym2610 ) +void device_stop_ym2610(void *_info) +{ + //ym2610_state *info = get_safe_token(device); + ym2610_state* info = (ym2610_state *)_info; + ym2610_shutdown(info->chip); + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_stop_ym(info->psg); + break; +#endif + case EC_EMU2149: + PSG_delete((PSG*)info->psg); + break; + } + info->psg = NULL; + } + free(info); +} + +//static DEVICE_RESET( ym2610 ) +void device_reset_ym2610(void *_info) +{ + //ym2610_state *info = get_safe_token(device); + ym2610_state* info = (ym2610_state *)_info; + ym2610_reset_chip(info->chip); // also resets the AY clock + //psg_reset(info); // already done as a callback in ym2610_reset_chip +} + + +//READ8_DEVICE_HANDLER( ym2610_r ) +UINT8 ym2610_r(void *_info, offs_t offset) +{ + //ym2610_state *info = get_safe_token(device); + ym2610_state* info = (ym2610_state *)_info; + return ym2610_read(info->chip, offset & 3); +} + +//WRITE8_DEVICE_HANDLER( ym2610_w ) +void ym2610_w(void *_info, offs_t offset, UINT8 data) +{ + //ym2610_state *info = get_safe_token(device); + ym2610_state* info = (ym2610_state *)_info; + ym2610_write(info->chip, offset & 3, data); +} + + +//READ8_DEVICE_HANDLER( ym2610_status_port_a_r ) +UINT8 ym2610_status_port_a_r(void *info, offs_t offset) +{ + return ym2610_r(info, 0); +} +//READ8_DEVICE_HANDLER( ym2610_status_port_b_r ) +UINT8 ym2610_status_port_b_r(void *info, offs_t offset) +{ + return ym2610_r(info, 2); +} +//READ8_DEVICE_HANDLER( ym2610_read_port_r ) +UINT8 ym2610_read_port_r(void *info, offs_t offset) +{ + return ym2610_r(info, 1); +} + +//WRITE8_DEVICE_HANDLER( ym2610_control_port_a_w ) +void ym2610_control_port_a_w(void *info, offs_t offset, UINT8 data) +{ + ym2610_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ym2610_control_port_b_w ) +void ym2610_control_port_b_w(void *info, offs_t offset, UINT8 data) +{ + ym2610_w(info, 2, data); +} +//WRITE8_DEVICE_HANDLER( ym2610_data_port_a_w ) +void ym2610_data_port_a_w(void *info, offs_t offset, UINT8 data) +{ + ym2610_w(info, 1, data); +} +//WRITE8_DEVICE_HANDLER( ym2610_data_port_b_w ) +void ym2610_data_port_b_w(void *info, offs_t offset, UINT8 data) +{ + ym2610_w(info, 3, data); +} + + +void ym2610_write_data_pcmrom(void *_info, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData) +{ + ym2610_state* info = (ym2610_state *)_info; + ym2610_write_pcmrom(info->chip, rom_id, ROMSize, DataStart, DataLength, ROMData); +} + +void ym2610_set_mute_mask(void *_info, UINT32 MuteMaskFM, UINT32 MuteMaskAY) +{ + ym2610_state* info = (ym2610_state *)_info; + ym2610_set_mutemask(info->chip, MuteMaskFM); + if (info->psg != NULL) + { + switch(info->AY_EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); + break; +#endif + case EC_EMU2149: + PSG_setMask((PSG*)info->psg, MuteMaskAY); + break; + } + } +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym2610 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2610_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2610 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2610 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2610 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2610"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + +/*DEVICE_GET_INFO( ym2610b ) +{ + switch (state) + { + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2610B"); break; + + default: DEVICE_GET_INFO_CALL(ym2610); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/2610intf.h b/Frameworks/GME/vgmplay/chips/2610intf.h new file mode 100644 index 000000000..4027f3bf7 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2610intf.h @@ -0,0 +1,59 @@ +#pragma once + +#include "fm.h" +#include "ay8910.h" +#include "emu2149.h" + + +void ym2610_update_request(void *param); + +/*typedef struct _ym2610_interface ym2610_interface; +struct _ym2610_interface +{ + //void ( *handler )( const device_config *device, int irq ); // IRQ handler for the YM2610 + void ( *handler )( int irq ); // IRQ handler for the YM2610 +};*/ + +/*READ8_DEVICE_HANDLER( ym2610_r ); +WRITE8_DEVICE_HANDLER( ym2610_w ); + +READ8_DEVICE_HANDLER( ym2610_status_port_a_r ); +READ8_DEVICE_HANDLER( ym2610_status_port_b_r ); +READ8_DEVICE_HANDLER( ym2610_read_port_r ); + +WRITE8_DEVICE_HANDLER( ym2610_control_port_a_w ); +WRITE8_DEVICE_HANDLER( ym2610_control_port_b_w ); +WRITE8_DEVICE_HANDLER( ym2610_data_port_a_w ); +WRITE8_DEVICE_HANDLER( ym2610_data_port_b_w ); + + +DEVICE_GET_INFO( ym2610 ); +DEVICE_GET_INFO( ym2610b ); + +#define SOUND_YM2610 DEVICE_GET_INFO_NAME( ym2610 ) +#define SOUND_YM2610B DEVICE_GET_INFO_NAME( ym2610b )*/ + +void ym2610_stream_update(void *chip, stream_sample_t **outputs, int samples); +void ym2610b_stream_update(void *chip, stream_sample_t **outputs, int samples); +void ym2610_stream_update_ay(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ym2610(void **chip, int core, int clock, UINT8 AYDisable, int* AYrate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym2610(void *chip); +void device_reset_ym2610(void *chip); + +UINT8 ym2610_r(void *chip, offs_t offset); +void ym2610_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym2610_status_port_a_r(void *chip, offs_t offset); +UINT8 ym2610_status_port_b_r(void *chip, offs_t offset); +UINT8 ym2610_read_port_r(void *chip, offs_t offset); + +void ym2610_control_port_a_w(void *chip, offs_t offset, UINT8 data); +void ym2610_control_port_b_w(void *chip, offs_t offset, UINT8 data); +void ym2610_data_port_a_w(void *chip, offs_t offset, UINT8 data); +void ym2610_data_port_b_w(void *chip, offs_t offset, UINT8 data); + +void ym2610_write_data_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData); +void ym2610_set_mute_mask(void *chip, UINT32 MuteMaskFM, UINT32 MuteMaskAY); + diff --git a/Frameworks/GME/vgmplay/chips/2612intf.c b/Frameworks/GME/vgmplay/chips/2612intf.c new file mode 100644 index 000000000..a943c6739 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2612intf.c @@ -0,0 +1,412 @@ +/*************************************************************************** + + 2612intf.c + + The YM2612 emulator supports up to 2 chips. + Each chip has the following connections: + - Status Read / Control Write A + - Port Read / Data Write A + - Control Write B + - Data Write B + +***************************************************************************/ + +#include +#include // for NULL +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +//#include "sound/fm.h" +//#include "sound/2612intf.h" +#include "fm.h" +#include "2612intf.h" +#ifdef ENABLE_ALL_CORES +#include "ym2612.h" +#endif + + +#define EC_MAME 0x00 // YM2612 core from MAME (now fixed, so it isn't worse than Gens anymore) +#ifdef ENABLE_ALL_CORES +#define EC_GENS 0x01 // Gens YM2612 core from in_vgm +#endif + +typedef struct _ym2612_state ym2612_state; +struct _ym2612_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + int EMU_CORE; + //const ym2612_interface *intf; + //const device_config *device; + int* GensBuf[0x02]; + UINT8 ChipFlags; +}; + + +/*INLINE ym2612_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM2612 || sound_get_type(device) == SOUND_YM3438); + return (ym2612_state *)device->token; +}*/ + +/*------------------------- TM2612 -------------------------------*/ +/* IRQ Handler */ +/*static void IRQHandler(void *param,int irq) +{ + ym2612_state *info = (ym2612_state *)param; + //if(info->intf->handler) info->intf->handler(info->device, irq); + //if(info->intf->handler) info->intf->handler(irq); +}*/ + +/* Timer overflow callback from timer.c */ +//static TIMER_CALLBACK( timer_callback_2612_0 ) +/*void timer_callback_2612_0(void *ptr, int param) +{ + ym2612_state *info = (ym2612_state *)ptr; + ym2612_timer_over(info->chip,0); +} + +//static TIMER_CALLBACK( timer_callback_2612_1 ) +void timer_callback_2612_1(void *ptr, int param) +{ + ym2612_state *info = (ym2612_state *)ptr; + ym2612_timer_over(info->chip,1); +}*/ + +/*static void timer_handler(void *param,int c,int count,int clock) +{ + ym2612_state *info = (ym2612_state *)param; + if( count == 0 ) + { // Reset FM Timer + //timer_enable(info->timer[c], 0); + } + else + { // Start FM Timer + //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); + //if (!timer_enable(info->timer[c], 1)) + // timer_adjust_oneshot(info->timer[c], period, 0); + } +}*/ + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +/* update request from fm.c */ +void ym2612_update_request(void *param) +{ + ym2612_state *info = (ym2612_state *)param; + //stream_update(info->stream); + + if (!param) + return; + + switch(info->EMU_CORE) + { + case EC_MAME: + ym2612_update_one(info->chip, DUMMYBUF, 0); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + YM2612_Update(info->chip, DUMMYBUF, 0); + YM2612_DacAndTimers_Update(info->chip, DUMMYBUF, 0); + break; +#endif + } +} + +/***********************************************************/ +/* YM2612 */ +/***********************************************************/ + +//static STREAM_UPDATE( ym2612_stream_update ) +void ym2612_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + //ym2612_state *info = (ym2612_state *)param; + ym2612_state *info = (ym2612_state *)_info; +#ifdef ENABLE_ALL_CORES + int i; +#endif + + switch(info->EMU_CORE) + { + case EC_MAME: + ym2612_update_one(info->chip, outputs, samples); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + YM2612_ClearBuffer(info->GensBuf, samples); + YM2612_Update(info->chip, info->GensBuf, samples); + YM2612_DacAndTimers_Update(info->chip, info->GensBuf, samples); + for (i = 0x00; i < samples; i ++) + { + outputs[0x00][i] = (stream_sample_t)info->GensBuf[0x00][i]; + outputs[0x01][i] = (stream_sample_t)info->GensBuf[0x01][i]; + } + break; +#endif + } +} + + +//static STATE_POSTLOAD( ym2612_intf_postload ) +/*static void ym2612_intf_postload(UINT8 ChipID) +{ + //ym2612_state *info = (ym2612_state *)param; + ym2612_state *info = &YM2612Data[ChipID]; + ym2612_postload(info->chip); +}*/ + + +//static DEVICE_START( ym2612 ) +int device_start_ym2612(void **_info, int EMU_CORE, int ChipFlags, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE, UINT8 * IsVGMInit) +{ + //static const ym2612_interface dummy = { 0 }; + //ym2612_state *info = get_safe_token(device); + ym2612_state *info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_MAME; +#else + EMU_CORE = EC_MAME; +#endif + + info = (ym2612_state *) calloc(1, sizeof(ym2612_state)); + *_info = (void *) info; + + info->EMU_CORE = EMU_CORE; + info->ChipFlags = ChipFlags; + rate = clock/72; + if (EMU_CORE == EC_MAME && ! (ChipFlags & 0x02)) + rate /= 2; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + //info->intf = device->static_config ? (const ym2612_interface *)device->static_config : &dummy; + //info->intf = &dummy; + //info->device = device; + + /* FM init */ + /* Timer Handler set */ + //info->timer[0] = timer_alloc(device->machine, timer_callback_2612_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_2612_1, info); + + /* stream system initialize */ + //info->stream = stream_create(device,0,2,rate,info,ym2612_stream_update); + + /**** initialize YM2612 ****/ + switch(EMU_CORE) + { + case EC_MAME: + //info->chip = ym2612_init(info,clock,rate,timer_handler,IRQHandler); + info->chip = ym2612_init(info, clock, rate, NULL, NULL, IsVGMInit, ChipFlags); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + if (info->GensBuf[0x00] == NULL) + { + info->GensBuf[0x00] = malloc(sizeof(int) * 0x100); + info->GensBuf[0x01] = info->GensBuf[0x00] + 0x80; + } + info->chip = YM2612_Init(clock, rate, 0x00); + YM2612_SetMute(info->chip, 0x80); // Disable SSG-EG + YM2612_SetOptions(ChipFlags); + break; +#endif + } + //assert_always(info->chip != NULL, "Error creating YM2612 chip"); + //ym2612_postload(info->chip); + + //state_save_register_postload(device->machine, ym2612_intf_postload, info); + //ym2612_intf_postload(); + + return rate; +} + + +//static DEVICE_STOP( ym2612 ) +void device_stop_ym2612(void *_info) +{ + //ym2612_state *info = get_safe_token(device); + ym2612_state *info = (ym2612_state *)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + ym2612_shutdown(info->chip); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + YM2612_End(info->chip); + if (info->GensBuf[0x00] != NULL) + { + free(info->GensBuf[0x00]); + info->GensBuf[0x00] = NULL; + info->GensBuf[0x01] = NULL; + } + break; +#endif + } + + free(info); +} + +//static DEVICE_RESET( ym2612 ) +void device_reset_ym2612(void *_info) +{ + //ym2612_state *info = get_safe_token(device); + ym2612_state *info = (ym2612_state *)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + ym2612_reset_chip(info->chip); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + YM2612_Reset(info->chip); + break; +#endif + } +} + + +//READ8_DEVICE_HANDLER( ym2612_r ) +UINT8 ym2612_r(void *_info, offs_t offset) +{ + //ym2612_state *info = get_safe_token(device); + ym2612_state *info = (ym2612_state *)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + return ym2612_read(info->chip, offset & 3); +#ifdef ENABLE_ALL_CORES + case EC_GENS: + return YM2612_Read(info->chip); +#endif + default: + return 0x00; + } +} + +//WRITE8_DEVICE_HANDLER( ym2612_w ) +void ym2612_w(void *_info, offs_t offset, UINT8 data) +{ + //ym2612_state *info = get_safe_token(device); + ym2612_state *info = (ym2612_state *)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + ym2612_write(info->chip, offset & 3, data); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + YM2612_Write(info->chip, (unsigned char)(offset & 0x03), data); + break; +#endif + } +} + + +/*READ8_DEVICE_HANDLER( ym2612_status_port_a_r ) { return ym2612_r(device, 0); } +READ8_DEVICE_HANDLER( ym2612_status_port_b_r ) { return ym2612_r(device, 2); } +READ8_DEVICE_HANDLER( ym2612_data_port_a_r ) { return ym2612_r(device, 1); } +READ8_DEVICE_HANDLER( ym2612_data_port_b_r ) { return ym2612_r(device, 3); } + +WRITE8_DEVICE_HANDLER( ym2612_control_port_a_w ) { ym2612_w(device, 0, data); } +WRITE8_DEVICE_HANDLER( ym2612_control_port_b_w ) { ym2612_w(device, 2, data); } +WRITE8_DEVICE_HANDLER( ym2612_data_port_a_w ) { ym2612_w(device, 1, data); } +WRITE8_DEVICE_HANDLER( ym2612_data_port_b_w ) { ym2612_w(device, 3, data); }*/ +UINT8 ym2612_status_port_a_r(void *_info, offs_t offset) +{ + return ym2612_r(_info, 0); +} +UINT8 ym2612_status_port_b_r(void *_info, offs_t offset) +{ + return ym2612_r(_info, 2); +} +UINT8 ym2612_data_port_a_r(void *_info, offs_t offset) +{ + return ym2612_r(_info, 1); +} +UINT8 ym2612_data_port_b_r(void *_info, offs_t offset) +{ + return ym2612_r(_info, 3); +} + +void ym2612_control_port_a_w(void *_info, offs_t offset, UINT8 data) +{ + ym2612_w(_info, 0, data); +} +void ym2612_control_port_b_w(void *_info, offs_t offset, UINT8 data) +{ + ym2612_w(_info, 2, data); +} +void ym2612_data_port_a_w(void *_info, offs_t offset, UINT8 data) +{ + ym2612_w(_info, 1, data); +} +void ym2612_data_port_b_w(void *_info, offs_t offset, UINT8 data) +{ + ym2612_w(_info, 3, data); +} + + +void ym2612_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ym2612_state *info = (ym2612_state *)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + ym2612_set_mutemask(info->chip, MuteMask); + break; +#ifdef ENABLE_ALL_CORES + case EC_GENS: + YM2612_SetMute(info->chip, (int)MuteMask); + break; +#endif + } + + return; +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym2612 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2612_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2612 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2612 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2612 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM2612"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEVICE_GET_INFO( ym3438 ) +{ + switch (state) + { + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM3438"); break; + + default: DEVICE_GET_INFO_CALL(ym2612); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/2612intf.h b/Frameworks/GME/vgmplay/chips/2612intf.h new file mode 100644 index 000000000..3aadd9121 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/2612intf.h @@ -0,0 +1,72 @@ +#pragma once + +void ym2612_update_request(void *param); + +/*typedef struct _ym2612_interface ym2612_interface; +struct _ym2612_interface +{ + //void (*handler)(const device_config *device, int irq); + void (*handler)(int irq); +};*/ + +/*READ8_DEVICE_HANDLER( ym2612_r ); +WRITE8_DEVICE_HANDLER( ym2612_w ); + +READ8_DEVICE_HANDLER( ym2612_status_port_a_r ); +READ8_DEVICE_HANDLER( ym2612_status_port_b_r ); +READ8_DEVICE_HANDLER( ym2612_data_port_a_r ); +READ8_DEVICE_HANDLER( ym2612_data_port_b_r ); + +WRITE8_DEVICE_HANDLER( ym2612_control_port_a_w ); +WRITE8_DEVICE_HANDLER( ym2612_control_port_b_w ); +WRITE8_DEVICE_HANDLER( ym2612_data_port_a_w ); +WRITE8_DEVICE_HANDLER( ym2612_data_port_b_w ); + + +DEVICE_GET_INFO( ym2612 ); +#define SOUND_YM2612 DEVICE_GET_INFO_NAME( ym2612 )*/ +void ym2612_stream_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_ym2612(void **chip, int core, int options, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE, UINT8 * IsVGMInit); +void device_stop_ym2612(void *chip); +void device_reset_ym2612(void *chip); + +UINT8 ym2612_r(void *chip, offs_t offset); +void ym2612_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym2612_status_port_a_r(void *chip, offs_t offset); +UINT8 ym2612_status_port_b_r(void *chip, offs_t offset); +UINT8 ym2612_data_port_a_r(void *chip, offs_t offset); +UINT8 ym2612_data_port_b_r(void *chip, offs_t offset); + +void ym2612_control_port_a_w(void *chip, offs_t offset, UINT8 data); +void ym2612_control_port_b_w(void *chip, offs_t offset, UINT8 data); +void ym2612_data_port_a_w(void *chip, offs_t offset, UINT8 data); +void ym2612_data_port_b_w(void *chip, offs_t offset, UINT8 data); + +void ym2612_set_mute_mask(void *chip, UINT32 MuteMask); + + +/*typedef struct _ym3438_interface ym3438_interface; +struct _ym3438_interface +{ + //void (*handler)(const device_config *device, int irq); + void (*handler)(int irq); +}; + + +#define ym3438_r ym2612_r +#define ym3438_w ym2612_w + +#define ym3438_status_port_a_r ym2612_status_port_a_r +#define ym3438_status_port_b_r ym2612_status_port_b_r +#define ym3438_data_port_a_r ym2612_data_port_a_r +#define ym3438_data_port_b_r ym2612_data_port_b_r + +#define ym3438_control_port_a_w ym2612_control_port_a_w +#define ym3438_control_port_b_w ym2612_control_port_b_w +#define ym3438_data_port_a_w ym2612_data_port_a_w +#define ym3438_data_port_b_w ym2612_data_port_b_w*/ + + +//DEVICE_GET_INFO( ym3438 ); +//#define SOUND_YM3438 DEVICE_GET_INFO_NAME( ym3438 ) diff --git a/Frameworks/GME/vgmplay/chips/262intf.c b/Frameworks/GME/vgmplay/chips/262intf.c new file mode 100644 index 000000000..96cdb74a4 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/262intf.c @@ -0,0 +1,321 @@ +/*************************************************************************** + + 262intf.c + + MAME interface for YMF262 (OPL3) emulator + +***************************************************************************/ +#include "mamedef.h" +#include +//#include "attotime.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "262intf.h" +#ifdef ENABLE_ALL_CORES +#include "ymf262.h" +#endif + +#define OPLTYPE_IS_OPL3 +#include "adlibemu.h" + + +#define EC_DBOPL 0x00 // DosBox OPL (AdLibEmu) +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // YMF262 core from MAME +#endif + +typedef struct _ymf262_state ymf262_state; +struct _ymf262_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + int EMU_CORE; + //const ymf262_interface *intf; + //const device_config *device; +}; + + +/*INLINE ymf262_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YMF262); + return (ymf262_state *)device->token; +}*/ + + + + +static void IRQHandler_262(void *param,int irq) +{ + ymf262_state *info = (ymf262_state *)param; + //if (info->intf->handler) (info->intf->handler)(info->device, irq); +} + +/*static TIMER_CALLBACK( timer_callback_262_0 ) +{ + ymf262_state *info = (ymf262_state *)ptr; + ymf262_timer_over(info->chip, 0); +} + +static TIMER_CALLBACK( timer_callback_262_1 ) +{ + ymf262_state *info = (ymf262_state *)ptr; + ymf262_timer_over(info->chip, 1); +}*/ + +//static void timer_handler_262(void *param,int timer, attotime period) +static void timer_handler_262(void *param,int timer, int period) +{ + ymf262_state *info = (ymf262_state *)param; + if( period == 0 ) + { /* Reset FM Timer */ + //timer_enable(info->timer[timer], 0); + } + else + { /* Start FM Timer */ + //timer_adjust_oneshot(info->timer[timer], period, 0); + } +} + +//static STREAM_UPDATE( ymf262_stream_update ) +void ymf262_stream_update(void *param, stream_sample_t **outputs, int samples) +{ + ymf262_state *info = (ymf262_state *)param; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ymf262_update_one(info->chip, outputs, samples); + break; +#endif + case EC_DBOPL: + adlib_OPL3_getsample(info->chip, outputs, samples); + break; + } +} + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +static void _stream_update(void *param/*, int interval*/) +{ + ymf262_state *info = (ymf262_state *)param; + //stream_update(info->stream); + + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ymf262_update_one(info->chip, DUMMYBUF, 0); + break; +#endif + case EC_DBOPL: + adlib_OPL3_getsample(info->chip, DUMMYBUF, 0); + break; + } +} + + +//static DEVICE_START( ymf262 ) +int device_start_ymf262(void **_info, int EMU_CORE, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //static const ymf262_interface dummy = { 0 }; + //ymf262_state *info = get_safe_token(device); + ymf262_state *info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_DBOPL; +#else + EMU_CORE = EC_DBOPL; +#endif + + info = (ymf262_state *) calloc(1, sizeof(ymf262_state)); + *_info = (void *) info; + + info->EMU_CORE = EMU_CORE; + rate = clock/288; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + + //info->intf = device->static_config ? (const ymf262_interface *)device->static_config : &dummy; + //info->intf = &dummy; + //info->device = device; + + /* stream system initialize */ + switch(EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + info->chip = ymf262_init(clock,rate); + //assert_always(info->chip != NULL, "Error creating YMF262 chip"); + + //info->stream = stream_create(device,0,4,rate,info,ymf262_stream_update); + + /* YMF262 setup */ + ymf262_set_timer_handler (info->chip, timer_handler_262, info); + ymf262_set_irq_handler (info->chip, IRQHandler_262, info); + ymf262_set_update_handler(info->chip, _stream_update, info); + + //info->timer[0] = timer_alloc(device->machine, timer_callback_262_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_262_1, info); + break; +#endif + case EC_DBOPL: + info->chip = adlib_OPL3_init(clock, rate, _stream_update, info); + break; + } + + return rate; +} + +//static DEVICE_STOP( ymf262 ) +void device_stop_ymf262(void *_info) +{ + //ymf262_state *info = get_safe_token(device); + ymf262_state *info = (ymf262_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ymf262_shutdown(info->chip); + break; +#endif + case EC_DBOPL: + adlib_OPL3_stop(info->chip); + break; + } + free(info); +} + +/* reset */ +//static DEVICE_RESET( ymf262 ) +void device_reset_ymf262(void *_info) +{ + //ymf262_state *info = get_safe_token(device); + ymf262_state *info = (ymf262_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ymf262_reset_chip(info->chip); + break; +#endif + case EC_DBOPL: + adlib_OPL3_reset(info->chip); + break; + } +} + + +//READ8_DEVICE_HANDLER( ymf262_r ) +UINT8 ymf262_r(void *_info, offs_t offset) +{ + //ymf262_state *info = get_safe_token(device); + ymf262_state *info = (ymf262_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + return ymf262_read(info->chip, offset & 3); +#endif + case EC_DBOPL: + return adlib_OPL3_reg_read(info->chip, offset & 0x03); + default: + return 0x00; + } +} + +//WRITE8_DEVICE_HANDLER( ymf262_w ) +void ymf262_w(void *_info, offs_t offset, UINT8 data) +{ + //ymf262_state *info = get_safe_token(device); + ymf262_state *info = (ymf262_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ymf262_write(info->chip, offset & 3, data); + break; +#endif + case EC_DBOPL: + adlib_OPL3_writeIO(info->chip, offset & 3, data); + break; + } +} + +//READ8_DEVICE_HANDLER ( ymf262_status_r ) +UINT8 ymf262_status_r(void *info, offs_t offset) +{ + return ymf262_r(info, 0); +} +//WRITE8_DEVICE_HANDLER( ymf262_register_a_w ) +void ymf262_register_a_w(void *info, offs_t offset, UINT8 data) +{ + ymf262_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ymf262_register_b_w ) +void ymf262_register_b_w(void *info, offs_t offset, UINT8 data) +{ + ymf262_w(info, 2, data); +} +//WRITE8_DEVICE_HANDLER( ymf262_data_a_w ) +void ymf262_data_a_w(void *info, offs_t offset, UINT8 data) +{ + ymf262_w(info, 1, data); +} +//WRITE8_DEVICE_HANDLER( ymf262_data_b_w ) +void ymf262_data_b_w(void *info, offs_t offset, UINT8 data) +{ + ymf262_w(info, 3, data); +} + + +void ymf262_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ymf262_state *info = (ymf262_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ymf262_set_mutemask(info->chip, MuteMask); + break; +#endif + case EC_DBOPL: + adlib_OPL3_set_mute_mask(info->chip, MuteMask); + break; + } + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ymf262 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ymf262_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymf262 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ymf262 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ymf262 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YMF262"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + diff --git a/Frameworks/GME/vgmplay/chips/262intf.h b/Frameworks/GME/vgmplay/chips/262intf.h new file mode 100644 index 000000000..df8af1cf0 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/262intf.h @@ -0,0 +1,41 @@ +#pragma once + + +/*typedef struct _ymf262_interface ymf262_interface; +struct _ymf262_interface +{ + //void (*handler)(const device_config *device, int irq); + void (*handler)(int irq); +};*/ + + +/*READ8_DEVICE_HANDLER( ymf262_r ); +WRITE8_DEVICE_HANDLER( ymf262_w ); + +READ8_DEVICE_HANDLER ( ymf262_status_r ); +WRITE8_DEVICE_HANDLER( ymf262_register_a_w ); +WRITE8_DEVICE_HANDLER( ymf262_register_b_w ); +WRITE8_DEVICE_HANDLER( ymf262_data_a_w ); +WRITE8_DEVICE_HANDLER( ymf262_data_b_w ); + + +DEVICE_GET_INFO( ymf262 ); +#define SOUND_YMF262 DEVICE_GET_INFO_NAME( ymf262 )*/ + +void ymf262_stream_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ymf262(void **chip, int core, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ymf262(void *chip); +void device_reset_ymf262(void *chip); + +UINT8 ymf262_r(void *chip, offs_t offset); +void ymf262_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ymf262_status_r(void *chip, offs_t offset); +void ymf262_register_a_w(void *chip, offs_t offset, UINT8 data); +void ymf262_register_b_w(void *chip, offs_t offset, UINT8 data); +void ymf262_data_a_w(void *chip, offs_t offset, UINT8 data); +void ymf262_data_b_w(void *chip, offs_t offset, UINT8 data); + +void ymf262_set_mute_mask(void *chip, UINT32 MuteMask); + diff --git a/Frameworks/GME/vgmplay/chips/281btone.h b/Frameworks/GME/vgmplay/chips/281btone.h new file mode 100644 index 000000000..bfa3613d0 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/281btone.h @@ -0,0 +1,20 @@ +/* YMF281B tone by Chabin */ +0x49,0x4c,0x4c,0x32,0x00,0x00,0x00,0x00, +0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, +0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, +0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, +0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, +0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, +0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, +0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, +0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, +0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, +0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, +0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, +0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, +0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, +0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, \ No newline at end of file diff --git a/Frameworks/GME/vgmplay/chips/3526intf.c b/Frameworks/GME/vgmplay/chips/3526intf.c new file mode 100644 index 000000000..1d0cb0ddf --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/3526intf.c @@ -0,0 +1,225 @@ +/****************************************************************************** +* FILE +* Yamaha 3812 emulator interface - MAME VERSION +* +* CREATED BY +* Ernesto Corvi +* +* UPDATE LOG +* JB 28-04-2002 Fixed simultaneous usage of all three different chip types. +* Used real sample rate when resample filter is active. +* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. +* CHS 1999-01-09 Fixes new ym3812 emulation interface. +* CHS 1998-10-23 Mame streaming sound chip update +* EC 1998 Created Interface +* +* NOTES +* +******************************************************************************/ +#include "mamedef.h" +#include +//#include "attotime.h" +//#include "sndintrf.h" +//#include "streams.h" +//#include "cpuintrf.h" +#include "3526intf.h" +#include "fmopl.h" + +#include + +typedef struct _ym3526_state ym3526_state; +struct _ym3526_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + //const ym3526_interface *intf; + //const device_config *device; +}; + + +/*INLINE ym3526_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM3526); + return (ym3526_state *)device->token; +}*/ + + +/* IRQ Handler */ +static void IRQHandler(void *param,int irq) +{ + ym3526_state *info = (ym3526_state *)param; + //if (info->intf->handler) (info->intf->handler)(info->device, irq ? ASSERT_LINE : CLEAR_LINE); + //if (info->intf->handler) (info->intf->handler)(irq ? ASSERT_LINE : CLEAR_LINE); +} +/* Timer overflow callback from timer.c */ +/*static TIMER_CALLBACK( timer_callback_0 ) +{ + ym3526_state *info = (ym3526_state *)ptr; + ym3526_timer_over(info->chip,0); +} +static TIMER_CALLBACK( timer_callback_1 ) +{ + ym3526_state *info = (ym3526_state *)ptr; + ym3526_timer_over(info->chip,1); +}*/ +/* TimerHandler from fm.c */ +//static void TimerHandler(void *param,int c,attotime period) +static void TimerHandler(void *param,int c,int period) +{ + ym3526_state *info = (ym3526_state *)param; + //if( attotime_compare(period, attotime_zero) == 0 ) + if( period == 0 ) + { /* Reset FM Timer */ + //timer_enable(info->timer[c], 0); + } + else + { /* Start FM Timer */ + //timer_adjust_oneshot(info->timer[c], period, 0); + } +} + + +//static STREAM_UPDATE( ym3526_stream_update ) +void ym3526_stream_update(void *param, stream_sample_t **outputs, int samples) +{ + ym3526_state *info = (ym3526_state *)param; + ym3526_update_one(info->chip, outputs, samples); +} + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +static void _stream_update(void *param/*, int interval*/) +{ + ym3526_state *info = (ym3526_state *)param; + //stream_update(info->stream); + + ym3526_update_one(info->chip, DUMMYBUF, 0); +} + + +//static DEVICE_START( ym3526 ) +int device_start_ym3526(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //static const ym3526_interface dummy = { 0 }; + //ym3526_state *info = get_safe_token(device); + ym3526_state *info; + int rate; + + info = (ym3526_state *) calloc(1, sizeof(ym3526_state)); + *_info = (void *) info; + + rate = clock/72; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + //info->intf = device->static_config ? (const ym3526_interface *)device->static_config : &dummy; + //info->intf = &dummy; + //info->device = device; + + /* stream system initialize */ + info->chip = ym3526_init(clock,rate); + //assert_always(info->chip != NULL, "Error creating YM3526 chip"); + + //info->stream = stream_create(device,0,1,rate,info,ym3526_stream_update); + /* YM3526 setup */ + ym3526_set_timer_handler (info->chip, TimerHandler, info); + ym3526_set_irq_handler (info->chip, IRQHandler, info); + ym3526_set_update_handler(info->chip, _stream_update, info); + + //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); + + return rate; +} + +//static DEVICE_STOP( ym3526 ) +void device_stop_ym3526(void *_info) +{ + //ym3526_state *info = get_safe_token(device); + ym3526_state *info = (ym3526_state *)_info; + ym3526_shutdown(info->chip); + free(info); +} + +//static DEVICE_RESET( ym3526 ) +void device_reset_ym3526(void *_info) +{ + //ym3526_state *info = get_safe_token(device); + ym3526_state *info = (ym3526_state *)_info; + ym3526_reset_chip(info->chip); +} + + +//READ8_DEVICE_HANDLER( ym3526_r ) +UINT8 ym3526_r(void *_info, offs_t offset) +{ + //ym3526_state *info = get_safe_token(device); + ym3526_state *info = (ym3526_state *)_info; + return ym3526_read(info->chip, offset & 1); +} + +//WRITE8_DEVICE_HANDLER( ym3526_w ) +void ym3526_w(void *_info, offs_t offset, UINT8 data) +{ + //ym3526_state *info = get_safe_token(device); + ym3526_state *info = (ym3526_state *)_info; + ym3526_write(info->chip, offset & 1, data); +} + +//READ8_DEVICE_HANDLER( ym3526_status_port_r ) +UINT8 ym3526_status_port_r(void *info, offs_t offset) +{ + return ym3526_r(info, 0); +} +//READ8_DEVICE_HANDLER( ym3526_read_port_r ) +UINT8 ym3526_read_port_r(void *info, offs_t offset) +{ + return ym3526_r(info, 1); +} +//WRITE8_DEVICE_HANDLER( ym3526_control_port_w ) +void ym3526_control_port_w(void *info, offs_t offset, UINT8 data) +{ + ym3526_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ym3526_write_port_w ) +void ym3526_write_port_w(void *info, offs_t offset, UINT8 data) +{ + ym3526_w(info, 1, data); +} + + +void ym3526_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ym3526_state *info = (ym3526_state *)_info; + opl_set_mute_mask(info->chip, MuteMask); +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym3526 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym3526_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym3526 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym3526 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym3526 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM3526"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/3526intf.h b/Frameworks/GME/vgmplay/chips/3526intf.h new file mode 100644 index 000000000..d1100f601 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/3526intf.h @@ -0,0 +1,33 @@ +#pragma once + +/*typedef struct _ym3526_interface ym3526_interface; +struct _ym3526_interface +{ + //void (*handler)(const device_config *device, int linestate); + void (*handler)(int linestate); +};*/ + +/*READ8_DEVICE_HANDLER( ym3526_r ); +WRITE8_DEVICE_HANDLER( ym3526_w ); + +READ8_DEVICE_HANDLER( ym3526_status_port_r ); +READ8_DEVICE_HANDLER( ym3526_read_port_r ); +WRITE8_DEVICE_HANDLER( ym3526_control_port_w ); +WRITE8_DEVICE_HANDLER( ym3526_write_port_w ); + +DEVICE_GET_INFO( ym3526 ); +#define SOUND_YM3526 DEVICE_GET_INFO_NAME( ym3526 )*/ +void ym3526_stream_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_ym3526(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym3526(void *chip); +void device_reset_ym3526(void *chip); + +UINT8 ym3526_r(void *chip, offs_t offset); +void ym3526_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym3526_status_port_r(void *chip, offs_t offset); +UINT8 ym3526_read_port_r(void *chip, offs_t offset); +void ym3526_control_port_w(void *chip, offs_t offset, UINT8 data); +void ym3526_write_port_w(void *chip, offs_t offset, UINT8 data); + +void ym3526_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/3812intf.c b/Frameworks/GME/vgmplay/chips/3812intf.c new file mode 100644 index 000000000..e80b984db --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/3812intf.c @@ -0,0 +1,330 @@ +/****************************************************************************** +* FILE +* Yamaha 3812 emulator interface - MAME VERSION +* +* CREATED BY +* Ernesto Corvi +* +* UPDATE LOG +* JB 28-04-2002 Fixed simultaneous usage of all three different chip types. +* Used real sample rate when resample filter is active. +* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. +* CHS 1999-01-09 Fixes new ym3812 emulation interface. +* CHS 1998-10-23 Mame streaming sound chip update +* EC 1998 Created Interface +* +* NOTES +* +******************************************************************************/ +#include + +#include +#include "mamedef.h" +//#include "attotime.h" +//#include "sndintrf.h" +//#include "streams.h" +//#include "cpuintrf.h" +#include "3812intf.h" +#ifdef ENABLE_ALL_CORES +#include "fmopl.h" +#endif + +#define OPLTYPE_IS_OPL2 +#include "adlibemu.h" + +#define NULL ((void *)0) + + +#define EC_DBOPL 0x00 // DosBox OPL (AdLibEmu) +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // YM3826 core from MAME +#endif + +typedef struct _ym3812_state ym3812_state; +struct _ym3812_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + int EMU_CORE; + //const ym3812_interface *intf; + //const device_config *device; +}; + + +/*INLINE ym3812_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YM3812); + return (ym3812_state *)device->token; +}*/ + + + +static void IRQHandler(void *param,int irq) +{ + ym3812_state *info = (ym3812_state *)param; + //if (info->intf->handler) (info->intf->handler)(info->device, irq ? ASSERT_LINE : CLEAR_LINE); + //if (info->intf->handler) (info->intf->handler)(irq ? ASSERT_LINE : CLEAR_LINE); +} +/*static TIMER_CALLBACK( timer_callback_0 ) +{ + ym3812_state *info = (ym3812_state *)ptr; + ym3812_timer_over(info->chip,0); +} + +static TIMER_CALLBACK( timer_callback_1 ) +{ + ym3812_state *info = (ym3812_state *)ptr; + ym3812_timer_over(info->chip,1); +}*/ + +//static void TimerHandler(void *param,int c,attotime period) +static void TimerHandler(void *param,int c,int period) +{ + ym3812_state *info = (ym3812_state *)param; + //if( attotime_compare(period, attotime_zero) == 0 ) + if( period == 0 ) + { /* Reset FM Timer */ + //timer_enable(info->timer[c], 0); + } + else + { /* Start FM Timer */ + //timer_adjust_oneshot(info->timer[c], period, 0); + } +} + + +//static STREAM_UPDATE( ym3812_stream_update ) +void ym3812_stream_update(void *param, stream_sample_t **outputs, int samples) +{ + ym3812_state *info = (ym3812_state *)param; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym3812_update_one(info->chip, outputs, samples); + break; +#endif + case EC_DBOPL: + adlib_OPL2_getsample(info->chip, outputs, samples); + break; + } +} + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +static void _stream_update(void * param/*, int interval*/) +{ + ym3812_state *info = (ym3812_state *)param; + //stream_update(info->stream); + + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym3812_update_one(info->chip, DUMMYBUF, 0); + break; +#endif + case EC_DBOPL: + adlib_OPL2_getsample(info->chip, DUMMYBUF, 0); + break; + } +} + + +//static DEVICE_START( ym3812 ) +int device_start_ym3812(void **_info, int EMU_CORE, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //static const ym3812_interface dummy = { 0 }; + //ym3812_state *info = get_safe_token(device); + ym3812_state *info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_DBOPL; +#else + EMU_CORE = EC_DBOPL; +#endif + + info = (ym3812_state *) calloc(1, sizeof(ym3812_state)); + *_info = (void *) info; + + info->EMU_CORE = EMU_CORE; + rate = (clock & 0x7FFFFFFF)/72; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + //info->intf = device->static_config ? (const ym3812_interface *)device->static_config : &dummy; + //info->intf = &dummy; + //info->device = device; + + /* stream system initialize */ + switch(EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + info->chip = ym3812_init(clock & 0x7FFFFFFF,rate); + //assert_always(info->chip != NULL, "Error creating YM3812 chip"); + + //info->stream = stream_create(device,0,1,rate,info,ym3812_stream_update); + + /* YM3812 setup */ + ym3812_set_timer_handler (info->chip, TimerHandler, info); + ym3812_set_irq_handler (info->chip, IRQHandler, info); + ym3812_set_update_handler(info->chip, _stream_update, info); + + //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); + break; +#endif + case EC_DBOPL: + info->chip = adlib_OPL2_init(clock & 0x7FFFFFFF, rate, _stream_update, info); + break; + } + + return rate; +} + +//static DEVICE_STOP( ym3812 ) +void device_stop_ym3812(void *_info) +{ + //ym3812_state *info = get_safe_token(device); + ym3812_state *info = (ym3812_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym3812_shutdown(info->chip); + break; +#endif + case EC_DBOPL: + adlib_OPL2_stop(info->chip); + break; + } + free(info); +} + +//static DEVICE_RESET( ym3812 ) +void device_reset_ym3812(void *_info) +{ + //ym3812_state *info = get_safe_token(device); + ym3812_state *info = (ym3812_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym3812_reset_chip(info->chip); + break; +#endif + case EC_DBOPL: + adlib_OPL2_reset(info->chip); + break; + } +} + + +//READ8_DEVICE_HANDLER( ym3812_r ) +UINT8 ym3812_r(void *_info, offs_t offset) +{ + //ym3812_state *info = get_safe_token(device); + ym3812_state *info = (ym3812_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + return ym3812_read(info->chip, offset & 1); +#endif + case EC_DBOPL: + return adlib_OPL2_reg_read(info->chip, offset & 0x01); + default: + return 0x00; + } +} + +//WRITE8_DEVICE_HANDLER( ym3812_w ) +void ym3812_w(void *_info, offs_t offset, UINT8 data) +{ + //ym3812_state *info = get_safe_token(device); + ym3812_state *info = (ym3812_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ym3812_write(info->chip, offset & 1, data); + break; +#endif + case EC_DBOPL: + adlib_OPL2_writeIO(info->chip, offset & 1, data); + break; + } +} + +//READ8_DEVICE_HANDLER( ym3812_status_port_r ) +UINT8 ym3812_status_port_r(void *info, offs_t offset) +{ + return ym3812_r(info, 0); +} +//READ8_DEVICE_HANDLER( ym3812_read_port_r ) +UINT8 ym3812_read_port_r(void *info, offs_t offset) +{ + return ym3812_r(info, 1); +} +//WRITE8_DEVICE_HANDLER( ym3812_control_port_w ) +void ym3812_control_port_w(void *info, offs_t offset, UINT8 data) +{ + ym3812_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( ym3812_write_port_w ) +void ym3812_write_port_w(void *info, offs_t offset, UINT8 data) +{ + ym3812_w(info, 1, data); +} + + +void ym3812_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ym3812_state *info = (ym3812_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + opl_set_mute_mask(info->chip, MuteMask); + break; +#endif + case EC_DBOPL: + adlib_OPL2_set_mute_mask(info->chip, MuteMask); + break; + } + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ym3812 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym3812_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym3812 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym3812 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym3812 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YM3812"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/3812intf.h b/Frameworks/GME/vgmplay/chips/3812intf.h new file mode 100644 index 000000000..54172296d --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/3812intf.h @@ -0,0 +1,34 @@ +#pragma once + +/*typedef struct _ym3812_interface ym3812_interface; +struct _ym3812_interface +{ + //void (*handler)(const device_config *device, int linestate); + void (*handler)(int linestate); +};*/ + +/*READ8_DEVICE_HANDLER( ym3812_r ); +WRITE8_DEVICE_HANDLER( ym3812_w ); + +READ8_DEVICE_HANDLER( ym3812_status_port_r ); +READ8_DEVICE_HANDLER( ym3812_read_port_r ); +WRITE8_DEVICE_HANDLER( ym3812_control_port_w ); +WRITE8_DEVICE_HANDLER( ym3812_write_port_w ); + +DEVICE_GET_INFO( ym3812 ); +#define SOUND_YM3812 DEVICE_GET_INFO_NAME( ym3812 )*/ + +void ym3812_stream_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_ym3812(void **chip, int core, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ym3812(void *chip); +void device_reset_ym3812(void *chip); + +UINT8 ym3812_r(void *chip, offs_t offset); +void ym3812_w(void *chip, offs_t offset, UINT8 data); + +UINT8 ym3812_status_port_r(void *chip, offs_t offset); +UINT8 ym3812_read_port_r(void *chip, offs_t offset); +void ym3812_control_port_w(void *chip, offs_t offset, UINT8 data); +void ym3812_write_port_w(void *chip, offs_t offset, UINT8 data); + +void ym3812_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/8950intf.c b/Frameworks/GME/vgmplay/chips/8950intf.c new file mode 100644 index 000000000..02770da4a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/8950intf.c @@ -0,0 +1,275 @@ +/****************************************************************************** +* FILE +* Yamaha 3812 emulator interface - MAME VERSION +* +* CREATED BY +* Ernesto Corvi +* +* UPDATE LOG +* JB 28-04-2002 Fixed simultaneous usage of all three different chip types. +* Used real sample rate when resample filter is active. +* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers. +* CHS 1999-01-09 Fixes new ym3812 emulation interface. +* CHS 1998-10-23 Mame streaming sound chip update +* EC 1998 Created Interface +* +* NOTES +* +******************************************************************************/ +#include "mamedef.h" +#include +//#include "attotime.h" +//#include "sndintrf.h" +//#include "streams.h" +//#include "cpuintrf.h" +#include "8950intf.h" +//#include "fm.h" +#include "fmopl.h" + +#include + +#define NULL ((void *)0) + + +typedef struct _y8950_state y8950_state; +struct _y8950_state +{ + //sound_stream * stream; + //emu_timer * timer[2]; + void * chip; + //const y8950_interface *intf; + //const device_config *device; +}; + + +/*INLINE y8950_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_Y8950); + return (y8950_state *)device->token; +}*/ + + +static void IRQHandler(void *param,int irq) +{ + y8950_state *info = (y8950_state *)param; + //if (info->intf->handler) (info->intf->handler)(info->device, irq ? ASSERT_LINE : CLEAR_LINE); + //if (info->intf->handler) (info->intf->handler)(irq ? ASSERT_LINE : CLEAR_LINE); +} +/*static TIMER_CALLBACK( timer_callback_0 ) +{ + y8950_state *info = (y8950_state *)ptr; + y8950_timer_over(info->chip,0); +} +static TIMER_CALLBACK( timer_callback_1 ) +{ + y8950_state *info = (y8950_state *)ptr; + y8950_timer_over(info->chip,1); +}*/ +//static void TimerHandler(void *param,int c,attotime period) +static void TimerHandler(void *param,int c,int period) +{ + y8950_state *info = (y8950_state *)param; + //if( attotime_compare(period, attotime_zero) == 0 ) + if( period == 0 ) + { /* Reset FM Timer */ + //timer_enable(info->timer[c], 0); + } + else + { /* Start FM Timer */ + //timer_adjust_oneshot(info->timer[c], period, 0); + } +} + + +static unsigned char Y8950PortHandler_r(void *param) +{ + y8950_state *info = (y8950_state *)param; + /*if (info->intf->portread) + return info->intf->portread(0);*/ + return 0; +} + +static void Y8950PortHandler_w(void *param,unsigned char data) +{ + y8950_state *info = (y8950_state *)param; + /*if (info->intf->portwrite) + info->intf->portwrite(0,data);*/ +} + +static unsigned char Y8950KeyboardHandler_r(void *param) +{ + y8950_state *info = (y8950_state *)param; + /*if (info->intf->keyboardread) + return info->intf->keyboardread(0);*/ + return 0; +} + +static void Y8950KeyboardHandler_w(void *param,unsigned char data) +{ + y8950_state *info = (y8950_state *)param; + /*if (info->intf->keyboardwrite) + info->intf->keyboardwrite(0,data);*/ +} + +//static STREAM_UPDATE( y8950_stream_update ) +void y8950_stream_update(void *param, stream_sample_t **outputs, int samples) +{ + y8950_state *info = (y8950_state *)param; + y8950_update_one(info->chip, outputs, samples); +} + +static stream_sample_t* DUMMYBUF[0x02] = {NULL, NULL}; + +static void _stream_update(void *param/*, int interval*/) +{ + y8950_state *info = (y8950_state *)param; + //stream_update(info->stream); + + y8950_update_one(info->chip, DUMMYBUF, 0); +} + + +//static DEVICE_START( y8950 ) +int device_start_y8950(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //static const y8950_interface dummy = { 0 }; + //y8950_state *info = get_safe_token(device); + y8950_state *info; + int rate; + + info = (y8950_state *) calloc(1, sizeof(y8950_state)); + *_info = (void *) info; + + rate = clock/72; + if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + //info->intf = device->static_config ? (const y8950_interface *)device->static_config : &dummy; + //info->intf = &dummy; + //info->device = device; + + /* stream system initialize */ + info->chip = y8950_init(clock,rate); + //assert_always(info->chip != NULL, "Error creating Y8950 chip"); + + /* ADPCM ROM data */ + //y8950_set_delta_t_memory(info->chip, device->region, device->regionbytes); + y8950_set_delta_t_memory(info->chip, NULL, 0x00); + + //info->stream = stream_create(device,0,1,rate,info,y8950_stream_update); + + /* port and keyboard handler */ + y8950_set_port_handler(info->chip, Y8950PortHandler_w, Y8950PortHandler_r, info); + y8950_set_keyboard_handler(info->chip, Y8950KeyboardHandler_w, Y8950KeyboardHandler_r, info); + + /* Y8950 setup */ + y8950_set_timer_handler (info->chip, TimerHandler, info); + y8950_set_irq_handler (info->chip, IRQHandler, info); + y8950_set_update_handler(info->chip, _stream_update, info); + + //info->timer[0] = timer_alloc(device->machine, timer_callback_0, info); + //info->timer[1] = timer_alloc(device->machine, timer_callback_1, info); + + return rate; +} + +//static DEVICE_STOP( y8950 ) +void device_stop_y8950(void *_info) +{ + //y8950_state *info = get_safe_token(device); + y8950_state *info = (y8950_state *)_info; + y8950_shutdown(info->chip); + free(info); +} + +//static DEVICE_RESET( y8950 ) +void device_reset_y8950(void *_info) +{ + //y8950_state *info = get_safe_token(device); + y8950_state *info = (y8950_state *)_info; + y8950_reset_chip(info->chip); +} + + +//READ8_DEVICE_HANDLER( y8950_r ) +UINT8 y8950_r(void *_info, offs_t offset) +{ + //y8950_state *info = get_safe_token(device); + y8950_state *info = (y8950_state *)_info; + return y8950_read(info->chip, offset & 1); +} + +//WRITE8_DEVICE_HANDLER( y8950_w ) +void y8950_w(void *_info, offs_t offset, UINT8 data) +{ + //y8950_state *info = get_safe_token(device); + y8950_state *info = (y8950_state *)_info; + y8950_write(info->chip, offset & 1, data); +} + +//READ8_DEVICE_HANDLER( y8950_status_port_r ) +UINT8 y8950_status_port_r(void *info, offs_t offset) +{ + return y8950_r(info, 0); +} +//READ8_DEVICE_HANDLER( y8950_read_port_r ) +UINT8 y8950_read_port_r(void *info, offs_t offset) +{ + return y8950_r(info, 1); +} +//WRITE8_DEVICE_HANDLER( y8950_control_port_w ) +void y8950_control_port_w(void *info, offs_t offset, UINT8 data) +{ + y8950_w(info, 0, data); +} +//WRITE8_DEVICE_HANDLER( y8950_write_port_w ) +void y8950_write_port_w(void *info, offs_t offset, UINT8 data) +{ + y8950_w(info, 1, data); +} + + +void y8950_write_data_pcmrom(void *_info, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData) +{ + y8950_state* info = (y8950_state *)_info; + + y8950_write_pcmrom(info->chip, ROMSize, DataStart, DataLength, ROMData); + + return; +} + +void y8950_set_mute_mask(void *_info, UINT32 MuteMask) +{ + y8950_state *info = (y8950_state *)_info; + opl_set_mute_mask(info->chip, MuteMask); +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( y8950 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(y8950_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( y8950 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( y8950 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( y8950 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "Y8950"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/8950intf.h b/Frameworks/GME/vgmplay/chips/8950intf.h new file mode 100644 index 000000000..b00316242 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/8950intf.h @@ -0,0 +1,40 @@ +#pragma once + +/*typedef struct _y8950_interface y8950_interface; +struct _y8950_interface +{ + //void (*handler)(const device_config *device, int linestate); + void (*handler)(int linestate); + + read8_device_func keyboardread; + write8_device_func keyboardwrite; + read8_device_func portread; + write8_device_func portwrite; +};*/ + +/*READ8_DEVICE_HANDLER( y8950_r ); +WRITE8_DEVICE_HANDLER( y8950_w ); + +READ8_DEVICE_HANDLER( y8950_status_port_r ); +READ8_DEVICE_HANDLER( y8950_read_port_r ); +WRITE8_DEVICE_HANDLER( y8950_control_port_w ); +WRITE8_DEVICE_HANDLER( y8950_write_port_w ); + +DEVICE_GET_INFO( y8950 ); +#define SOUND_Y8950 DEVICE_GET_INFO_NAME( y8950 )*/ +void y8950_stream_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_y8950(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_y8950(void *chip); +void device_reset_y8950(void *chip); + +UINT8 y8950_r(void *chip, offs_t offset); +void y8950_w(void *chip, offs_t offset, UINT8 data); + +UINT8 y8950_status_port_r(void *chip, offs_t offset); +UINT8 y8950_read_port_r(void *chip, offs_t offset); +void y8950_control_port_w(void *chip, offs_t offset, UINT8 data); +void y8950_write_port_w(void *chip, offs_t offset, UINT8 data); + +void y8950_write_data_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData); +void y8950_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/ChipIncl.h b/Frameworks/GME/vgmplay/chips/ChipIncl.h new file mode 100644 index 000000000..64e739b54 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ChipIncl.h @@ -0,0 +1,44 @@ +// includes all chip-headers + +#include "sn764intf.h" +#include "2413intf.h" +#include "2612intf.h" +#include "2151intf.h" +#include "segapcm.h" +#include "rf5c68.h" +#include "2203intf.h" +#include "2608intf.h" +#include "2610intf.h" +#include "3812intf.h" +#include "3526intf.h" +#include "8950intf.h" +#include "262intf.h" +#include "ymz280b.h" +#include "ymf271.h" +#include "ymf278b.h" +#include "scd_pcm.h" +#include "pwm.h" +#include "ay_intf.h" +#include "dac_control.h" +#include "gb.h" +#include "nes_intf.h" +#include "multipcm.h" +#include "upd7759.h" +#include "okim6258.h" +#include "okim6295.h" +#include "k051649.h" +#include "k054539.h" +#include "c6280intf.h" +#include "c140.h" +#include "k053260.h" +#include "pokey.h" +#include "qsound.h" +#include "scsp.h" +#include "ws_audio.h" +#include "vsu.h" +#include "saa1099.h" +#include "es5503.h" +#include "es5506.h" +#include "x1_010.h" +#include "c352.h" +#include "iremga20.h" diff --git a/Frameworks/GME/vgmplay/chips/Ootake_PSG.c b/Frameworks/GME/vgmplay/chips/Ootake_PSG.c new file mode 100644 index 000000000..6b3d94ffb --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/Ootake_PSG.c @@ -0,0 +1,1055 @@ +/****************************************************************************** +Ootake +・キューã®å‚照処ç†ã‚’シンプルã«ã—ãŸã€‚テンãƒã®å®‰å®šæ€§ãŠã‚ˆã³éŸ³è³ªã®å‘上。 +・オーãƒãƒ¼ã‚µãƒ³ãƒ—リングã—ãªã„よã†ã«ã—ãŸã€‚(筆者ã®ä¸»è¦³ã‚‚ã‚ã‚‹ãŒã€PSGã®å ´åˆã€éŸ¿ãã® + 美ã—ã•ãŒæãªã‚れã¦ã—ã¾ã†ã‚±ãƒ¼ã‚¹ãŒå¤šã„ãŸã‚。速度的ã«ã‚‚アップ) +・ノイズã®éŸ³è³ªãƒ»éŸ³é‡ã‚’実機並ã¿ã«èª¿æ•´ã—ãŸã€‚v0.72 +・ノイズã®å‘¨æ³¢æ•°ã«0x1FãŒæ›¸ãè¾¼ã¾ã‚ŒãŸã¨ãã¯ã€0x1Eã¨åŒã˜å‘¨æ³¢æ•°ã§éŸ³é‡ã‚’åŠåˆ†ã«ã—㦠+ 鳴らã™ã‚ˆã†ã«ã—ãŸã€‚v0.68 +・ç¾çжã¯å†ç”Ÿã‚µãƒ³ãƒ—ルレートã¯44.1KHz固定ã¨ã—ãŸã€‚(CD-DAå†ç”Ÿæ™‚ã®é€Ÿåº¦ã‚¢ãƒƒãƒ—ã®ãŸã‚) +・DDA音ã®ç™ºå£°ãŒçµ‚了ã—ãŸã¨ãã«ã„ããªã‚Šæ³¢å½¢ã‚’0ã«ã›ãšã€ãƒ•ェードアウトã•ã›ã‚‹ã‚ˆã†ã« + ã—ã€ãƒŽã‚¤ã‚ºã‚’軽減ã—ãŸã€‚v0.57 +・DDAモード(サンプリング発声)ã®ã¨ãã®æ³¢å½¢ãƒ‡ãƒ¼ã‚¿ã®ãƒŽã‚¤ã‚ºãŒå¤šãå«ã¾ã‚Œã¦ã„る部分 + をカットã—ã—ã¦ã€éŸ³è³ªã‚’上ã’ãŸã€‚音é‡ã‚‚調節ã—ãŸã€‚v0.59 +・ノイズ音ã®éŸ³è³ªãƒ»éŸ³é‡ã‚’調整ã—ã¦ã€å®Ÿæ©Ÿã®é›°å›²æ°—ã«è¿‘ã¥ã‘ãŸã€‚v0.68 +・waveIndexã®åˆæœŸåŒ–ã¨DDAモード時ã®å‹•作を見直ã—ã¦å®Ÿæ©Ÿã®å‹•作ã«è¿‘ã¥ã‘ãŸã€‚v0.63 +・waveIndexã®åˆæœŸåŒ–時ã«waveãƒ†ãƒ¼ãƒ–ãƒ«ã‚‚åˆæœŸåŒ–ã™ã‚‹ã‚ˆã†ã«ã—ãŸã€‚ファイヤープロレス + リングã€ï¼¦ï¼‘トリプルãƒãƒˆãƒ«ãªã©ã®éŸ³ãŒå®Ÿæ©Ÿã«è¿‘ã¥ã„ãŸã€‚v0.65 +・waveã®æ³¢å½¢ã®æ­£è² ã‚’å®Ÿæ©ŸåŒæ§˜ã«ã—ãŸã€‚v0.74 +・waveã®æœ€å°å€¤ãŒ-14ã«ãªã‚‹ã‚ˆã†ã«ã—音質を整ãˆãŸã€‚v0.74 +・クリティカルセクションã¯å¿…è¦ãªã„(書ãè¾¼ã¿ãŒåŒæ™‚ã«è¡Œã‚れるã‚ã‘ã§ã¯ãªã„)よã†ãª + ã®ã§ã€çœç•¥ã—高速化ã—ãŸã€‚v1.09 +・キュー処ç†(ApuQueue.c)ã‚’ã“ã“ã«çµ±åˆã—ã¦é«˜é€ŸåŒ–ã—ãŸã€‚v1.10 +・低音領域ã®ãƒœãƒªãƒ¥ãƒ¼ãƒ ã‚’上ã’ã¦å®Ÿæ©Ÿä¸¦ã¿ã®èžã“ãˆã‚„ã™ã•ã«è¿‘ã¥ã‘ãŸã€‚v1.46 +・LFO処ç†ã®ã®å®Ÿè£…。"ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„"ã®OPã‚„ã€ãƒ•ラッシュãƒã‚¤ãƒ€ãƒ¼ã‚¹ã®åŠ¹æžœéŸ³ãŒ + 実機ã®éŸ³ã«è¿‘ã¥ã„ãŸã€‚v1.59 + +Copyright(C)2006-2012 Kitao Nakamura. + 改造版・後継版を公開ãªã•ã‚‹ã¨ãã¯å¿…ãšã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’添付ã—ã¦ãã ã•ã„。 + ãã®éš›ã«äº‹å¾Œã§ã‹ã¾ã„ã¾ã›ã‚“ã®ã§ã€ã²ã¨ã“ã¨ãŠçŸ¥ã‚‰ã›ã„ãŸã ã‘ã‚‹ã¨å¹¸ã„ã§ã™ã€‚ + 商的ãªåˆ©ç”¨ã¯ç¦ã˜ã¾ã™ã€‚ + ã‚ã¨ã¯ã€ŒGNU General Public License(一般公衆利用許諾契約書)ã€ã«æº–ã˜ã¾ã™ã€‚ + +******************************************************************************* + [PSG.c] + PSGを実装ã—ã¾ã™ã€‚ + + Implements the PSG. + + Copyright (C) 2004 Ki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +******************************************************************************/ +#include +#include +#include +#include +#include "mamedef.h" +#include "Ootake_PSG.h" +//#include "MainBoard.h" //Kitao追加 +//#include "App.h" //Kitao追加 +//#include "PRINTF.h" //Kitao追加 +//#define PRINTF printf + + +#define N_CHANNEL 6 + +//#define SAMPLE_RATE 44100.0 //Kitao更新。ç¾çжã¯é€Ÿåº¦å„ªå…ˆã§ã‚µãƒ³ãƒ—ルレートを44100固定ã¨ã—ãŸã€‚ +#define OVERSAMPLE_RATE 1.0 //Kitao更新。PSGã¯ã‚ªãƒ¼ãƒãƒ¼ã‚µãƒ³ãƒ—リングã™ã‚‹ã¨éŸ¿ãã®ç¾Žã—ã•ãŒæãªã‚れã¦ã—ã¾ã†ã®ã§ã‚ªãƒ¼ãƒãƒ¼ã‚µãƒ³ãƒ—リングã—ãªã„よã†ã«ã—ãŸã€‚速度的ã«ã‚‚アップ。 +#define PSG_DECLINE (21.8500*6.0) //21.8500。Kitao追加。PSG音é‡ã®æ¸›å°‘値。*6.0ã¯å„ãƒãƒ£ãƒ³ãƒãƒ«è¶³ã—ãŸã¶ã‚“を割るæ„味。大ãã„ã»ã©éŸ³ã¯æ¸›ã‚‹ã€‚CDDAãŒ100%ã®ã¨ãã«ã¡ã‚‡ã†ã©è‰¯ã„ãらã„ã®éŸ³é‡ã«åˆã‚ã›ã‚ˆã†ã€‚v2.19,v2.37,v2.39,v2.62æ›´æ–° +#define VOL_TABLE_DECLINE -1.05809999010 //-1.05809999010ã§é›€æŽ¢ç‰©èªžï¼’OK。Kitao追加。音é‡ãƒ†ãƒ¼ãƒ–ãƒ«ã®æ¸›å°‘値。マイナスãŒå¤§ãã„ã»ã©å°ã•ã„音ãŒèžã“ãˆã¥ã‚‰ããªã‚‹ã€‚マイナスãŒå°ã•ã™ãŽã‚‹ã¨å¹³é¢çš„ãªéŸ³ã«ãªã‚‹ã€‚v2.19,v2.37,v2.39,v2.40,v2.62,v2.65æ›´æ–° + // ※PSG_DECLINEã®å€¤ã‚’変更ã—ãŸå ´åˆã€æ¸›é€€çއã®ãƒ™ã‚¹ãƒˆå€¤ã‚‚変更ã™ã‚‹å¿…è¦ãŒã‚る。雀探物語2(マイナスãŒå°ã•ã„ã¨PSGãŒç›®ç«‹ã¡ã™ãŽã¦ADPCMãŒè´ãã¥ã‚‰ã„),大魔界æ‘(マイナスãŒå¤§ãã„ã¨éŸ³ç¯­ã‚Š),ソルジャーブレイドã§ã€PSG_DECLINE=(14.4701*6.0)ã§æ¸›é€€çއ-1.0498779900dbå‰å¾ŒãŒé£›ã³æŠœã‘ã¦ã„ã„響ã(ã†ã¡ã®ç’°å¢ƒã§ä¸»è¦³)。 + // モトローダー(マイナスやや大ãç›®ãŒã„ã„),1941(マイナスå°ã•ã‚ãŒã„ã„)ãªã©ã‚‚微妙ãªå€¤å¤‰æ›´ã§å¤§ãã変ã‚る。 +#define NOISE_TABLE_VALUE -18 : -1 //キレã¨è´ãã‚„ã™ã•ã§-18:-1をベストã¨ã—ãŸã€‚最大値ãŒå¤§ãã„(+ã«è¿‘ã„)ã¨é‡ã„音ã«ã€‚ï¼’ã¤ã®å€¤ãŒé›¢ã‚Œã¦ã„ã‚‹ã¨é‡ã„音ã«ã€‚フォーメーションサッカー,大魔界æ‘ã®ã‚¨ãƒ³ãƒ‡ã‚£ãƒ³ã‚°ã®ãƒ‰ãƒ©ãƒ ãªã©ã§èª¿æ•´ã€‚v1.46,v2.40,v2.62æ›´æ–° + // ※VOL_TABLE_DECLINEã«ã‚ˆã£ã¦ã“ã®å€¤ã®æœ€é©å€¤ã‚‚変化ã™ã‚‹ã€‚ +#define SAMPLE_FADE_DECLINE 0.305998999951 //0.30599899951。Kitaoè¿½åŠ ã€‚ã‚µãƒ³ãƒ—ãƒªãƒ³ã‚°éŸ³ã®æ¶ˆéŸ³æ™‚ã®éŸ³ã®æ¸›é€€é‡ã€‚ソルジャーブレイド,将棋åˆå¿ƒè€…無用ã®éŸ³å£°ã§èª¿æ•´ã€‚基本的ã«ã“ã®å€¤ãŒå°ã•ã„ã»ã†ãŒãƒŽã‚¤ã‚ºãŒæ¸›ã‚‹(逆ã®ã‚±ãƒ¼ã‚¹ã‚‚ã‚ã‚‹)。v2.40 + // サンプリングドラムã®éŸ³è‰²ãŒæ±ºã¾ã‚‹ã®ã§å¤§äº‹ãªå€¤ã€‚値ãŒå¤§ãã™ãŽã‚‹ã¨ãƒ•ァイナルソルジャーやソルジャーブレイド,モトローダーãªã©ã§ãƒ‰ãƒ©ãƒ ãŒã—ょã¼ããªã‚‹ã€‚ + +//#define RESMPL_RATE PSG_FRQ / OVERSAMPLE_RATE / SAMPLE_RATE // the lack of () is intentional + + +/*----------------------------------------------------------------------------- + [DEV NOTE] + + MAL --- 0 - 15 (15 ã§ -0[dB], 1減るã”ã¨ã« -3.0 [dB]) + AL --- 0 - 31 (31 ã§ -0[dB], 1減るã”ã¨ã« -1.5 [dB]) + LAL/RAL --- 0 - 15 (15 ã§ -0[dB], 1減るã”ã¨ã« -3.0 [dB]) + + 次ã®ã‚ˆã†ã«è§£é‡ˆã—ãªãŠã™ã€‚ + + MAL*2 --- 0 - 30 (30 ã§ -0[dB], 1減るã”ã¨ã« -1.5 [dB]) + AL --- 0 - 31 (31 ã§ -0[dB], 1減るã”ã¨ã« -1.5 [dB]) + LAL/RAL*2 --- 0 - 30 (30 ã§ -0[dB], 1減るã”ã¨ã« -1.5 [dB]) + + + dB = 20 * log10(OUT/IN) + + dB / 20 = log10(OUT/IN) + + OUT/IN = 10^(dB/20) + + IN(最大出力) ã‚’ 1.0 ã¨ã™ã‚‹ã¨ã€ + + OUT = 10^(dB/20) + + -91 <= -(MAL*2 + AL + LAL(RAL)*2) <= 0 + + ã ã‹ã‚‰ã€æœ€ã‚‚å°ã•ã„音ã¯ã€ + + -91 * 1.5 [dB] = -136.5 [dB] = 10^(-136.5/20) ~= 1.496236e-7 [å€] + + ã¨ãªã‚‹ã€‚ + + 1e-7 オーダーã®å€¤ã¯ã€å›ºå®šå°æ•°ç‚¹ã§è¡¨ç¾ã—よã†ã¨ã™ã‚‹ã¨ã€å°æ•°éƒ¨ã ã‘ã§ + 24 ビット以上必è¦ã§ã€ãªãŠã‹ã¤ï¼‘6ビットã®éŸ³å£°ã‚’扱ã†ãŸã‚ã«ã¯ +16ビット + ã ã‹ã‚‰ 24+16 = 40ビット以上必è¦ã«ãªã‚‹ã€‚よã£ã¦ã€32 ビットã®å‡¦ç†ç³»ã§ + PCEã®éŸ³å£°ã‚’å›ºå®šå°æ•°ç‚¹ã§è¡¨ç¾ã™ã‚‹ã®ã¯ã¤ã‚‰ã„。ãã“ã§ã€æ³¢å½¢ã®è¨ˆç®—㯠+ float ã§è¡Œãªã†ã“ã¨ã«ã™ã‚‹ã€‚ + + float ã‹ã‚‰å‡ºåЛ形å¼ã«å¤‰æ›ã™ã‚‹ã®ã¯ï¼¡ï¼°ï¼µã®ä»•事ã¨ã™ã‚‹ã€‚ + + [2004.4.28] ã‚„ã£ã±ã‚Š Sint32 ã§å®Ÿè£…ã™ã‚‹ã“ã¨ã«ã—ãŸ(å¾®å°ãªå€¤ã¯ç„¡è¦–ã™ã‚‹)。 + + CPUã¨ï¼°ï¼³ï¼§ã¯åŒã˜ï¼©ï¼£ã«ãƒ‘ッケージã—ã¦ã‚ã‚‹ã®ã ãŒã€ + 実際ã«ã¯ï¼°ï¼³ï¼§ã¯ï¼£ï¼°ï¼µã®ï¼‘ï¼ï¼’ã®ã‚¯ãƒ­ãƒƒã‚¯ã§å‹•作ã™ã‚‹ã¨è€ƒãˆã¦è‰¯ã„よã†ã ã€‚ + よã£ã¦ã€ï¼°ï¼³ï¼§ã®å‹•作周波数 Fpsg ã¯ã€ + + Fpsg = 21.47727 [MHz] / 3 / 2 = 3.579545 [MHz] + + ã¨ãªã‚‹ã€‚ + + ãŸã¨ãˆã°ï¼“2サンプルを1周期ã¨ã™ã‚‹æ³¢å½¢ãŒå†ç”Ÿã•れるã¨ã〠+ ã“ã®å‘¨æ³¢æ•°ã®å‘¨æœŸã§ã‚µãƒ³ãƒ—ルを1ã¤ãšã¤æ‹¾ã„出ã™ã¨ã€ + + M = 3579545 / 32 = 111860.78125 [Hz] + + ã¨ã„ã†ãƒžã‚¸ãƒƒã‚¯ãƒŠãƒ³ãƒãƒ¼ãŒå¾—られる(ファミコンã¨åŒã˜ï¼‰ã€‚ + ãŸã ã—ã€å†ç”Ÿå‘¨æ³¢æ•°ãŒå›ºå®šã§ã¯æ›²ã®æ¼”å¥ãŒã§ããªã„ã®ã§ã€ + FRQ ãªã‚‹å‘¨æ³¢æ•°ãƒ‘ラメータを用ã„ã¦å†ç”Ÿå‘¨æ³¢æ•°ã‚’変化ã•ã›ã‚‹ã€‚ + FRQ ã¯ï¼°ï¼³ï¼§ã®ãƒ¬ã‚¸ã‚¹ã‚¿ã«æ›¸ãè¾¼ã¾ã‚Œã‚‹ï¼‘2ビット長ã®ãƒ‘ラメータã§ã€ + ↑ã§å¾—られãŸãƒžã‚¸ãƒƒã‚¯ãƒŠãƒ³ãƒãƒ¼ã®ã€Œå‰²ã‚‹æ•°ã€ã«ãªã£ã¦ã„る。 + + 上ã®ï¼“2サンプルを1周期ã¨ã™ã‚‹æ³¢å½¢ãŒå†ç”Ÿã•れるã¨ã〠+ ã“ã®æ³¢å½¢ã®å‘¨æ³¢æ•° F ã¯ã€FRQ を用ã„ã¦ã€ + + F = M / FRQ [Hz] (FRQ != 0) + + ã¨ãªã‚‹ã€‚ + + ï¼°ï¼£ã®å†ç”Ÿã‚µãƒ³ãƒ—リング周波数㌠Fpc [Hz] ã ã¨ã™ã‚‹ã¨ã€ + ï¼‘å‘¨æœŸï¼“ï¼’ã‚µãƒ³ãƒ—ãƒ«ã®æ³¢å½¢ã®å†ç”Ÿå‘¨æ³¢æ•° F2 㯠F2 = Fpc / 32 [Hz]。 + よã£ã¦ã€ï¼°ï¼£ã®ï¼‘サンプルã«å¯¾ã—ã¦ã€ï¼°ï¼£ï¼¥ã®ï¼‘サンプルを拾ã„出㙠+ カウンタã®é€²ã¿å¹… I 㯠+ + I = F / F2 = 32 * F / Fpc = Fpsg / FRQ / Fpc [å˜ä½ãªã—] + + ã¨ãªã‚‹ã€‚ + + [NOISE CHANNEL] + + 擬似ノイズã®ç”Ÿæˆã«ã¯ï¼­ç³»åˆ—(maximum length sequence)ãŒç”¨ã„られる。 + M系列ã®ãƒ“ãƒƒãƒˆé•·ã¯æœªèª¿æŸ»ã«ã¤ã䏿˜Žã€‚ + ã“ã“ã§ã¯ä»®ã«ï¼‘5ビットã¨ã—ã¦å®Ÿè£…を行ãªã†ã€‚ + 出力ã¯ï¼‘ビットã§ã€D0 ãŒã‚¼ãƒ­ã®ã¨ãã¯è² ã®å€¤ã€ï¼‘ã®ã¨ãã¯æ­£ã®å€¤ã¨ã™ã‚‹ã€‚ + + ï¼°ï¼£ã®ï¼‘サンプルã«å¯¾ã—ã¦ã€ï¼°ï¼£ï¼¥ã®ï¼‘サンプルを拾ã„出㙠+ カウンタã®é€²ã¿å¹… I ã¯ã€ + + I = Fpsg / 64 / FRQ / Fpc (FRQ != 0) + + ã¨ãªã‚‹ã€‚ + + [å†ç”Ÿã‚¯ã‚ªãƒªãƒ†ã‚£å‘上ã«ã¤ã„ã¦] 2004.6.22 + + エミュレータã§ã¯ã€ï¼°ï¼³ï¼§ã®ãƒ¬ã‚¸ã‚¹ã‚¿ã«ãƒ‡ãƒ¼ã‚¿ãŒæ›¸ãè¾¼ã¾ã‚Œã‚‹ã¾ã§ã€ + 次ã«ç™ºå£°ã™ã¹ã音ãŒã‚ã‹ã‚‰ãªã„。レジスタã«ãƒ‡ãƒ¼ã‚¿ãŒæ›¸ãè¾¼ã¾ã‚ŒãŸã¨ãã«ã€ + サウンドãƒãƒƒãƒ•ã‚¡ã‚’æ›´æ–°ã—ãŸã„ã®ã ã‘ã©ã€ã‚ã„ã«ãç¾åœ¨ã®å®Ÿè£…ã§ã¯ã€ + サウンドãƒãƒƒãƒ•ã‚¡ã®æ›´æ–°ã¯åˆ¥ã‚¹ãƒ¬ãƒƒãƒ‰ã§è¡Œãªã‚れã¦ã„ã¦ã€ + エミュレーションスレッドã‹ã‚‰ä»»æ„ã®æ™‚é–“ã«æ›´æ–°ã™ã‚‹ã“ã¨ãŒã§ããªã„。 + + ã“れã¾ã§ã®å†ç”Ÿã§ã¯ã€ã‚µã‚¦ãƒ³ãƒ‰ãƒãƒƒãƒ•ã‚¡ã®æ›´æ–°æ™‚ã®ãƒ¬ã‚¸ã‚¹ã‚¿è¨­å®šã®ã¿ãŒ + 有効ã ã£ãŸãŒã€ã“れã ã¨ä¾‹ãˆã°ã‚µã‚¦ãƒ³ãƒ‰ãƒãƒƒãƒ•ã‚¡æ›´æ–°ã®åˆé–“ã«ä¸€çž¬ã ã‘ + 出力ã•れãŸéŸ³ãªã©ãŒç„¡è¦–ã•れã¦ã—ã¾ã†ã€‚ã“れã¯ç‰¹ã«ï¼¤ï¼¤ï¼¡ãƒ¢ãƒ¼ãƒ‰ã‚„ノイズ㌠+ リズムパートã¨ã—ã¦ä½¿ç”¨ã•れる上ã§å•題ã«ãªã‚‹ã€‚ + + ãƒ¬ã‚¸ã‚¹ã‚¿ã«æ›¸ãè¾¼ã¾ã‚ŒãŸå€¤ã‚’ãã¡ã‚“ã¨éŸ³å£°å‡ºåŠ›ã«å映ã•ã›ã‚‹ã«ã¯ã€ + éŽåŽ»ã«æ›¸ãè¾¼ã¾ã‚ŒãŸãƒ¬ã‚¸ã‚¹ã‚¿ã®å€¤(ã„ã¤ã€ã©ã®ãƒ¬ã‚¸ã‚¹ã‚¿ã«ã€ä½•ãŒæ›¸ãè¾¼ã¾ã‚ŒãŸã‹) + ã‚’ä¿å­˜ã—ã¦ãŠã„ã¦ã€ã‚µã‚¦ãƒ³ãƒ‰ãƒãƒƒãƒ•ァ更新時ã«ã“れをå‚ç…§ã™ã‚‹æ–¹æ³•㌠+ 考ãˆã‚‰ã‚Œã‚‹ã€‚ã©ã®ãらã„éŽåŽ»ã¾ã§ãƒ¬ã‚¸ã‚¹ã‚¿ã®å€¤ã‚’ä¿å­˜ã—ã¦ãŠãã‹ã¯ã€ + サウンドãƒãƒƒãƒ•ã‚¡ã®é•·ã•ã«ã‚‚ã‚ˆã‚‹ã¨æ€ã‚れるãŒã€ã¨ã‚Šã‚ãˆãšã¯è©¦è¡ŒéŒ¯èª¤ã§ + 決ã‚ã‚‹ã“ã¨ã«ã™ã‚‹ã€‚ + + PSGレジスタã¸ã®æ›¸ãè¾¼ã¿å‹•作ã¯ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚¹ãƒ¬ãƒƒãƒ‰ã§ + 行ãªã‚れã€ã‚µã‚¦ãƒ³ãƒ‰ãƒãƒƒãƒ•ã‚¡æ›´æ–°ã¯ãã®å°‚用スレッドã§è¡Œãªã‚れる。 + ã“れã ã¨ã€ã‚¨ãƒŸãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚¹ãƒ¬ãƒƒãƒ‰ãŒãƒ¬ã‚¸ã‚¹ã‚¿ã®ã‚­ãƒ¥ãƒ¼ã«æ›¸ãè¾¼ã¿ã‚’ + 行ãªã£ã¦ã„る最中ã«ã€ã‚µã‚¦ãƒ³ãƒ‰ãƒãƒƒãƒ•ァ更新スレッドãŒã‚­ãƒ¥ãƒ¼ã‹ã‚‰ + 読ã¿å‡ºã—を行ãªã£ã¦ã—ã¾ã„ã€ã‚¢ã‚¯ã‚»ã‚¹ãŒè¡çªã™ã‚‹ã€‚ã“ã®å•題を解決ã™ã‚‹ã«ã¯ã€ + + 1.サウンドãƒãƒƒãƒ•ã‚¡ã®æ›´æ–°ã‚’別スレッドã§è¡Œãªã‚ãªã„ + 2.キューã®ã‚¢ã‚¯ã‚»ã‚¹éƒ¨åˆ†ã‚’排他処ç†ã«ã™ã‚‹ + + ã®ï¼’ã¨ãŠã‚ŠãŒè€ƒãˆã‚‰ã‚Œã‚‹ã€‚ã¨ã‚Šã‚ãˆãšï¼’ã®æ–¹æ³•ã‚’ã¨ã‚‹ã“ã¨ã«ã™ã‚‹ã€‚ +---------------------------------------------------------------------------*/ + + +typedef struct +{ + Uint32 frq; + BOOL bOn; + BOOL bDDA; + Uint32 volume; + Uint32 volumeL; + Uint32 volumeR; + Sint32 outVolumeL; + Sint32 outVolumeR; + Sint32 wave[32]; + Uint32 waveIndex; + Sint32 ddaSample; + Uint32 phase; + Uint32 deltaPhase; + BOOL bNoiseOn; + Uint32 noiseFrq; + Uint32 deltaNoisePhase; +} PSG; + +typedef struct +{ + double SAMPLE_RATE; + double PSG_FRQ; + double RESMPL_RATE; + + PSG Psg[8]; // 6, 7 is unused + Sint32 DdaFadeOutL[8]; //Kitao追加 + Sint32 DdaFadeOutR[8]; //Kitao追加 + Uint32 Channel; // 0 - 5; + Uint32 MainVolumeL; // 0 - 15 + Uint32 MainVolumeR; // 0 - 15 + Uint32 LfoFrq; + BOOL bLfoOn; //v1.59ã‹ã‚‰éžä½¿ç”¨ã€‚éŽåŽ»verã®ã‚¹ãƒ†ãƒ¼ãƒˆãƒ­ãƒ¼ãƒ‰ã®ãŸã‚ã«æ®‹ã—ã¦ã‚る。 + Uint32 LfoCtrl; + Uint32 LfoShift; //v1.59ã‹ã‚‰éžä½¿ç”¨ã€‚éŽåŽ»verã®ã‚¹ãƒ†ãƒ¼ãƒˆãƒ­ãƒ¼ãƒ‰ã®ãŸã‚ã«æ®‹ã—ã¦ã‚る。 + Sint32 PsgVolumeEffect; // = 0;//Kitao追加 + double Volume; // = 0;//Kitao追加 + double VOL; // = 0.0;//Kitao追加。v1.08 +// BOOL _bPsgMute[8] = {FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE};//Kitao追加。v1.29 + BOOL bPsgMute[8]; + + Uint8 Port[16]; // for debug purpose + + BOOL bWaveCrash; //Kitao追加。DDAå†ç”Ÿä¸­ã«Waveãƒ‡ãƒ¼ã‚¿ãŒæ›¸ãæ›ãˆã‚‰ã‚ŒãŸã‚‰TRUE + BOOL bHoneyInTheSky; //ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„パッãƒç”¨ã€‚v2.60 +} huc6280_state; + +static Sint32 _VolumeTable[92]; +static Sint32 _NoiseTable[32768]; + +//static BOOL _bPsgInit = FALSE; +static BOOL _bTblInit = FALSE; + +//Kitao更新。v1.10。キュー処ç†ã‚’ã“ã“ã«çµ±åˆã—ã¦é«˜é€ŸåŒ–。 +/* + APU専用キューã®ä»•様 + + ãƒ¬ã‚¸ã‚¹ã‚¿ã«æ›¸ãè¾¼ã¿ãŒè¡Œãªã‚れるã”ã¨ã«ã€ + キューã«ãã®å†…容を追加ã™ã‚‹ã€‚ + + サウンドãƒãƒƒãƒ•ァ更新時ã«çµŒéŽæ™‚é–“ã‚’ã¿ã¦ã€ + éŽåŽ»ã«æ›¸ãè¾¼ã¾ã‚ŒãŸãƒ¬ã‚¸ã‚¹ã‚¿å†…容を + 書ãè¾¼ã¾ã‚ŒãŸé †ã«ã‚­ãƒ¥ãƒ¼ã‹ã‚‰å–り出ã—〠+ PSGレジスタを更新ã™ã‚‹ã€‚ + (ãªãŠï¼°ï¼³ï¼§ãƒ¬ã‚¸ã‚¹ã‚¿ã¯å…¨ã¦ write only ã¨ã¿ãªã™) + ↑è¦ç¢ºèª + + キューã«è¿½åŠ ã™ã‚‹ã¨ãã«ã¯ write index を用ã„〠+ å–り出ã™ã¨ãã«ã¯ read index を用ã„る。 + + // 追加 + queue[write index++] = written data + + // å–り出㗠+ data = queue[read index++] + + キューã‹ã‚‰å€¤ã‚’å–り出ã—ãŸã¨ãã« read index ㌠+ write index ã¨ä¸€è‡´ã—ãŸã¨ã㯠queue underflow。 + →ã¨ã‚Šã‚ãˆãšãªã«ã‚‚ã—ãªã„。 + + キューã«å€¤ã‚’追加ã—ãŸã¨ãã« write index ㌠+ read index ã¨ä¸€è‡´ã—ãŸã¨ã㯠queue overflow。 + →ã¨ã‚Šã‚ãˆãšãƒªã‚»ãƒƒãƒˆã™ã‚‹ã“ã¨ã«ã™ã‚‹ã€‚ +*/ + +/*#define APUQUEUE_SIZE 65536*2 // must be power of 2 v1.61更新。65536ã ã¨ï¼¡åˆ—車3をオーãƒãƒ¼ã‚¯ãƒ­ãƒƒã‚¯ã—ã¦ãƒ—レイã—ãŸã¨ãã«è¶³ã‚Šãªã‹ã£ãŸã€‚ + +typedef struct //Kitao更新。clockã¯éžä½¿ç”¨ã¨ã—ãŸã€‚v1.61ã‹ã‚‰ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ã®ã‚µã‚¤ã‚ºã‚’減らã™ãŸã‚ã«å¤‰æ•°ä¸Šã‹ã‚‰ã‚‚カット。 +{ + Uint8 reg; // 0-15 + Uint8 data; // written data +} ApuQueue; +typedef struct //v1.60以å‰ã®ã‚¹ãƒ†ãƒ¼ãƒˆãƒ­ãƒ¼ãƒ‰ã®ãŸã‚残ã—ã¦ã‚る。 +{ + Uint32 clock; // cpu cycles elapsed since previous write Kitao更新。clockã¯ç¾åœ¨éžä½¿ç”¨ã€‚ + Uint8 reg; // 0-15 + Uint8 data; // written data +} OldApuQueue; + +static ApuQueue _Queue[APUQUEUE_SIZE]; +static Uint32 _QueueWriteIndex; +static Uint32 _QueueReadIndex;*/ + + +//ボリュームテーブルã®ä½œæˆ +//Kitao更新。低音é‡ã®éŸ³ãŒå®Ÿæ©Ÿã‚ˆã‚Šèžã“ãˆã¥ã‚‰ã„ã®ã§ã€æ¸›é€€çŽ‡ã‚’VOL_TABLE_DECLINE[db](試行錯誤ã—ãŸãƒ™ã‚¹ãƒˆå€¤)ã¨ã—ã€ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚ºå‡¦ç†ã‚’ã™ã‚‹ã‚ˆã†ã«ã—ãŸã€‚v1.46 +// ãŠãらãã€å®Ÿæ©Ÿã‚‚アンプを通ã£ã¦å‡ºåŠ›ã•れる際ã«ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚ºå‡¦ç†ã•れã¦ã„る。 +static void +create_volume_table() +{ + int i; + double v; + + _VolumeTable[0] = 0; //Kitao追加 + for (i = 1; i <= 91; i++) + { + v = 91 - i; + _VolumeTable[i] = (Sint32)(32768.0 * pow(10.0, v * VOL_TABLE_DECLINE / 20.0)); //VOL_TABLE_DECLINE。å°ã•ãã—ã™ãŽã‚‹ã¨éŸ³ãŒå¹³é¢çš„ãªå‚¾å‘ã«ã€‚ソルジャーブレイドã§èª¿æ•´ã€‚v1.46。 + } +} + + +//ノイズテーブルã®ä½œæˆ +static void +create_noise_table() +{ + Sint32 i; + Uint32 bit0; + Uint32 bit1; + Uint32 bit14; + Uint32 reg = 0x100; + + for (i = 0; i < 32768; i++) + { + bit0 = reg & 1; + bit1 = (reg & 2) >> 1; + bit14 = (bit0 ^ bit1); + reg >>= 1; + reg |= (bit14 << 14); + _NoiseTable[i] = (bit0) ? NOISE_TABLE_VALUE; //Kitao更新。ノイズã®ãƒœãƒªãƒ¥ãƒ¼ãƒ ã¨éŸ³è³ªã‚’調整ã—ãŸã€‚ + } +} + + +/*----------------------------------------------------------------------------- + [write_reg] + PSGãƒãƒ¼ãƒˆã®æ›¸ãè¾¼ã¿ã«å¯¾ã™ã‚‹å‹•作を記述ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +//static inline void +INLINE void +write_reg( + huc6280_state* info, + Uint8 reg, + Uint8 data) +{ + Uint32 i; + Uint32 frq;//Kitao追加 + PSG* PSGChn; + + info->Port[reg & 15] = data; + + switch (reg & 15) + { + case 0: // register select + info->Channel = data & 7; + break; + + case 1: // main volume + info->MainVolumeL = (data >> 4) & 0x0F; + info->MainVolumeR = data & 0x0F; + + /* LMAL, RMAL ã¯å…¨ãƒãƒ£ãƒãƒ«ã®éŸ³é‡ã«å½±éŸ¿ã™ã‚‹ */ + for (i = 0; i < N_CHANNEL; i++) + { + PSGChn = &info->Psg[i]; + PSGChn->outVolumeL = _VolumeTable[PSGChn->volume + (info->MainVolumeL + PSGChn->volumeL) * 2]; + PSGChn->outVolumeR = _VolumeTable[PSGChn->volume + (info->MainVolumeR + PSGChn->volumeR) * 2]; + } + break; + + case 2: // frequency low + PSGChn = &info->Psg[info->Channel]; + PSGChn->frq &= ~0xFF; + PSGChn->frq |= data; + //Kitao更新。update_frequencyã¯ã€é€Ÿåº¦ã‚¢ãƒƒãƒ—ã®ãŸã‚サブルーãƒãƒ³ã«ã›ãšç›´æŽ¥å®Ÿè¡Œã™ã‚‹ã‚ˆã†ã«ã—ãŸã€‚ + frq = (PSGChn->frq - 1) & 0xFFF; + if (frq) + PSGChn->deltaPhase = (Uint32)((double)(65536.0 * 256.0 * 8.0 * info->RESMPL_RATE) / (double)frq +0.5); //Kitao更新。速度アップã®ãŸã‚frq以外ã¯å®šæ•°è¨ˆç®—ã«ã—ãŸã€‚精度å‘上ã®ãŸã‚ã€å…ˆã«å€¤ã®å°ã•ã„OVERSAMPLE_RATEã®ã»ã†ã§å‰²ã‚‹ã‚ˆã†ã«ã—ãŸã€‚+0.5ã¯å››æ¨äº”å…¥ã§ç²¾åº¦ã‚¢ãƒƒãƒ—。プãƒãƒŽã‚¤ã‚ºè»½æ¸›ã®ãŸã‚。 + else + PSGChn->deltaPhase = 0; + break; + + case 3: // frequency high + PSGChn = &info->Psg[info->Channel]; + PSGChn->frq &= ~0xF00; + PSGChn->frq |= (data & 0x0F) << 8; + //Kitao更新。update_frequencyã¯ã€é€Ÿåº¦ã‚¢ãƒƒãƒ—ã®ãŸã‚サブルーãƒãƒ³ã«ã›ãšç›´æŽ¥å®Ÿè¡Œã™ã‚‹ã‚ˆã†ã«ã—ãŸã€‚ + frq = (PSGChn->frq - 1) & 0xFFF; + if (frq) + PSGChn->deltaPhase = (Uint32)((double)(65536.0 * 256.0 * 8.0 * info->RESMPL_RATE) / (double)frq +0.5); //Kitao更新。速度アップã®ãŸã‚frq以外ã¯å®šæ•°è¨ˆç®—ã«ã—ãŸã€‚精度å‘上ã®ãŸã‚ã€å…ˆã«å€¤ã®å°ã•ã„OVERSAMPLE_RATEã®ã»ã†ã§å‰²ã‚‹ã‚ˆã†ã«ã—ãŸã€‚+0.5ã¯å››æ¨äº”å…¥ã§ç²¾åº¦ã‚¢ãƒƒãƒ—。プãƒãƒŽã‚¤ã‚ºè»½æ¸›ã®ãŸã‚。 + else + PSGChn->deltaPhase = 0; + break; + + case 4: // ON, DDA, AL + PSGChn = &info->Psg[info->Channel]; + if (info->bHoneyInTheSky) //ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„ã®ãƒãƒ¼ã‚ºæ™‚ã«ã€å¾®å¦™ãªãƒœãƒªãƒ¥ãƒ¼ãƒ èª¿æ•´ã‚¿ã‚¤ãƒŸãƒ³ã‚°ã®å•題ã§ãƒ—ãƒãƒŽã‚¤ã‚ºãŒè¼‰ã£ã¦ã—ã¾ã†ã®ã§ã€ç¾çжã¯ãƒ‘ッãƒå‡¦ç†ã§å¯¾å¿œã€‚v2.60æ›´æ–° + { + if ((PSGChn->bOn)&&(data == 0)) //発声中ã«dataãŒ0ã®å ´åˆã€LRボリュームも0ã«ãƒªã‚»ãƒƒãƒˆã€‚ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„ã®ãƒãƒ¼ã‚ºæ™‚ã®ãƒŽã‚¤ã‚ºãŒè§£æ¶ˆã€‚(data & 0x1F)ã ã‘ãŒ0ã®ã¨ãã«ãƒªã‚»ãƒƒãƒˆã™ã‚‹ã¨ã€ã‚µã‚¤ãƒ¬ãƒ³ãƒˆãƒ‡ãƒãƒƒã‚¬ãƒ¼ã‚ºç­‰ã§NG。発声ã—ã¦ãªã„時ã«ãƒªã‚»ãƒƒãƒˆã™ã‚‹ã¨ã‚¢ãƒˆãƒŸãƒƒã‚¯ãƒ­ãƒœã§NG。v2.55 + { + //PRINTF("test %X %X %X %X",info->Channel,PSGChn->bOn,info->MainVolumeL,info->MainVolumeR); + if ((info->MainVolumeL & 1) == 0) //メインボリュームã®bit0ãŒ0ã®ã¨ãã ã‘処ç†(ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„ã§ã‚¤ãƒ¬ã‚®ãƒ¥ãƒ©ãƒ¼ãª0xE。他ã®ã‚²ãƒ¼ãƒ ã¯0xF。※ヘビーユニットも0xEã ã£ãŸ)。ã“れãŒãªã„ã¨ãƒŸã‚ºãƒã‚¯å¤§å†’険ã§éŸ³ãŒå‡ºãªã„。実機ã®ä»•組ã¿ã¨åŒã˜ã‹ã©ã†ã‹ã¯æœªç¢ºèªã€‚v2.53追加 + PSGChn->volumeL = 0; + if ((info->MainVolumeR & 1) == 0) //å³ãƒãƒ£ãƒ³ãƒãƒ«ã‚‚åŒæ§˜ã¨ã™ã‚‹ + PSGChn->volumeR = 0; + } + } + + PSGChn->bOn = ((data & 0x80) != 0); + if ((PSGChn->bDDA)&&((data & 0x40)==0)) //DDAã‹ã‚‰WAVEã¸åˆ‡ã‚Šæ›¿ã‚ã‚‹ã¨ã or DDAã‹ã‚‰æ¶ˆéŸ³ã™ã‚‹ã¨ã + { + //Kitao追加。DDAã¯ã„ããªã‚Šæ¶ˆéŸ³ã™ã‚‹ã¨ç›®ç«‹ã¤ãƒŽã‚¤ã‚ºãŒè¼‰ã‚‹ã®ã§ãƒ•ェードアウトã™ã‚‹ã€‚ + info->DdaFadeOutL[info->Channel] = (Sint32)((double)(PSGChn->ddaSample * PSGChn->outVolumeL) * + ((1 + (1 >> 3) + (1 >> 4) + (1 >> 5) + (1 >> 7) + (1 >> 12) + (1 >> 14) + (1 >> 15)) * SAMPLE_FADE_DECLINE)); //å…ƒã®éŸ³é‡ã€‚v2.65æ›´æ–° + info->DdaFadeOutR[info->Channel] = (Sint32)((double)(PSGChn->ddaSample * PSGChn->outVolumeR) * + ((1 + (1 >> 3) + (1 >> 4) + (1 >> 5) + (1 >> 7) + (1 >> 12) + (1 >> 14) + (1 >> 15)) * SAMPLE_FADE_DECLINE)); + + } + PSGChn->bDDA = ((data & 0x40) != 0); + + //Kitao追加。dataã®bit7,6ãŒ01ã®ã¨ãã«Waveインデックスをリセットã™ã‚‹ã€‚ + //DDAモード時ã«Waveデータを書ã込んã§ã„ãŸå ´åˆã¯ã“ã“ã§Waveãƒ‡ãƒ¼ã‚¿ã‚’ä¿®å¾©ï¼ˆåˆæœŸåŒ–)ã™ã‚‹ã€‚ファイヤープロレスリング。 + if ((data & 0xC0) == 0x40) + { + PSGChn->waveIndex = 0; + if (info->bWaveCrash) + { + for (i=0; i<32; i++) + PSGChn->wave[i] = -14; //Waveデータを最å°å€¤ã§åˆæœŸåŒ– + info->bWaveCrash = FALSE; + } + } + + PSGChn->volume = data & 0x1F; + PSGChn->outVolumeL = _VolumeTable[PSGChn->volume + (info->MainVolumeL + PSGChn->volumeL) * 2]; + PSGChn->outVolumeR = _VolumeTable[PSGChn->volume + (info->MainVolumeR + PSGChn->volumeR) * 2]; + break; + + case 5: // LAL, RAL + PSGChn = &info->Psg[info->Channel]; + PSGChn->volumeL = (data >> 4) & 0xF; + PSGChn->volumeR = data & 0xF; + PSGChn->outVolumeL = _VolumeTable[PSGChn->volume + (info->MainVolumeL + PSGChn->volumeL) * 2]; + PSGChn->outVolumeR = _VolumeTable[PSGChn->volume + (info->MainVolumeR + PSGChn->volumeR) * 2]; + break; + + case 6: // wave data //Kitao更新。DDAモードã®ã¨ãã‚‚Waveデータを更新ã™ã‚‹ã‚ˆã†ã«ã—ãŸã€‚v0.63。ファイヤープロレスリング + PSGChn = &info->Psg[info->Channel]; + data &= 0x1F; + info->bWaveCrash = FALSE; //Kitao追加 + if (!PSGChn->bOn) //Kitao追加。音を鳴らã—ã¦ã„ãªã„ã¨ãã ã‘Waveデータを更新ã™ã‚‹ã€‚v0.65。F1トリプルãƒãƒˆãƒ«ã®ã‚¨ãƒ³ã‚¸ãƒ³éŸ³ã€‚ + { + PSGChn->wave[PSGChn->waveIndex++] = 17 - data; //17。Kitao更新。一番心地よã響ã値ã«ã€‚ミズãƒã‚¯å¤§å†’険,モトローダー,ドラゴンスピリット等ã§èª¿æ•´ã€‚ + PSGChn->waveIndex &= 0x1F; + } + if (PSGChn->bDDA) + { + //Kitao更新。ノイズ軽減ã®ãŸã‚6より下å´ã®å€¤ã¯ã‚«ãƒƒãƒˆã™ã‚‹ã‚ˆã†ã«ã—ãŸã€‚v0.59 + if (data < 6) //サイãƒãƒ¼ãƒŠã‚¤ãƒˆã§6ã«æ±ºå®š + data = 6; //ノイズãŒå¤šã„ã®ã§å°ã•ãªå€¤ã¯ã‚«ãƒƒãƒˆ + PSGChn->ddaSample = 11 - data; //サイãƒãƒ¼ãƒŠã‚¤ãƒˆã§11ã«æ±ºå®šã€‚ドラムã®éŸ³è‰²ãŒæœ€é©ã€‚v0.74 + + if (!PSGChn->bOn) //DDAモード時ã«Waveãƒ‡ãƒ¼ã‚¿ã‚’æ›¸ãæ›ãˆãŸå ´åˆ + info->bWaveCrash = TRUE; + } + break; + + case 7: // noise on, noise frq + if (info->Channel >= 4) + { + PSGChn = &info->Psg[info->Channel]; + PSGChn->bNoiseOn = ((data & 0x80) != 0); + PSGChn->noiseFrq = 0x1F - (data & 0x1F); + if (PSGChn->noiseFrq == 0) + PSGChn->deltaNoisePhase = (Uint32)((double)(2048.0 * info->RESMPL_RATE) +0.5); //Kitaoæ›´æ–° + else + PSGChn->deltaNoisePhase = (Uint32)((double)(2048.0 * info->RESMPL_RATE) / (double)PSGChn->noiseFrq +0.5); //Kitaoæ›´æ–° + } + break; + + case 8: // LFO frequency + info->LfoFrq = data; + //Kitaoテスト用 + //PRINTF("LFO Frq = %X",info->LfoFrq); + break; + + case 9: // LFO control Kitao更新。シンプルã«å®Ÿè£…ã—ã¦ã¿ãŸã€‚実機ã§åŒã˜å‹•作ã‹ã¯æœªç¢ºèªã€‚ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„ã®éŸ³ãŒä¼¼ã‚‹ã‚ˆã†ã«å®Ÿè£…。v1.59 + if (data & 0x80) //bit7ã‚’ç«‹ã¦ã¦å‘¼ã¶ã¨æã‚‰ãリセット + { + info->Psg[1].phase = 0; //LfoFrqã¯åˆæœŸåŒ–ã—ãªã„。ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„。 + //Kitaoテスト用 + //PRINTF("LFO control = %X",data); + } + info->LfoCtrl = data & 7; //ドロップロックã»ã‚‰ãƒ›ãƒ©ã§5ãŒä½¿ã‚れる。v1.61æ›´æ–° + if (info->LfoCtrl & 4) + info->LfoCtrl = 0; //ドロップロックã»ã‚‰ãƒ›ãƒ©ã€‚実機ã§è´ã„ãŸæ„Ÿã˜ã¯LFOオフã¨åŒã˜éŸ³ã®ã‚ˆã†ãªã®ã§bit2ãŒç«‹ã£ã¦ã„ãŸ(è² ã®æ•°æ‰±ã„?)ら0ã¨åŒã˜ã“ã¨ã¨ã™ã‚‹ã€‚ + //Kitaoテスト用 + //PRINTF("LFO control = %X, Frq =%X",data,info->LfoFrq); + break; + + default: // invalid write + break; + } + + return; +} + + +//Kitao追加 +static void +set_VOL(huc6280_state* info) +{ + //Sint32 v; + + if (info->PsgVolumeEffect == 0) + //info->VOL = 0.0; //ミュート + info->VOL = 1.0 / 128.0; + else if (info->PsgVolumeEffect == 3) + info->VOL = info->Volume / (double)(OVERSAMPLE_RATE * 4.0/3.0); // 3/4。v1.29追加 + else + info->VOL = info->Volume / (double)(OVERSAMPLE_RATE * info->PsgVolumeEffect); //Kitao追加。_PsgVolumeEffect=ボリューム調節効果。 + + /*if (!APP_GetCDGame()) //Huカードゲームã®ã¨ãã ã‘ã€ãƒœãƒªãƒ¥ãƒ¼ãƒ 101~120を有効化。v2.62 + { + v = APP_GetWindowsVolume(); + if (v > 100) + _VOL *= ((double)(v-100) * 3.0 + 100.0) / 100.0; //101~120ã¯é€šå¸¸ã®3.0å€ã®éŸ³é‡å¤‰åŒ–。3.0å€ã®Vol120ã§ã‚½ãƒ«ã‚¸ãƒ£ãƒ¼ãƒ–レイド最é©ã€‚ビックリマンワールドOK。3.1å€ä»¥ä¸Šã ã¨éŸ³ãŒè–„ããªã‚‹ï¼†éŸ³å‰²ã‚Œã®å¿ƒé…ã‚‚ã‚り。 + }*/ +} + +/*----------------------------------------------------------------------------- + [Mix] + PSGã®å‡ºåŠ›ã‚’ãƒŸãƒƒã‚¯ã‚¹ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +void +PSG_Mix( +// Sint16* pDst, // 出力先ãƒãƒƒãƒ•ã‚¡ //Kitao更新。PSG専用ãƒãƒƒãƒ•ã‚¡ã«ã—ãŸãŸã‚Sint16ã«ã€‚ + void* chip, + Sint32** pDst, + Sint32 nSample) // 書ã出ã™ã‚µãƒ³ãƒ—ル数 +{ + huc6280_state* info = (huc6280_state*)chip; + PSG* PSGChn; + Sint32 i; + Sint32 j; + Sint32 sample; //Kitao追加 + Sint32 lfo; + Sint32 sampleAllL; //Kitao追加。6chã¶ã‚“ã®ã‚µãƒ³ãƒ—ルを足ã—ã¦ã„ããŸã‚ã®ãƒãƒƒãƒ•ァ。精度を維æŒã™ã‚‹ãŸã‚ã«å¿…è¦ã€‚6chã¶ã‚“åˆè¨ˆãŒçµ‚ã‚ã£ãŸå¾Œã«ã€ã“れをSint16ã«å¤‰æ›ã—ã¦æ›¸ã込むよã†ã«ã—ãŸã€‚ + Sint32 sampleAllR; //Kitao追加。上ã®ï¼²ãƒãƒ£ãƒ³ãƒãƒ«ç”¨ + Sint32 smp; //Kitao追加。DDA音é‡,ノイズ音é‡è¨ˆç®—用 + Sint32* bufL = pDst[0]; + Sint32* bufR = pDst[1]; + +// if (!_bPsgInit) +// return; + + for (j=0; jPsg[i]; + + if ((PSGChn->bOn)&&((i != 1)||(info->LfoCtrl == 0))&&(!info->bPsgMute[i])) //Kitaoæ›´æ–° + { + if (PSGChn->bDDA) + { + smp = PSGChn->ddaSample * PSGChn->outVolumeL; + sampleAllL += smp + (smp >> 3) + (smp >> 4) + (smp >> 5) + (smp >> 7) + (smp >> 12) + (smp >> 14) + (smp >> 15); //Kitao更新。サンプリング音ã®éŸ³é‡ã‚’実機並ã¿ã«èª¿æ•´ã€‚v2.39,v2.40,v2.62,v2.65å†èª¿æ•´ã—ãŸã€‚ + smp = PSGChn->ddaSample * PSGChn->outVolumeR; + sampleAllR += smp + (smp >> 3) + (smp >> 4) + (smp >> 5) + (smp >> 7) + (smp >> 12) + (smp >> 14) + (smp >> 15); //Kitao更新。サンプリング音ã®éŸ³é‡ã‚’実機並ã¿ã«èª¿æ•´ã€‚v2.39,v2.40,v2.62,v2.65å†èª¿æ•´ã—ãŸã€‚ + } + else if (PSGChn->bNoiseOn) + { + sample = _NoiseTable[PSGChn->phase >> 17]; + + if (PSGChn->noiseFrq == 0) //Kitao追加。noiseFrq=0(dataã«0x1FãŒæ›¸ãè¾¼ã¾ã‚ŒãŸ)ã®ã¨ãã¯éŸ³é‡ãŒé€šå¸¸ã®åŠåˆ†ã¨ã—ãŸã€‚(ファイヤープロレスリング3ã€ãƒ‘ãƒƒã‚¯ãƒ©ãƒ³ãƒ‰ã€æ¡ƒå¤ªéƒŽæ´»åЇã€ãŒã‚“ã°ã‚Œã‚´ãƒ«ãƒ•ボーイズãªã©ï¼‰ + { + smp = sample * PSGChn->outVolumeL; + sampleAllL += (smp >> 1) + (smp >> 12) + (smp >> 14); //(1/2 + 1/4096 + (1/32768 + 1/32768)) + smp = sample * PSGChn->outVolumeR; + sampleAllR += (smp >> 1) + (smp >> 12) + (smp >> 14); + } + else //通常 + { + smp = sample * PSGChn->outVolumeL; + sampleAllL += smp + (smp >> 11) + (smp >> 14) + (smp >> 15); //Kitao更新。ノイズã®éŸ³é‡ã‚’実機並ã¿ã«èª¿æ•´(1 + 1/2048 + 1/16384 + 1/32768)。ã“ã®"+1/32768"ã§çµ¶å¦™(主観。大魔界æ‘,ソルジャーブレイドãªã©)ã«ãªã‚‹ã€‚v2.62æ›´æ–° + smp = sample * PSGChn->outVolumeR; + sampleAllR += smp + (smp >> 11) + (smp >> 14) + (smp >> 15); //Kitao更新。ノイズã®éŸ³é‡ã‚’実機並ã¿ã«èª¿æ•´ + } + + PSGChn->phase += PSGChn->deltaNoisePhase; //Kitaoæ›´æ–° + } + else if (PSGChn->deltaPhase) + { + //Kitao更新。オーãƒãƒ¼ã‚µãƒ³ãƒ—リングã—ãªã„よã†ã«ã—ãŸã€‚ + sample = PSGChn->wave[PSGChn->phase >> 27]; + if (PSGChn->frq < 128) + sample -= sample >> 2; //低周波域ã®éŸ³é‡ã‚’制é™ã€‚ブラッドギアã®ã‚¹ã‚¿ãƒ¼ãƒˆæ™‚ãªã©ã§å®Ÿæ©Ÿã¨åŒæ§˜ã®éŸ³ã«ã€‚ソルジャーブレイドãªã©ã‚‚実機ã«è¿‘ããªã£ãŸã€‚v2.03 + + sampleAllL += sample * PSGChn->outVolumeL; //Kitaoæ›´æ–° + sampleAllR += sample * PSGChn->outVolumeR; //Kitaoæ›´æ–° + + //Kitao更新。Lfoã‚ªãƒ³ãŒæœ‰åйã«ãªã‚‹ã‚ˆã†ã«ã—ã€Lfoã®æŽ›ã‹ã‚Šå…·åˆã‚’実機ã«è¿‘ã¥ã‘ãŸã€‚v1.59 + if ((i==0)&&(info->LfoCtrl>0)) + { + //_LfoCtrlãŒ1ã®ã¨ãã«0回シフト(ãã®ã¾ã¾)ã§ã€ã¯ã«ã„ã„ã‚“ã–ã™ã‹ã„ãŒå®Ÿæ©Ÿã®éŸ³ã«è¿‘ã„。 + //_LfoCtrlãŒ3ã®ã¨ãã«4回シフトã§ã€ãƒ•ラッシュãƒã‚¤ãƒ€ãƒ¼ã‚¹ãŒå®Ÿæ©Ÿã®éŸ³ã«è¿‘ã„。 + lfo = info->Psg[1].wave[info->Psg[1].phase >> 27] << ((info->LfoCtrl-1) << 1); //v1.60æ›´æ–° + info->Psg[0].phase += (Uint32)((double)(65536.0 * 256.0 * 8.0 * info->RESMPL_RATE) / (double)(info->Psg[0].frq + lfo) +0.5); + info->Psg[1].phase += (Uint32)((double)(65536.0 * 256.0 * 8.0 *info-> RESMPL_RATE) / (double)(info->Psg[1].frq * info->LfoFrq) +0.5); //v1.60æ›´æ–° + } + else + PSGChn->phase += PSGChn->deltaPhase; + } + } + //Kitao追加。DDA消音時ã¯ãƒŽã‚¤ã‚ºè»½æ¸›ã®ãŸã‚ãƒ•ã‚§ãƒ¼ãƒ‰ã‚¢ã‚¦ãƒˆã§æ¶ˆéŸ³ã™ã‚‹ã€‚ + // ベラボーマン(「ã‚ã—ãŒã°ãã ã¯ã‹ã›ã˜ã‚ƒã€ã‹ã‚‰æ•°ç§’後)やパワーテニス(タイトル曲終了ã‹ã‚‰æ•°ç§’後。点数コール),将棋åˆå¿ƒè€…無用(音声)ç­‰ã§åŠ¹æžœã‚り。 + if (info->DdaFadeOutL[i] > 0) + --info->DdaFadeOutL[i]; + else if (info->DdaFadeOutL[i] < 0) + ++info->DdaFadeOutL[i]; + if (info->DdaFadeOutR[i] > 0) + --info->DdaFadeOutR[i]; + else if (info->DdaFadeOutR[i] < 0) + ++info->DdaFadeOutR[i]; + sampleAllL += info->DdaFadeOutL[i]; + sampleAllR += info->DdaFadeOutR[i]; + } + //Kitao更新。6chåˆã‚ã•ã£ãŸã¨ã“ã‚ã§ã€ãƒœãƒªãƒ¥ãƒ¼ãƒ èª¿æ•´ã—ã¦ãƒãƒƒãƒ•ã‚¡ã«æ›¸ã込む。 + sampleAllL = (Sint32)((double)sampleAllL * info->VOL); + //if ((sampleAllL>32767)||(sampleAllL<-32768)) PRINTF("PSG Sachitta!");//test用 +// if (sampleAllL> 32767) sampleAllL= 32767; //Volをアップã—ãŸã®ã§ã‚µãƒãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒã‚§ãƒƒã‚¯ãŒå¿…è¦ã€‚v2.39 +// if (sampleAllL<-32768) sampleAllL=-32768; // パックランドã§UFOç­‰ã«ã‚„られãŸã¨ããらã„ã§ã€é€šå¸¸ã®ã‚²ãƒ¼ãƒ ã§ã¯èµ·ã“らãªã„。音é‡ã®å¤§ããªãƒ“ックリマンワールドもOK。パックランドも通常ã¯OKã§ã‚µãƒãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã—ãŸã¨ãã§ã‚‚ã‚ãšã‹ãªã®ã§éŸ³è³ªçš„ã«å¤§ä¸ˆå¤«ã€‚ + // ãªã®ã§éŸ³è³ªçš„ã«ã¯ã€PSGã‚’ï¼’ã¤ã®DirectXãƒãƒ£ãƒ³ãƒãƒ«ã«åˆ†ã‘ã¦é³´ã‚‰ã™ã¹ã(処ç†ã¯é‡ããªã‚‹)ã ãŒã€ç¾çжã¯ãƒ‘ックランドã§ã‚‚サãƒãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³å‡¦ç†ã ã‘ã§éŸ³è³ªçš„ã«å•題ãªã—(速度優先)ã¨ã™ã‚‹ã€‚ + sampleAllR = (Sint32)((double)sampleAllR * info->VOL); + //if ((sampleAllR>32767)||(sampleAllR<-32768)) PRINTF("PSG Satitta!");//test用 +// if (sampleAllR> 32767) sampleAllR= 32767; //Volをアップã—ãŸã®ã§ã‚µãƒãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒã‚§ãƒƒã‚¯ãŒå¿…è¦ã€‚v2.39 +// if (sampleAllR<-32768) sampleAllR=-32768; // + *bufL++ = sampleAllL; + *bufR++ = sampleAllR; + + //キューをå‚ç…§ã—PSGレジスタを更新ã™ã‚‹ã€‚Kitao更新。高速化ã®ãŸã‚サブルーãƒãƒ³ã«ã›ãšã“ã“ã§å‡¦ç†ã€‚キューã®å‚ç…§ã¯ã‚·ãƒ³ãƒ—ルã«ã—ãŸ(テンãƒã®å®‰å®šæ€§å‘上)。 + /*while (_QueueReadIndex != _QueueWriteIndex) //v1.10更新。キュー処ç†ã‚’ã“ã“ã¸çµ±åˆã—高速化。 + { + write_reg(_Queue[_QueueReadIndex].reg, _Queue[_QueueReadIndex].data); + _QueueReadIndex++; //Kitaoæ›´æ–° + _QueueReadIndex &= APUQUEUE_SIZE-1; //Kitaoæ›´æ–° + }*/ + } +} + + +//Kitaoæ›´æ–° +static void +psg_reset(huc6280_state* info) +{ + int i,j; + + memset(info->Psg, 0, sizeof(info->Psg)); + memset(info->DdaFadeOutL, 0, sizeof(info->DdaFadeOutL)); //Kitao追加 + memset(info->DdaFadeOutR, 0, sizeof(info->DdaFadeOutR)); //Kitao追加 + info->MainVolumeL = 0; + info->MainVolumeR = 0; + info->LfoFrq = 0; + info->LfoCtrl = 0; + info->Channel = 0; //Kitao追加。v2.65 + info->bWaveCrash = FALSE; //Kitao追加 + + //Kitao更新。v0.65.waveãƒ‡ãƒ¼ã‚¿ã‚’åˆæœŸåŒ–。 + for (i=0; iPsg[i].wave[j] = -14; //最å°å€¤ã§åˆæœŸåŒ–。ファイプロ,フォーメーションサッカー'90,F1トリプルãƒãƒˆãƒ«ã§å¿…è¦ã€‚ + for (j=0; j<32; j++) + info->Psg[3].wave[j] = 17; //ch3ã¯æœ€å¤§å€¤ã§åˆæœŸåŒ–。F1トリプルãƒãƒˆãƒ«ã€‚v2.65 + + //Kitao更新。v1.10。キュー処ç†ã‚’ã“ã“ã«çµ±åˆ +// _QueueWriteIndex = 0; +// _QueueReadIndex = 0; +} + + +static void PSG_SetVolume(huc6280_state* info); +/*----------------------------------------------------------------------------- + [Init] + ï¼°ï¼³ï¼§ã‚’åˆæœŸåŒ–ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +//Sint32 +void* +PSG_Init( + Sint32 clock, + Sint32 sampleRate) +{ + huc6280_state* info; + + info = (huc6280_state*)malloc(sizeof(huc6280_state)); + if (info == NULL) + return NULL; + + info->PSG_FRQ = clock & 0x7FFFFFFF; + PSG_SetHoneyInTheSky(info, (clock >> 31) & 0x01); +// PSG_SetHoneyInTheSky(0x01); + + info->PsgVolumeEffect = 0; + info->Volume = 0; + info->VOL = 0.0; + + //PSG_SetVolume(APP_GetPsgVolume());//Kitao追加 + PSG_SetVolume(info); + + psg_reset(info); + + if (! _bTblInit) + { + create_volume_table(); + create_noise_table(); + _bTblInit = TRUE; + } + + //PSG_SetSampleRate(sampleRate); + info->SAMPLE_RATE = sampleRate; + info->RESMPL_RATE = info->PSG_FRQ / OVERSAMPLE_RATE / info->SAMPLE_RATE; + +// _bPsgInit = TRUE; + + return info; +} + + +/*----------------------------------------------------------------------------- + [SetSampleRate] +-----------------------------------------------------------------------------*/ +/*void +PSG_SetSampleRate( + Uint32 sampleRate) +{ + //_SampleRate = sampleRate; +}*/ + + +/*----------------------------------------------------------------------------- + [Deinit] + PSGを破棄ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +void +PSG_Deinit(void* chip) +{ + huc6280_state* info = (huc6280_state*)chip; + + /*memset(info->Psg, 0, sizeof(_Psg)); + memset(info->DdaFadeOutL, 0, sizeof(_DdaFadeOutL)); //Kitao追加 + memset(info->DdaFadeOutR, 0, sizeof(_DdaFadeOutR)); //Kitao追加 + info->MainVolumeL = 0; + info->MainVolumeR = 0; + info->LfoFrq = 0; + info->LfoCtrl = 0; + info->bWaveCrash = FALSE; //Kitao追加 +// _bPsgInit = FALSE;*/ + + free(info); +} + + +/*----------------------------------------------------------------------------- + [Read] + PSGãƒãƒ¼ãƒˆã®èª­ã¿å‡ºã—ã«å¯¾ã™ã‚‹å‹•作を記述ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +Uint8 +PSG_Read( + void* chip, + Uint32 regNum) +{ + huc6280_state* info = (huc6280_state*)chip; + + if (regNum == 0) + return (Uint8)info->Channel; + + return info->Port[regNum & 15]; +} + + +/*----------------------------------------------------------------------------- + [Write] + PSGãƒãƒ¼ãƒˆã®æ›¸ãè¾¼ã¿ã«å¯¾ã™ã‚‹å‹•作を記述ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +void +PSG_Write( + void* chip, + Uint32 regNum, + Uint8 data) +{ + huc6280_state* info = (huc6280_state*)chip; + + //v1.10更新。キュー処ç†ã‚’ã“ã“ã«çµ±åˆã—ã¦é«˜é€ŸåŒ–。 + //Kitao更新。clockã¯è€ƒæ…®ã›ãšã«ã‚·ãƒ³ãƒ—ルã«ã—ã¦é«˜é€ŸåŒ–ã—ãŸã€‚ +/* if (((_QueueWriteIndex + 1) & (APUQUEUE_SIZE-1)) == _QueueReadIndex) + { + PRINTF("PSG Queue Over!"); // ã‚­ãƒ¥ãƒ¼ãŒæº€ã‚¿ãƒ³ + return; + } + _Queue[_QueueWriteIndex].reg = (Uint8)(regNum & 15); + _Queue[_QueueWriteIndex].data = data; + _QueueWriteIndex++; //Kitaoæ›´æ–° + _QueueWriteIndex &= APUQUEUE_SIZE-1; //Kitaoæ›´æ–° +*/ + write_reg(chip, regNum, data); +} + + +/*Sint32 +PSG_AdvanceClock( + Sint32 clock) +{ + return 0; +}*/ + + +//Kitao追加。PSGã®ãƒœãƒªãƒ¥ãƒ¼ãƒ ã‚‚個別ã«è¨­å®šå¯èƒ½ã«ã—ãŸã€‚ +/*static void +PSG_SetVolume( + Uint32 volume) // 0 - 65535*/ +static void PSG_SetVolume(huc6280_state* info) +{ + /*if (volume < 0) volume = 0; + if (volume > 65535) volume = 65535;*/ + + //_Volume = (double)volume / 65535.0 / PSG_DECLINE; + info->Volume = 1.0 / PSG_DECLINE; + set_VOL(info); +} + +//Kitao追加。ボリュームミュートã€ãƒãƒ¼ãƒ•ãªã©ã‚’ã§ãるよã†ã«ã—ãŸã€‚ +/*static void +PSG_SetVolumeEffect( + Uint32 volumeEffect) +{ + _PsgVolumeEffect = (Sint32)volumeEffect; //※数値ãŒå¤§ãã„ã»ã©ãƒœãƒªãƒ¥ãƒ¼ãƒ ã¯å°ã•ããªã‚‹ + set_VOL(); +}*/ + + +//Kitao追加 +void +PSG_ResetVolumeReg(void* chip) +{ + huc6280_state* info = (huc6280_state*)chip; + int i; + + info->MainVolumeL = 0; + info->MainVolumeR = 0; + for (i = 0; i < N_CHANNEL; i++) + { + info->Psg[i].volume = 0; + info->Psg[i].outVolumeL = 0; + info->Psg[i].outVolumeR = 0; + info->DdaFadeOutL[i] = 0; + info->DdaFadeOutR[i] = 0; + } +} + + +//Kitao追加 +void +PSG_SetMutePsgChannel( + void* chip, + Sint32 num, + BOOL bMute) +{ + huc6280_state* info = (huc6280_state*)chip; + + info->bPsgMute[num] = bMute; + if (bMute) + { + info->DdaFadeOutL[num] = 0; + info->DdaFadeOutR[num] = 0; + } +} + +void PSG_SetMuteMask(void* chip, Uint32 MuteMask) +{ + Uint8 CurChn; + + for (CurChn = 0x00; CurChn < N_CHANNEL; CurChn ++) + PSG_SetMutePsgChannel(chip, CurChn, (MuteMask >> CurChn) & 0x01); + + return; +} + +//Kitao追加 +BOOL +PSG_GetMutePsgChannel( + void* chip, + Sint32 num) +{ + huc6280_state* info = (huc6280_state*)chip; + + return info->bPsgMute[num]; +} + +//Kitao追加。v2.60 +void +PSG_SetHoneyInTheSky( + void* chip, + BOOL bHoneyInTheSky) +{ + huc6280_state* info = (huc6280_state*)chip; + + info->bHoneyInTheSky = bHoneyInTheSky; +} + + +/*// save variable +#define SAVE_V(V) if (fwrite(&V, sizeof(V), 1, p) != 1) return FALSE +#define LOAD_V(V) if (fread(&V, sizeof(V), 1, p) != 1) return FALSE +// save array +#define SAVE_A(A) if (fwrite(A, sizeof(A), 1, p) != 1) return FALSE +#define LOAD_A(A) if (fread(A, sizeof(A), 1, p) != 1) return FALSE*/ +/*----------------------------------------------------------------------------- + [SaveState] + 状態をファイルã«ä¿å­˜ã—ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +/*BOOL +PSG_SaveState( + FILE* p) +{ + BOOL bFlashHiders = FALSE; //Kitao更新。ç¾åœ¨éžä½¿ç”¨ã€‚æ—§ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ã¨ã®äº’æ›ã®ãŸã‚ + + if (p == NULL) + return FALSE; + + SAVE_A(_Psg); + + SAVE_V(_Channel); + SAVE_V(_MainVolumeL); + SAVE_V(_MainVolumeR); + SAVE_V(_LfoFrq); + SAVE_V(_bLfoOn); //v1.59ã‹ã‚‰éžä½¿ç”¨ã«ã€‚ + SAVE_V(_LfoCtrl); + SAVE_V(_LfoShift); //v1.59ã‹ã‚‰éžä½¿ç”¨ã«ã€‚ + SAVE_V(_bWaveCrash); //Kitao追加。v0.65 + + SAVE_V(bFlashHiders); //Kitao追加。v0.62 + + //v1.10追加。キュー処ç†ã‚’ã“ã“ã¸çµ±åˆã€‚ + SAVE_A(_Queue); //v1.61ã‹ã‚‰ã‚µã‚¤ã‚ºãŒï¼’å€ã«ãªã£ãŸã€‚ + SAVE_V(_QueueWriteIndex); + SAVE_V(_QueueReadIndex); + + return TRUE; +}*/ + + +/*----------------------------------------------------------------------------- + [LoadState] + 状態をファイルã‹ã‚‰èª­ã¿è¾¼ã¿ã¾ã™ã€‚ +-----------------------------------------------------------------------------*/ +/*BOOL +PSG_LoadState( + FILE* p) +{ + Uint32 i; + double clockCounter; //Kitao更新。ç¾åœ¨éžä½¿ç”¨ã€‚æ—§ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ã¨ã®äº’æ›ã®ãŸã‚ + BOOL bInit; //Kitao更新。ç¾åœ¨éžä½¿ç”¨ã€‚æ—§ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ã¨ã®äº’æ›ã®ãŸã‚ + Sint32 totalClockAdvanced; //Kitao更新。ç¾åœ¨éžä½¿ç”¨ã€‚æ—§ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ã¨ã®äº’æ›ã®ãŸã‚ + BOOL bFlashHiders; //Kitao更新。ç¾åœ¨éžä½¿ç”¨ã€‚æ—§ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚»ãƒ¼ãƒ–ã¨ã®äº’æ›ã®ãŸã‚ + OldApuQueue oldQueue[65536]; //v1.60以å‰ã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚’読ã¿è¾¼ã¿ç”¨ã€‚ + + + if (p == NULL) + return FALSE; + + LOAD_A(_Psg); + + LOAD_V(_Channel); + LOAD_V(_MainVolumeL); + LOAD_V(_MainVolumeR); + LOAD_V(_LfoFrq); + LOAD_V(_bLfoOn); //v1.59ã‹ã‚‰éžä½¿ç”¨ã«ã€‚ + LOAD_V(_LfoCtrl); + if (MAINBOARD_GetStateVersion() >= 3) //Kitao追加。v0.57以é™ã®ã‚»ãƒ¼ãƒ–ファイルãªã‚‰ + LOAD_V(_LfoShift); //v1.59ã‹ã‚‰éžä½¿ç”¨ã«ã€‚ + if (MAINBOARD_GetStateVersion() >= 9) //Kitao追加。v0.65以é™ã®ã‚»ãƒ¼ãƒ–ファイルãªã‚‰ + { + LOAD_V(_bWaveCrash); + } + else + _bWaveCrash = FALSE; + + if (MAINBOARD_GetStateVersion() >= 7) //Kitao追加。v0.62以é™ã®ã‚»ãƒ¼ãƒ–ファイルãªã‚‰ + LOAD_V(bFlashHiders); + + //v1.10追加。キュー処ç†ã‚’ã“ã“ã¸çµ±åˆã€‚v1.61æ›´æ–° + if (MAINBOARD_GetStateVersion() >= 34) //v1.61beta以é™ã®ã‚»ãƒ¼ãƒ–ファイルãªã‚‰ + { + LOAD_A(_Queue); //v1.61ã‹ã‚‰ã‚µã‚¤ã‚ºãŒï¼’å€ï¼†clock部分を削除ã—ãŸã€‚ + LOAD_V(_QueueWriteIndex); + LOAD_V(_QueueReadIndex); + } + else //v1.60以å‰ã®ã‚­ãƒ¥ãƒ¼(æ—§)ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¹ãƒ†ãƒ¼ãƒˆã®å ´åˆã€æ–°ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®æ–¹ã«åˆã†ã‚ˆã†ã«å¤‰æ›ã€‚ + { + LOAD_A(oldQueue); + LOAD_V(_QueueWriteIndex); + LOAD_V(_QueueReadIndex); + if (_QueueWriteIndex >= _QueueReadIndex) + { + for (i=_QueueReadIndex; i<=_QueueWriteIndex; i++) + { + _Queue[i].reg = oldQueue[i].reg; + _Queue[i].data = oldQueue[i].data; + } + } + else //Writeã®ä½ç½®ãŒReadã®ä½ç½®ã‚ˆã‚Šã‚‚å‰ï¼ˆ65536地点をã¾ãŸã„ã§ãƒ‡ãƒ¼ã‚¿ãŒå­˜åœ¨ã—ã¦ã„ã‚‹ã¨ã)ã®å ´åˆ + { + for (i=_QueueReadIndex; i<=65535; i++) + { + _Queue[i].reg = oldQueue[i].reg; + _Queue[i].data = oldQueue[i].data; + } + for (i=0; i<=_QueueWriteIndex; i++) + { + _Queue[65536+i].reg = oldQueue[i].reg; + _Queue[65536+i].data = oldQueue[i].data; + } + _QueueWriteIndex += 65536; + } + } + if (MAINBOARD_GetStateVersion() < 26) //Kitao追加。v1.11よりå‰ã®ã‚»ãƒ¼ãƒ–ファイルãªã‚‰ + { + LOAD_V(clockCounter); //ç¾åœ¨éžä½¿ç”¨ã€‚v0.95 + LOAD_V(bInit); //ç¾åœ¨éžä½¿ç”¨ã€‚v1.10 + LOAD_V(totalClockAdvanced); //ç¾åœ¨éžä½¿ç”¨ã€‚v0.95 + } + + return TRUE; +} + +#undef SAVE_V +#undef SAVE_A +#undef LOAD_V +#undef LOAD_A*/ + diff --git a/Frameworks/GME/vgmplay/chips/Ootake_PSG.h b/Frameworks/GME/vgmplay/chips/Ootake_PSG.h new file mode 100644 index 000000000..7bb6efcd1 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/Ootake_PSG.h @@ -0,0 +1,114 @@ +/*----------------------------------------------------------------------------- + [PSG.h] + PSGを記述ã™ã‚‹ã®ã«å¿…è¦ãªå®šç¾©ãŠã‚ˆã³é–¢æ•°ã®ãƒ—ロトタイプ宣言を行ãªã„ã¾ã™ï¼Ž + + Copyright (C) 2004 Ki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +**---------------------------------------------------------------------------*/ +//#include +//#include "TypeDefs.h" + +typedef UINT8 Uint8; +typedef INT16 Sint16; +typedef INT32 Sint32; +typedef UINT32 Uint32; +typedef UINT8 BOOL; +#define TRUE 1 +#define FALSE 0 + +//#define PSG_FRQ 3579545.0 + + +/*----------------------------------------------------------------------------- +** 関数ã®ãƒ—ロトタイプ宣言を行ãªã„ã¾ã™ï¼Ž +**---------------------------------------------------------------------------*/ +//Sint32 +void* +PSG_Init( + Sint32 clock, + Sint32 sampleRate); + +void +PSG_Deinit(void* chip); + +void +PSG_Mix( +// Sint16* pDst, // 出力先ãƒãƒƒãƒ•ã‚¡ + void* chip, + Sint32** pDst, + Sint32 nSample); // 書ã出ã™ã‚µãƒ³ãƒ—ル数 + +/*void +PSG_SetSampleRate( + Uint32 sampleRate);* + +/*void +PSGDEBUG_ShowRegs();*/ + +Uint8 +PSG_Read(void* chip, Uint32 regNum); + +void +PSG_Write( + void* chip, + Uint32 regNum, + Uint8 data); + +/*Sint32 +PSG_AdvanceClock(Sint32 clock); + +BOOL +PSG_SaveState( + FILE* p); + +BOOL +PSG_LoadState( + FILE* p);*/ + +//Kitao追加。PSGã®ãƒœãƒªãƒ¥ãƒ¼ãƒ ã‚‚個別ã«è¨­å®šå¯èƒ½ã«ã—ãŸã€‚ +/*void +PSG_SetVolume( + Uint32 volume); // 0 - 65535 + +//Kitao追加。ボリュームミュートã€ãƒãƒ¼ãƒ•ãªã©ã‚’ã§ãるよã†ã«ã—ãŸã€‚ +void +PSG_SetVolumeEffect( + Uint32 volumeEffect);*/ + +//Kitao追加 +void +PSG_ResetVolumeReg(void* chip); + +//Kitao追加 +void +PSG_SetMutePsgChannel( + void* chip, + Sint32 num, + BOOL bMute); + +void PSG_SetMuteMask(void* chip, Uint32 MuteMask); + +//Kitao追加 +BOOL +PSG_GetMutePsgChannel( + void* chip, + Sint32 num); + +//Kitao追加。v2.60 +void +PSG_SetHoneyInTheSky( + void* chip, + BOOL bHoneyInTheSky); diff --git a/Frameworks/GME/vgmplay/chips/adlibemu.h b/Frameworks/GME/vgmplay/chips/adlibemu.h new file mode 100644 index 000000000..c43d2190e --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/adlibemu.h @@ -0,0 +1,20 @@ +#if defined(OPLTYPE_IS_OPL2) +#define ADLIBEMU(name) adlib_OPL2_##name +#elif defined(OPLTYPE_IS_OPL3) +#define ADLIBEMU(name) adlib_OPL3_##name +#endif + +typedef void (*ADL_UPDATEHANDLER)(void *param); + +void* ADLIBEMU(init)(UINT32 clock, UINT32 samplerate, + ADL_UPDATEHANDLER UpdateHandler, void* param); +void ADLIBEMU(stop)(void *chip); +void ADLIBEMU(reset)(void *chip); + +void ADLIBEMU(writeIO)(void *chip, UINT32 addr, UINT8 val); +void ADLIBEMU(getsample)(void *chip, INT32 ** sndptr, INT32 numsamples); + +UINT32 ADLIBEMU(reg_read)(void *chip, UINT32 port); +void ADLIBEMU(write_index)(void *chip, UINT32 port, UINT8 val); + +void ADLIBEMU(set_mute_mask)(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/adlibemu_opl2.c b/Frameworks/GME/vgmplay/chips/adlibemu_opl2.c new file mode 100644 index 000000000..1d3a50583 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/adlibemu_opl2.c @@ -0,0 +1,5 @@ +#include "mamedef.h" + +#define OPLTYPE_IS_OPL2 +#include "adlibemu.h" +#include "opl.c" diff --git a/Frameworks/GME/vgmplay/chips/adlibemu_opl3.c b/Frameworks/GME/vgmplay/chips/adlibemu_opl3.c new file mode 100644 index 000000000..f969e0c9a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/adlibemu_opl3.c @@ -0,0 +1,5 @@ +#include "mamedef.h" + +#define OPLTYPE_IS_OPL3 +#include "adlibemu.h" +#include "opl.c" diff --git a/Frameworks/GME/vgmplay/chips/ay8910.c b/Frameworks/GME/vgmplay/chips/ay8910.c new file mode 100644 index 000000000..ed85b1dfd --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ay8910.c @@ -0,0 +1,1354 @@ +/*************************************************************************** + + ay8910.c + + Emulation of the AY-3-8910 / YM2149 sound chip. + + Based on various code snippets by Ville Hallik, Michael Cuddy, + Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria. + + Mostly rewritten by couriersud in 2008 + + Public documentation: + + - http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/ + Die pictures of the AY8910 + + - US Patent 4933980 + + Games using ADSR: gyruss + + A list with more games using ADSR can be found here: + http://mametesters.org/view.php?id=3043 + + TODO: + * The AY8930 has an extended mode which is currently + not emulated. + * The YMZ284 only has one 1 output channel (mixed chan A,B,C). + This should be forced. + * YM2610 & YM2608 will need a separate flag in their config structures + to distinguish between legacy and discrete mode. + + The rewrite also introduces a generic model for the DAC. This model is + not perfect, but allows channel mixing based on a parametrized approach. + This model also allows to factor in different loads on individual channels. + If a better model is developped in the future or better measurements are + available, the driver should be easy to change. The model is described + later. + + In order to not break hundreds of existing drivers by default the flag + AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the + new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff). + In the case of channel mixing, output range is 0...3 * 7fff. + + The main difference between the AY-3-8910 and the YM2149 is, that the + AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by + registers 8 to 10 is "channel off". The YM2149 mentions, that the generated + signal has a 2V DC component. This is confirmed by measurements. The approach + taken here is to assume the 2V DC offset for all outputs for the YM2149. + For the AY-3-8910, an offset is used if envelope is active for a channel. + This is backed by oscilloscope pictures from the datasheet. If a fixed volume + is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings + I found on the web for gyruss indicate, that the AY-3-8910 offset should + be around 0.2V. This will also make sound levels more compatible with + user observations for scramble. + + The Model: + 5V 5V + | | + / | + Volume Level x >---| Z + > Z Pullup Resistor RU + | Z + Z | + Rx Z | + Z | + | | + '-----+--------> >---+----> Output signal + | | + Z Z + Pulldown RD Z Z Load RL + Z Z + | | + GND GND + +Each Volume level x will select a different resistor Rx. Measurements from fpgaarcade.com +where used to calibrate channel mixing for the YM2149. This was done using +a least square approach using a fixed RL of 1K Ohm. + +For the AY measurements cited in e.g. openmsx as "Hacker Kay" for a single +channel were taken. These were normalized to 0 ... 65535 and consequently +adapted to an offset of 0.2V and a VPP of 1.3V. These measurements are in +line e.g. with the formula used by pcmenc for the volume: vol(i) = exp(i/2-7.5). + +The following is documentation from the code moved here and amended to reflect +the changes done: + +Careful studies of the chip output prove that the chip counts up from 0 +until the counter becomes greater or equal to the period. This is an +important difference when the program is rapidly changing the period to +modulate the sound. This is worthwhile noting, since the datasheets +say, that the chip counts down. +Also, note that period = 0 is the same as period = 1. This is mentioned +in the YM2203 data sheets. However, this does NOT apply to the Envelope +period. In that case, period = 0 is half as period = 1. + +Envelope shapes: + C AtAlH + 0 0 x x \___ + 0 1 x x /___ + 1 0 0 0 \\\\ + 1 0 0 1 \___ + 1 0 1 0 \/\/ + 1 0 1 1 \``` + 1 1 0 0 //// + 1 1 0 1 /``` + 1 1 1 0 /\/\ + 1 1 1 1 /___ + +The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it +has twice the steps, happening twice as fast. + +***************************************************************************/ + +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +//#include "cpuintrf.h" +//#include "cpuexec.h" +#include +#include +#include +#include "ay8910.h" + +#define NULL ((void *)0) + +/************************************* + * + * Defines + * + *************************************/ + +#define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */ + +//#define MAX_OUTPUT 0x7fff +#define MAX_OUTPUT 0x4000 +#define NUM_CHANNELS 3 + +/* register id's */ +#define AY_AFINE (0) +#define AY_ACOARSE (1) +#define AY_BFINE (2) +#define AY_BCOARSE (3) +#define AY_CFINE (4) +#define AY_CCOARSE (5) +#define AY_NOISEPER (6) +#define AY_ENABLE (7) +#define AY_AVOL (8) +#define AY_BVOL (9) +#define AY_CVOL (10) +#define AY_EFINE (11) +#define AY_ECOARSE (12) +#define AY_ESHAPE (13) + +#define AY_PORTA (14) +#define AY_PORTB (15) + +#define NOISE_ENABLEQ(_psg, _chan) (((_psg)->regs[AY_ENABLE] >> (3 + _chan)) & 1) +#define TONE_ENABLEQ(_psg, _chan) (((_psg)->regs[AY_ENABLE] >> (_chan)) & 1) +#define TONE_PERIOD(_psg, _chan) ( (_psg)->regs[(_chan) << 1] | (((_psg)->regs[((_chan) << 1) | 1] & 0x0f) << 8) ) +#define NOISE_PERIOD(_psg) ( (_psg)->regs[AY_NOISEPER] & 0x1f) +#define TONE_VOLUME(_psg, _chan) ( (_psg)->regs[AY_AVOL + (_chan)] & 0x0f) +//#define TONE_ENVELOPE(_psg, _chan) (((_psg)->regs[AY_AVOL + (_chan)] >> 4) & (((_psg)->device->type() == AY8914) ? 3 : 1)) +#define TONE_ENVELOPE(_psg, _chan) (((_psg)->regs[AY_AVOL + (_chan)] >> 4) & (((_psg)->chip_type == CHTYPE_AY8914) ? 3 : 1)) +#define ENVELOPE_PERIOD(_psg) (((_psg)->regs[AY_EFINE] | ((_psg)->regs[AY_ECOARSE]<<8))) +#define NOISE_OUTPUT(_psg) ((_psg)->rng & 1) + +#define CHTYPE_AY8910 0x00 +#define CHTYPE_AY8912 0x01 +#define CHTYPE_AY8913 0x02 +#define CHTYPE_AY8930 0x03 +#define CHTYPE_AY8914 0x04 +#define CHTYPE_YM2149 0x10 +#define CHTYPE_YM3439 0x11 +#define CHTYPE_YMZ284 0x12 +#define CHTYPE_YMZ294 0x13 +#define CHTYPE_YM2203 0x20 +#define CHTYPE_YM2608 0x21 +#define CHTYPE_YM2610 0x22 +#define CHTYPE_YM2610B 0x23 + +/************************************* + * + * Type definitions + * + *************************************/ + +typedef struct _ay_ym_param ay_ym_param; +struct _ay_ym_param +{ + double r_up; + double r_down; + int res_count; + double res[32]; +}; + +typedef struct _ay8910_context ay8910_context; +struct _ay8910_context +{ + //const device_config *device; + int streams; + int ready; + //sound_stream *channel; + const ay8910_interface *intf; + INT32 register_latch; + UINT8 regs[16]; + INT32 last_enable; + INT32 count[NUM_CHANNELS]; + UINT8 output[NUM_CHANNELS]; + UINT8 prescale_noise; + INT32 count_noise; + INT32 count_env; + INT8 env_step; + UINT32 env_volume; + UINT8 hold,alternate,attack,holding; + INT32 rng; + UINT8 env_step_mask; + /* init parameters ... */ + int step; + int zero_is_off; + UINT8 vol_enabled[NUM_CHANNELS]; + const ay_ym_param *par; + const ay_ym_param *par_env; + INT32 vol_table[NUM_CHANNELS][16]; + INT32 env_table[NUM_CHANNELS][32]; + INT32 vol3d_table[8*32*32*32]; + //devcb_resolved_read8 portAread; + //devcb_resolved_read8 portBread; + //devcb_resolved_write8 portAwrite; + //devcb_resolved_write8 portBwrite; + UINT8 StereoMask[NUM_CHANNELS]; + UINT32 MuteMsk[NUM_CHANNELS]; + UINT8 chip_type; + UINT8 IsDisabled; + + SRATE_CALLBACK SmpRateFunc; + void* SmpRateData; +}; + +//#define MAX_CHIPS 0x02 +//static ay8910_context AY8910Data[MAX_CHIPS]; + +#define MAX_UPDATE_LEN 0x10 // in samples +static stream_sample_t AYBuf[NUM_CHANNELS][MAX_UPDATE_LEN]; + + +/*INLINE ay8910_context *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_AY8910 || + sound_get_type(device) == SOUND_AY8912 || + sound_get_type(device) == SOUND_AY8913 || + sound_get_type(device) == SOUND_AY8930 || + sound_get_type(device) == SOUND_YM2149 || + sound_get_type(device) == SOUND_YM3439 || + sound_get_type(device) == SOUND_YMZ284 || + sound_get_type(device) == SOUND_YMZ294); + return (ay8910_context *)device->token; +}*/ + + +/************************************* + * + * Static + * + *************************************/ + +static const ay_ym_param ym2149_param = +{ + 630, 801, + 16, + { 73770, 37586, 27458, 21451, 15864, 12371, 8922, 6796, + 4763, 3521, 2403, 1737, 1123, 762, 438, 251 }, +}; + +static const ay_ym_param ym2149_param_env = +{ + 630, 801, + 32, + { 103350, 73770, 52657, 37586, 32125, 27458, 24269, 21451, + 18447, 15864, 14009, 12371, 10506, 8922, 7787, 6796, + 5689, 4763, 4095, 3521, 2909, 2403, 2043, 1737, + 1397, 1123, 925, 762, 578, 438, 332, 251 }, +}; + +#if 0 +/* RL = 1000, Hacker Kay normalized, 2.1V to 3.2V */ +static const ay_ym_param ay8910_param = +{ + 664, 913, + 16, + { 85785, 34227, 26986, 20398, 14886, 10588, 7810, 4856, + 4120, 2512, 1737, 1335, 1005, 747, 586, 451 }, +}; + +/* + * RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V + * These values correspond with guesses based on Gyruss schematics + * They work well with scramble as well. + */ +static const ay_ym_param ay8910_param = +{ + 930, 454, + 16, + { 85066, 34179, 27027, 20603, 15046, 10724, 7922, 4935, + 4189, 2557, 1772, 1363, 1028, 766, 602, 464 }, +}; + +/* + * RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V + * These values correspond with guesses based on Gyruss schematics + * They work well with scramble as well. + */ +static const ay_ym_param ay8910_param = +{ + 1371, 313, + 16, + { 93399, 33289, 25808, 19285, 13940, 9846, 7237, 4493, + 3814, 2337, 1629, 1263, 962, 727, 580, 458 }, +}; + +/* + * RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V + */ +static const ay_ym_param ay8910_param = +{ + 5806, 300, + 16, + { 118996, 42698, 33105, 24770, 17925, 12678, 9331, 5807, + 4936, 3038, 2129, 1658, 1271, 969, 781, 623 } +}; +#endif + +/* + * RL = 2000, Based on Matthew Westcott's measurements from Dec 2001. + * ------------------------------------------------------------------- + * + * http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e + * After what Russell mentioned a couple of weeks back about the lack of + * publicised measurements of AY chip volumes - I've finally got round to + * making these readings, and I'm placing them in the public domain - so + * anyone's welcome to use them in emulators or anything else. + + * To make the readings, I set up the chip to produce a constant voltage on + * channel C (setting bits 2 and 5 of register 6), and varied the amplitude + * (the low 4 bits of register 10). The voltages were measured between the + * channel C output (pin 1) and ground (pin 6). + * + * Level Voltage + * 0 1.147 + * 1 1.162 + * 2 1.169 + * 3 1.178 + * 4 1.192 + * 5 1.213 + * 6 1.238 + * 7 1.299 + * 8 1.336 + * 9 1.457 + * 10 1.573 + * 11 1.707 + * 12 1.882 + * 13 2.06 + * 14 2.32 + * 15 2.58 + * ------------------------------------------------------------------- + * + * The ZX spectrum output circuit was modelled in SwitcherCAD and + * the resistor values below create the voltage levels above. + * RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm. + */ + +static const ay_ym_param ay8910_param = +{ + 800000, 8000000, + 16, + { 15950, 15350, 15090, 14760, 14275, 13620, 12890, 11370, + 10600, 8590, 7190, 5985, 4820, 3945, 3017, 2345 } +}; + +/************************************* + * + * Inline + * + *************************************/ + +INLINE void build_3D_table(double rl, const ay_ym_param *par, const ay_ym_param *par_env, int normalize, double factor, int zero_is_off, INT32 *tab) +{ + int j, j1, j2, j3, e, indx; + double rt, rw, n; + double min = 10.0, max = 0.0; + double *temp; + double spdup; + + temp = (double *)malloc(8*32*32*32*sizeof(*temp)); + + for (e=0; e < 8; e++) + for (j1=0; j1 < 32; j1++) + for (j2=0; j2 < 32; j2++) + for (j3=0; j3 < 32; j3++) + { + if (zero_is_off) + { + n = (j1 != 0 || (e & 0x01)) ? 1 : 0; + n += (j2 != 0 || (e & 0x02)) ? 1 : 0; + n += (j3 != 0 || (e & 0x04)) ? 1 : 0; + } + else + n = 3.0; + + rt = n / par->r_up + 3.0 / par->r_down + 1.0 / rl; + rw = n / par->r_up; + + /*rw += 1.0 / ( (e & 0x01) ? par_env->res[j1] : par->res[j1]); + rt += 1.0 / ( (e & 0x01) ? par_env->res[j1] : par->res[j1]); + rw += 1.0 / ( (e & 0x02) ? par_env->res[j2] : par->res[j2]); + rt += 1.0 / ( (e & 0x02) ? par_env->res[j2] : par->res[j2]); + rw += 1.0 / ( (e & 0x04) ? par_env->res[j3] : par->res[j3]); + rt += 1.0 / ( (e & 0x04) ? par_env->res[j3] : par->res[j3]);*/ + /* this should speed up the initialsation a bit (it calculates 262144 values) */ + spdup = 1.0 / ( (e & 0x01) ? par_env->res[j1] : par->res[j1]); + spdup += 1.0 / ( (e & 0x02) ? par_env->res[j2] : par->res[j2]); + spdup += 1.0 / ( (e & 0x04) ? par_env->res[j3] : par->res[j3]); + if (spdup > 1.0) + { + // Bugfix for spdup being 1.#INF + indx = (e << 15) | (j3<<10) | (j2<<5) | j1; + temp[indx] = 0.0; + continue; + } + rw += spdup; + rt += spdup; + + indx = (e << 15) | (j3<<10) | (j2<<5) | j1; + temp[indx] = rw / rt; + if (temp[indx] < min) + min = temp[indx]; + if (temp[indx] > max) + max = temp[indx]; + } + + if (normalize) + { + for (j=0; j < 32*32*32*8; j++) + //tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min))) * factor; + tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min)) * 0.5) * factor; + } + else + { + for (j=0; j < 32*32*32*8; j++) + tab[j] = MAX_OUTPUT * temp[j]; + } + + /* for (e=0;e<16;e++) printf("%d %d\n",e<<10, tab[e<<10]); */ + + free(temp); +} + +INLINE void build_single_table(double rl, const ay_ym_param *par, int normalize, INT32 *tab, int zero_is_off) +{ + int j; + double rt, rw = 0; + double temp[32], min=10.0, max=0.0; + + for (j=0; j < par->res_count; j++) + { + rt = 1.0 / par->r_down + 1.0 / rl; + + rw = 1.0 / par->res[j]; + rt += 1.0 / par->res[j]; + + if (!(zero_is_off && j == 0)) + { + rw += 1.0 / par->r_up; + rt += 1.0 / par->r_up; + } + + temp[j] = rw / rt; + if (temp[j] < min) + min = temp[j]; + if (temp[j] > max) + max = temp[j]; + } + if (normalize) + { + for (j=0; j < par->res_count; j++) + /* The following line generates values that cause clicks when starting/pausing/stopping + because there're off (the center is at zero, not the base). + That's quite bad for a player. + tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min)) - 0.25) * 0.5; + */ + tab[j] = MAX_OUTPUT * ((temp[j] - min)/(max-min)) / NUM_CHANNELS; + } + else + { + for (j=0; j < par->res_count; j++) + tab[j] = MAX_OUTPUT * temp[j] / NUM_CHANNELS; + } + +} + +INLINE UINT16 mix_3D(ay8910_context *psg) +{ + int indx = 0, chan; + + for (chan = 0; chan < NUM_CHANNELS; chan++) + if (TONE_ENVELOPE(psg, chan) != 0) + { + //if (psg->device->type() == AY8914) // AY8914 Has a two bit tone_envelope field + if (psg->chip_type == CHTYPE_AY8914) // AY8914 Has a two bit tone_envelope field + { + indx |= (1 << (chan+15)) | ( psg->vol_enabled[chan] ? ((psg->env_volume >> (3-TONE_ENVELOPE(psg, chan))) << (chan*5)) : 0); + } + else + { + indx |= (1 << (chan+15)) | ( psg->vol_enabled[chan] ? psg->env_volume << (chan*5) : 0); + } + } + else + { + indx |= (psg->vol_enabled[chan] ? TONE_VOLUME(psg, chan) << (chan*5) : 0); + } + return psg->vol3d_table[indx]; +} + +/************************************* + * + * Static functions + * + *************************************/ + +static void ay8910_write_reg(ay8910_context *psg, int r, int v) +{ + //if (r >= 11 && r <= 13 ) printf("%d %x %02x\n", PSG->index, r, v); + psg->regs[r] = v; + + switch( r ) + { + case AY_AFINE: + case AY_ACOARSE: + case AY_BFINE: + case AY_BCOARSE: + case AY_CFINE: + case AY_CCOARSE: + case AY_NOISEPER: + case AY_AVOL: + case AY_BVOL: + case AY_CVOL: + case AY_EFINE: + /* No action required */ + break; + case AY_ECOARSE: + #ifdef MAME_DEBUG + if ( (v & 0x0f) > 0) + popmessage("Write to ECoarse register detected - please inform www.mametesters.org"); + #endif + /* No action required */ + break; + case AY_ENABLE: + /*if ((psg->last_enable == -1) || + ((psg->last_enable & 0x40) != (psg->regs[AY_ENABLE] & 0x40))) + { + // write out 0xff if port set to input + devcb_call_write8(&psg->portAwrite, 0, (psg->regs[AY_ENABLE] & 0x40) ? psg->regs[AY_PORTA] : 0xff); + } + + if ((psg->last_enable == -1) || + ((psg->last_enable & 0x80) != (psg->regs[AY_ENABLE] & 0x80))) + { + // write out 0xff if port set to input + devcb_call_write8(&psg->portBwrite, 0, (psg->regs[AY_ENABLE] & 0x80) ? psg->regs[AY_PORTB] : 0xff); + }*/ + if (~v & 0x3F) // one of the channels gets enabled -> Enable emulation + psg->IsDisabled = 0x00; + + psg->last_enable = psg->regs[AY_ENABLE]; + break; + case AY_ESHAPE: + #ifdef MAME_DEBUG + if ( (v & 0x0f) > 0) + popmessage("Write to EShape register detected - please inform www.mametesters.org"); + #endif + psg->attack = (psg->regs[AY_ESHAPE] & 0x04) ? psg->env_step_mask : 0x00; + if ((psg->regs[AY_ESHAPE] & 0x08) == 0) + { + /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ + psg->hold = 1; + psg->alternate = psg->attack; + } + else + { + psg->hold = psg->regs[AY_ESHAPE] & 0x01; + psg->alternate = psg->regs[AY_ESHAPE] & 0x02; + } + psg->env_step = psg->env_step_mask; + psg->holding = 0; + psg->env_volume = (psg->env_step ^ psg->attack); + break; + case AY_PORTA: + /*if (psg->regs[AY_ENABLE] & 0x40) + { + if (psg->portAwrite.write) + devcb_call_write8(&psg->portAwrite, 0, psg->regs[AY_PORTA]); + else + logerror("warning - write %02x to 8910 '%s' Port A\n",psg->regs[AY_PORTA],psg->device->tag); + } + else + { + logerror("warning: write to 8910 '%s' Port A set as input - ignored\n",psg->device->tag); + }*/ + break; + case AY_PORTB: + /*if (psg->regs[AY_ENABLE] & 0x80) + { + if (psg->portBwrite.write) + devcb_call_write8(&psg->portBwrite, 0, psg->regs[AY_PORTB]); + else + logerror("warning - write %02x to 8910 '%s' Port B\n",psg->regs[AY_PORTB],psg->device->tag); + } + else + { + logerror("warning: write to 8910 '%s' Port B set as input - ignored\n",psg->device->tag); + }*/ + break; + } +} + +//static STREAM_UPDATE( ay8910_update ) +/*void ay8910_update(UINT8 ChipID, stream_sample_t **outputs, int samples) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + ay8910_update_one(psg, outputs, samples); + + return; +}*/ + +void ay8910_update_one(void *param, stream_sample_t **outputs, int samples) +{ + ay8910_context *psg = (ay8910_context *)param; + stream_sample_t *buf[NUM_CHANNELS]; + int chan; + int cursmpl; + int buf_smpls; + stream_sample_t *bufL = outputs[0]; + stream_sample_t *bufR = outputs[1]; + //stream_sample_t bufSmpl; + + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + + // Speed hack for OPN chips (YM2203, YM26xx), that have an often unused AY8910 + if (psg->IsDisabled) + return; + + buf_smpls = samples; + //buf[0] = outputs[0]; + buf[0] = AYBuf[0]; + buf[1] = NULL; + buf[2] = NULL; + //if (psg->streams == NUM_CHANNELS) + //{ + //buf[1] = outputs[1]; + //buf[2] = outputs[2]; + buf[1] = AYBuf[1]; + buf[2] = AYBuf[2]; + //} + + /* hack to prevent us from hanging when starting filtered outputs */ + //if (!psg->ready) + //{ + for (chan = 0; chan < NUM_CHANNELS; chan++) + if (buf[chan] != NULL) + memset(buf[chan], 0, samples * sizeof(*buf[chan])); + //} + + /* The 8910 has three outputs, each output is the mix of one of the three */ + /* tone generators and of the (single) noise generator. The two are mixed */ + /* BEFORE going into the DAC. The formula to mix each channel is: */ + /* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */ + /* Note that this means that if both tone and noise are disabled, the output */ + /* is 1, not 0, and can be modulated changing the volume. */ + + if (buf_smpls > MAX_UPDATE_LEN) + buf_smpls = MAX_UPDATE_LEN; + /* buffering loop */ + while (buf_smpls) + { + for (chan = 0; chan < NUM_CHANNELS; chan++) + { + psg->count[chan]++; + if (psg->count[chan] >= TONE_PERIOD(psg, chan)) + { + psg->output[chan] ^= 1; + psg->count[chan] = 0; + } + } + + psg->count_noise++; + if (psg->count_noise >= NOISE_PERIOD(psg)) + { + /* toggle the prescaler output. Noise is no different to + * channels. + */ + psg->count_noise = 0; + psg->prescale_noise ^= 1; + + if ( psg->prescale_noise) + { + /* The Random Number Generator of the 8910 is a 17-bit shift */ + /* register. The input to the shift register is bit0 XOR bit3 */ + /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ + + psg->rng ^= (((psg->rng & 1) ^ ((psg->rng >> 3) & 1)) << 17); + psg->rng >>= 1; + } + } + + for (chan = 0; chan < NUM_CHANNELS; chan++) + { + psg->vol_enabled[chan] = (psg->output[chan] | TONE_ENABLEQ(psg, chan)) & (NOISE_OUTPUT(psg) | NOISE_ENABLEQ(psg, chan)); + } + + /* update envelope */ + if (psg->holding == 0) + { + psg->count_env++; + if (psg->count_env >= ENVELOPE_PERIOD(psg) * psg->step ) + { + psg->count_env = 0; + psg->env_step--; + + /* check envelope current position */ + if (psg->env_step < 0) + { + if (psg->hold) + { + if (psg->alternate) + psg->attack ^= psg->env_step_mask; + psg->holding = 1; + psg->env_step = 0; + } + else + { + /* if CountEnv has looped an odd number of times (usually 1), */ + /* invert the output. */ + if (psg->alternate && (psg->env_step & (psg->env_step_mask + 1))) + psg->attack ^= psg->env_step_mask; + + psg->env_step &= psg->env_step_mask; + } + } + + } + } + psg->env_volume = (psg->env_step ^ psg->attack); + + //if (psg->streams == NUM_CHANNELS) + //{ + for (chan = 0; chan < NUM_CHANNELS; chan++) + if (TONE_ENVELOPE(psg, chan) != 0) + { + /* Envolope has no "off" state */ + //if (psg->device->type() == AY8914) // AY8914 Has a two bit tone_envelope field + if (psg->chip_type == CHTYPE_AY8914) // AY8914 Has a two bit tone_envelope field + { + *(buf[chan]++) = psg->env_table[chan][psg->vol_enabled[chan] ? psg->env_volume >> (3-TONE_ENVELOPE(psg,chan)) : 0]; + } + else + { + *(buf[chan]++) = psg->env_table[chan][psg->vol_enabled[chan] ? psg->env_volume : 0]; + } + } + else + { + *(buf[chan]++) = psg->vol_table[chan][psg->vol_enabled[chan] ? TONE_VOLUME(psg, chan) : 0]; + } + /*} + else + { + *(buf[0]++) = mix_3D(psg); +#if 0 + *(buf[0]) = ( vol_enabled[0] * psg->vol_table[psg->Vol[0]] + + vol_enabled[1] * psg->vol_table[psg->Vol[1]] + + vol_enabled[2] * psg->vol_table[psg->Vol[2]]) / psg->step; +#endif + }*/ + buf_smpls--; + + } + + buf_smpls = samples; + if (buf_smpls > MAX_UPDATE_LEN) + buf_smpls = MAX_UPDATE_LEN; + for (cursmpl = 0; cursmpl < buf_smpls; cursmpl ++) + { + /*bufSmpl = AYBuf[0][cursmpl] & psg->MuteMsk[0]; + if (psg->streams == NUM_CHANNELS) + { + bufSmpl += AYBuf[1][cursmpl] & psg->MuteMsk[1]; + bufSmpl += AYBuf[2][cursmpl] & psg->MuteMsk[2]; + } + bufL[cursmpl] += bufSmpl; + bufR[cursmpl] += bufSmpl;*/ + for (chan = 0; chan < NUM_CHANNELS; chan ++) + { + if (psg->MuteMsk[chan]) + { + if (psg->StereoMask[chan] & 0x01) + bufL[cursmpl] += AYBuf[chan][cursmpl]; + if (psg->StereoMask[chan] & 0x02) + bufR[cursmpl] += AYBuf[chan][cursmpl]; + } + } + } +} + +static void build_mixer_table(ay8910_context *psg) +{ + int normalize = 0; + int chan; + + if ((psg->intf->flags & AY8910_LEGACY_OUTPUT) != 0) + { +#ifdef _DEBUG + //logerror("AY-3-8910/YM2149 using legacy output levels!\n"); +#endif + //normalize = 1; + } + normalize = 1; + + /* skip unnecessary things to speed up the AY8910 init + 1-channel AY uses the 3D table, 3-channel AY uses envelope and volume + but building the 3D table still takes too long */ + //if (psg->streams == NUM_CHANNELS) + { + for (chan=0; chan < NUM_CHANNELS; chan++) + { + build_single_table(psg->intf->res_load[chan], psg->par, normalize, psg->vol_table[chan], psg->zero_is_off); + build_single_table(psg->intf->res_load[chan], psg->par_env, normalize, psg->env_table[chan], 0); + } + } + //else + //{ + // /* + // * The previous implementation added all three channels up instead of averaging them. + // * The factor of 3 will force the same levels if normalizing is used. + // */ + // build_3D_table(psg->intf->res_load[0], psg->par, psg->par_env, normalize, 3, psg->zero_is_off, psg->vol3d_table); + //} +} + +/*static void ay8910_statesave(ay8910_context *psg, const device_config *device) +{ + state_save_register_device_item(device, 0, psg->register_latch); + state_save_register_device_item_array(device, 0, psg->regs); + state_save_register_device_item(device, 0, psg->last_enable); + + state_save_register_device_item_array(device, 0, psg->count); + state_save_register_device_item(device, 0, psg->count_noise); + state_save_register_device_item(device, 0, psg->count_env); + + state_save_register_device_item(device, 0, psg->env_volume); + + state_save_register_device_item_array(device, 0, psg->output); + state_save_register_device_item(device, 0, psg->output_noise); + + state_save_register_device_item(device, 0, psg->env_step); + state_save_register_device_item(device, 0, psg->hold); + state_save_register_device_item(device, 0, psg->alternate); + state_save_register_device_item(device, 0, psg->attack); + state_save_register_device_item(device, 0, psg->holding); + state_save_register_device_item(device, 0, psg->rng); +}*/ + +/************************************* + * + * Public functions + * + * used by e.g. YM2203, YM2210 ... + * + *************************************/ + +//void *ay8910_start_ym(void *infoptr, sound_type chip_type, const device_config *device, int clock, const ay8910_interface *intf) +void *ay8910_start_ym(void *infoptr, unsigned char chip_type, int clock, const ay8910_interface *intf) +{ + ay8910_context *info = (ay8910_context *)infoptr; + int master_clock = clock; + UINT8 CurChn; + + if (info == NULL) + { + //info = auto_alloc_clear(device->machine, ay8910_context); + info = (ay8910_context*)malloc(sizeof(ay8910_context)); + memset(info, 0x00, sizeof(ay8910_context)); + } + + //info->device = device; + info->intf = intf; + info->SmpRateFunc = NULL; + //devcb_resolve_read8(&info->portAread, &intf->portAread, device); + //devcb_resolve_read8(&info->portBread, &intf->portBread, device); + //devcb_resolve_write8(&info->portAwrite, &intf->portAwrite, device); + //devcb_resolve_write8(&info->portBwrite, &intf->portBwrite, device); + if ((info->intf->flags & AY8910_SINGLE_OUTPUT) != 0) + { +#ifdef _DEBUG + //logerror("AY-3-8910/YM2149 using single output!\n"); +#endif + //info->streams = 1; + info->streams = 3; // set to 3 to greatly speed up loading times + } + else + info->streams = 3; + + info->chip_type = chip_type; + //if (chip_type == CHTYPE_AY8910 || chip_type == CHTYPE_AY8914 || chip_type == CHTYPE_AY8930) + if ((chip_type & 0xF0) == 0x00) // CHTYPE_AY89xx variants + { + info->step = 2; + info->par = &ay8910_param; + info->par_env = &ay8910_param; + info->zero_is_off = 0; /* FIXME: Remove after verification that off=vol(0) */ + info->env_step_mask = 0x0F; + } + else //if ((chip_type & 0xF0) == 0x10) // CHTYPE_YMxxxx variants (also YM2203/2608/2610) + { + info->step = 1; + info->par = &ym2149_param; + info->par_env = &ym2149_param_env; + info->zero_is_off = 0; + info->env_step_mask = 0x1F; + + /* YM2149 master clock divider? */ + if (info->intf->flags & YM2149_PIN26_LOW) + master_clock /= 2; + } + if (intf->flags & AY8910_ZX_STEREO) + { + // ABC Stereo + info->StereoMask[0] = 0x01; + info->StereoMask[1] = 0x03; + info->StereoMask[2] = 0x02; + } + else + { + info->StereoMask[0] = 0x03; + info->StereoMask[1] = 0x03; + info->StereoMask[2] = 0x03; + } + + build_mixer_table(info); + + /* The envelope is pacing twice as fast for the YM2149 as for the AY-3-8910, */ + /* This handled by the step parameter. Consequently we use a divider of 8 here. */ + //info->channel = stream_create(device, 0, info->streams, device->clock / 8, info, ay8910_update); + + ay8910_set_clock_ym(info,clock); + //ay8910_statesave(info, device); + + for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) + info->MuteMsk[CurChn] = 0; + + return info; +} + +void ay8910_stop_ym(void *chip) +{ + free(chip); +} + +void ay8910_reset_ym(void *chip) +{ + ay8910_context *psg = (ay8910_context *)chip; + int i; + + psg->register_latch = 0; + psg->rng = 1; + psg->output[0] = 0; + psg->output[1] = 0; + psg->output[2] = 0; + psg->count[0] = 0; + psg->count[1] = 0; + psg->count[2] = 0; + psg->count_noise = 0; + psg->count_env = 0; + psg->prescale_noise = 0; + psg->last_enable = -1; /* force a write */ + for (i = 0;i < AY_PORTA;i++) + ay8910_write_reg(psg,i,0); + psg->ready = 1; +#if ENABLE_REGISTER_TEST + ay8910_write_reg(psg, AY_AFINE, 0); + ay8910_write_reg(psg, AY_ACOARSE, 1); + ay8910_write_reg(psg, AY_BFINE, 0); + ay8910_write_reg(psg, AY_BCOARSE, 2); + ay8910_write_reg(psg, AY_CFINE, 0); + ay8910_write_reg(psg, AY_CCOARSE, 4); + //#define AY_NOISEPER (6) + ay8910_write_reg(psg, AY_ENABLE, ~7); + ay8910_write_reg(psg, AY_AVOL, 10); + ay8910_write_reg(psg, AY_BVOL, 10); + ay8910_write_reg(psg, AY_CVOL, 10); + //#define AY_EFINE (11) + //#define AY_ECOARSE (12) + //#define AY_ESHAPE (13) +#endif + if (psg->chip_type & 0x20) + psg->IsDisabled = 0x01; +} + +/*void ay8910_set_volume(UINT8 ChipID,int channel,int volume) +{ + //ay8910_context *psg = get_safe_token(device); + int ch; + + for (ch = 0; ch < psg->streams; ch++) + if (channel == ch || psg->streams == 1 || channel == ALL_8910_CHANNELS) + stream_set_output_gain(psg->channel, ch, volume / 100.0); +}*/ + +void ay8910_set_clock_ym(void *chip, int clock) +{ + ay8910_context *psg = (ay8910_context *)chip; + + if ((psg->chip_type & 0xF0) == 0x10 && (psg->intf->flags & YM2149_PIN26_LOW)) + clock /= 2; + + //stream_set_sample_rate(psg->channel, clock / 8 ); + if (psg->SmpRateFunc != NULL) + psg->SmpRateFunc(psg->SmpRateData, clock / 8); + + return; +} + +void ay8910_write_ym(void *chip, int addr, int data) +{ + ay8910_context *psg = (ay8910_context *)chip; + + if (addr & 1) + { /* Data port */ + int r = psg->register_latch; + + if (r > 15) return; + if (r == AY_ESHAPE || psg->regs[r] != data) + { + /* update the output buffer before changing the register */ + //stream_update(psg->channel); + } + + ay8910_write_reg(psg,r,data); + } + else + { /* Register port */ + psg->register_latch = data & 0x0f; + } +} + +int ay8910_read_ym(void *chip) +{ + ay8910_context *psg = (ay8910_context *)chip; + int r = psg->register_latch; + + if (r > 15) return 0; + + /* There are no state dependent register in the AY8910! */ + /* stream_update(psg->channel); */ + + switch (r) + { + case AY_PORTA: + //if ((psg->regs[AY_ENABLE] & 0x40) != 0) + // logerror("warning: read from 8910 '%s' Port A set as output\n",psg->device->tag); + /* + even if the port is set as output, we still need to return the external + data. Some games, like kidniki, need this to work. + */ + /*if (psg->portAread.read) + psg->regs[AY_PORTA] = devcb_call_read8(&psg->portAread, 0); + else + logerror("%s: warning - read 8910 '%s' Port A\n",cpuexec_describe_context(psg->device->machine),psg->device->tag);*/ + break; + case AY_PORTB: + //if ((psg->regs[AY_ENABLE] & 0x80) != 0) + // logerror("warning: read from 8910 '%s' Port B set as output\n",psg->device->tag); + /*if (psg->portBread.read) + psg->regs[AY_PORTB] = devcb_call_read8(&psg->portBread, 0); + else + logerror("%s: warning - read 8910 '%s' Port B\n",cpuexec_describe_context(psg->device->machine),psg->device->tag);*/ + break; + } + + /* Depending on chip type, unused bits in registers may or may not be accessible. + Untested chips are assumed to regard them as 'ram' + Tested and confirmed on hardware: + - AY-3-8910: inaccessible bits (see masks below) read back as 0 + - YM2149: no anomaly + */ + if (! (psg->chip_type & 0x10)) { + const UINT8 mask[0x10]={ + 0xff,0x0f,0xff,0x0f,0xff,0x0f,0x1f,0xff,0x1f,0x1f,0x1f,0xff,0xff,0x0f,0xff,0xff + }; + + return psg->regs[r] & mask[r]; + } + else return psg->regs[r]; +} + +/************************************* + * + * Sound Interface + * + *************************************/ + +//static DEVICE_START( ay8910 ) +/*int device_start_ay8910(UINT8 ChipID, int clock, unsigned char chip_type, unsigned char Flags) +{ + static const ay8910_interface generic_ay8910 = + { + AY8910_LEGACY_OUTPUT, + AY8910_DEFAULT_LOADS + }; + //const ay8910_interface *intf = (device->static_config ? (const ay8910_interface *)device->static_config : &generic_ay8910); + ay8910_interface intf = generic_ay8910; + ay8910_context *psg; + + if (ChipID >= MAX_CHIPS) + return 0; + + psg = &AY8910Data[ChipID]; + intf.flags = Flags; + ay8910_start_ym(psg, chip_type, clock, &intf); + + return clock / 8; +}*/ +int ay8910_start(void **chip, int clock, UINT8 chip_type, UINT8 Flags) +{ + static const ay8910_interface generic_ay8910 = + { + AY8910_LEGACY_OUTPUT, + AY8910_DEFAULT_LOADS + }; + ay8910_interface intf = generic_ay8910; + ay8910_context *psg = (ay8910_context*)chip; + + psg = (ay8910_context*)malloc(sizeof(ay8910_context)); + if(psg == NULL) + return 0; + memset(psg, 0x00, sizeof(ay8910_context)); + *chip = psg; + + intf.flags = Flags; + ay8910_start_ym(psg, chip_type, clock, &intf); + + if (Flags & YM2149_PIN26_LOW) + return clock / 16; + else + return clock / 8; +} + +/*static DEVICE_START( ym2149 ) +{ + static const ay8910_interface generic_ay8910 = + { + AY8910_LEGACY_OUTPUT, + AY8910_DEFAULT_LOADS, + DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL + }; + const ay8910_interface *intf = (device->static_config ? (const ay8910_interface *)device->static_config : &generic_ay8910); + ay8910_start_ym(get_safe_token(device), SOUND_YM2149, device, device->clock, intf); +}*/ + +//static DEVICE_STOP( ay8910 ) +/*void device_stop_ay8910(UINT8 ChipID) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + ay8910_stop_ym(psg); +} + +//static DEVICE_RESET( ay8910 ) +void device_reset_ay8910(UINT8 ChipID) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + ay8910_reset_ym(psg); +}*/ + +/*DEVICE_GET_INFO( ay8910 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ay8910_context); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ay8910 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ay8910 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "AY-3-8910A"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "PSG"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + +DEVICE_GET_INFO( ay8912 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "AY-3-8912A"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +} + +DEVICE_GET_INFO( ay8913 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "AY-3-8913A"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +} + +DEVICE_GET_INFO( ay8930 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ay8910 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "AY8930"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +} + +DEVICE_GET_INFO( ym2149 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "YM2149"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +} + +DEVICE_GET_INFO( ym3439 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "YM3439"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +} + +DEVICE_GET_INFO( ymz284 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "YMZ284"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +} + +DEVICE_GET_INFO( ymz294 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2149 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "YMZ294"); break; + default: DEVICE_GET_INFO_CALL(ay8910); break; + } +}*/ + +/************************************* + * + * Read/Write Handlers + * + *************************************/ + +//READ8_DEVICE_HANDLER( ay8910_r ) +/*UINT8 ay8910_r(UINT8 ChipID, offs_t offset) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + return ay8910_read_ym(psg); +} + +//WRITE8_DEVICE_HANDLER( ay8910_data_address_w ) +void ay8910_data_address_w(UINT8 ChipID, offs_t offset, UINT8 data) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + // note that directly connecting BC1 to A0 puts data on 0 and address on 1 + ay8910_write_ym(psg, ~offset & 1, data); +} + +//WRITE8_DEVICE_HANDLER( ay8910_address_data_w ) +void ay8910_address_data_w(UINT8 ChipID, offs_t offset, UINT8 data) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + ay8910_write_ym(psg, offset & 1, data); +} + +//WRITE8_DEVICE_HANDLER( ay8910_address_w ) +void ay8910_address_w(UINT8 ChipID, offs_t offset, UINT8 data) +{ +#if ENABLE_REGISTER_TEST + return; +#endif + ay8910_data_address_w(ChipID, 1, data); +} + +//WRITE8_DEVICE_HANDLER( ay8910_data_w ) +void ay8910_data_w(UINT8 ChipID, offs_t offset, UINT8 data) +{ +#if ENABLE_REGISTER_TEST + return; +#endif + ay8910_data_address_w(ChipID, 0, data); +}*/ + + +void ay8910_set_mute_mask_ym(void *chip, UINT32 MuteMask) +{ + ay8910_context *psg = (ay8910_context *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) + psg->MuteMsk[CurChn] = (MuteMask & (1 << CurChn)) ? 0 : ~0; + + return; +} + +/*void ay8910_set_mute_mask(UINT8 ChipID, UINT32 MuteMask) +{ + ay8910_context *psg = &AY8910Data[ChipID]; + ay8910_set_mute_mask_ym(psg, MuteMask); +}*/ + +void ay8910_set_srchg_cb_ym(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr) +{ + ay8910_context *info = (ay8910_context *)chip; + + // set Sample Rate Change Callback routine + info->SmpRateFunc = CallbackFunc; + info->SmpRateData = DataPtr; + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/ay8910.h b/Frameworks/GME/vgmplay/chips/ay8910.h new file mode 100644 index 000000000..34aa139c1 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ay8910.h @@ -0,0 +1,143 @@ +#pragma once + +//#include "devcb.h" +#define DEVCB_TYPE_NULL (0) +#define DEVCB_NULL { DEVCB_TYPE_NULL } + +/* +AY-3-8910A: 2 I/O ports +AY-3-8912A: 1 I/O port +AY-3-8913A: 0 I/O port +AY8930: upper compatible with 8910. +In extended mode, it has higher resolution and duty ratio setting +YM2149: higher resolution +YM3439: same as 2149 +YMZ284: 0 I/O port, different clock divider +YMZ294: 0 I/O port +*/ + +#define ALL_8910_CHANNELS -1 + +/* Internal resistance at Volume level 7. */ + +#define AY8910_INTERNAL_RESISTANCE (356) +#define YM2149_INTERNAL_RESISTANCE (353) + +/* + * Default values for resistor loads. + * The macro should be used in AY8910interface if + * the real values are unknown. + */ +#define AY8910_DEFAULT_LOADS {1000, 1000, 1000} + +/* + * The following is used by all drivers not reviewed yet. + * This will like the old behaviour, output between + * 0 and 7FFF + */ +#define AY8910_LEGACY_OUTPUT (1) + +/* + * Specifing the next define will simulate the special + * cross channel mixing if outputs are tied together. + * The driver will only provide one stream in this case. + */ +#define AY8910_SINGLE_OUTPUT (2) + +/* + * The follwoing define is the default behaviour. + * Output level 0 is 0V and 7ffff corresponds to 5V. + * Use this to specify that a discrete mixing stage + * follows. + */ +#define AY8910_DISCRETE_OUTPUT (4) + +/* + * The follwoing define causes the driver to output + * raw volume levels, i.e. 0 .. 15 and 0..31. + * This is intended to be used in a subsequent + * mixing modul (i.e. mpatrol ties 6 channels from + * AY-3-8910 together). Do not use it now. + */ +/* TODO: implement mixing module */ +#define AY8910_RAW_OUTPUT (8) + +#define AY8910_ZX_STEREO 0x80 +/* +* This define specifies the initial state of YM2149 +* pin 26 (SEL pin). By default it is set to high, +* compatible with AY8910. +*/ +/* TODO: make it controllable while it's running (used by any hw???) */ +#define YM2149_PIN26_HIGH (0x00) /* or N/C */ +#define YM2149_PIN26_LOW (0x10) + +typedef struct _ay8910_interface ay8910_interface; +struct _ay8910_interface +{ + int flags; /* Flags */ + int res_load[3]; /* Load on channel in ohms */ + //devcb_read8 portAread; + //devcb_read8 portBread; + //devcb_write8 portAwrite; + //devcb_write8 portBwrite; +}; + + +//void ay8910_set_volume(UINT8 ChipID,int channel,int volume); + +/*READ8_DEVICE_HANDLER( ay8910_r ); +WITE8_DEVICE_HANDLER( ay8910_address_w ); +WRITE8_DEVICE_HANDLER( ay8910_data_w );*/ +/*UINT8 ay8910_r(UINT8 ChipID, offs_t offset); +void ay8910_address_w(UINT8 ChipID, offs_t offset, UINT8 data); +void ay8910_data_w(UINT8 ChipID, offs_t offset, UINT8 data);*/ + +/* use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' */ +//WRITE8_DEVICE_HANDLER( ay8910_data_address_w ); +//void ay8910_data_address_w(UINT8 ChipID, offs_t offset, UINT8 data); + +/* use this when BC1 == !A0; here, BC1=0 selects 'latch address' and BC1=1 selects 'data' */ +//WRITE8_DEVICE_HANDLER( ay8910_address_data_w ); +//void ay8910_address_data_w(UINT8 ChipID, offs_t offset, UINT8 data); + + +/*********** An interface for SSG of YM2203 ***********/ + +//void *ay8910_start_ym(void *infoptr, sound_type chip_type, const device_config *device, int clock, const ay8910_interface *intf); +void *ay8910_start_ym(void *infoptr, unsigned char chip_type, int clock, const ay8910_interface *intf); + +void ay8910_stop_ym(void *chip); +void ay8910_reset_ym(void *chip); +void ay8910_set_clock_ym(void *chip, int clock); +void ay8910_write_ym(void *chip, int addr, int data); +int ay8910_read_ym(void *chip); + +//void ay8910_update(UINT8 ChipID, stream_sample_t **outputs, int samples); +void ay8910_update_one(void *param, stream_sample_t **outputs, int samples); +int ay8910_start(void **chip, int clock, UINT8 chip_type, UINT8 Flags); +/*int device_start_ay8910(UINT8 ChipID, int clock, unsigned char chip_type, unsigned char Flags); +void device_stop_ay8910(UINT8 ChipID); +void device_reset_ay8910(UINT8 ChipID);*/ + +void ay8910_set_mute_mask_ym(void *chip, UINT32 MuteMask); +//void ay8910_set_mute_mask(UINT8 ChipID, UINT32 MuteMask); +void ay8910_set_srchg_cb_ym(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr); + +/*DEVICE_GET_INFO( ay8910 ); +DEVICE_GET_INFO( ay8912 ); +DEVICE_GET_INFO( ay8913 ); +DEVICE_GET_INFO( ay8930 ); +DEVICE_GET_INFO( ym2149 ); +DEVICE_GET_INFO( ym3439 ); +DEVICE_GET_INFO( ymz284 ); +DEVICE_GET_INFO( ymz294 ); + +#define SOUND_AY8910 DEVICE_GET_INFO_NAME( ay8910 ) +#define SOUND_AY8912 DEVICE_GET_INFO_NAME( ay8912 ) +#define SOUND_AY8913 DEVICE_GET_INFO_NAME( ay8913 ) +#define SOUND_AY8930 DEVICE_GET_INFO_NAME( ay8930 ) +#define SOUND_YM2149 DEVICE_GET_INFO_NAME( ym2149 ) +#define SOUND_YM3439 DEVICE_GET_INFO_NAME( ym3439 ) +#define SOUND_YMZ284 DEVICE_GET_INFO_NAME( ymz284 ) +#define SOUND_YMZ294 DEVICE_GET_INFO_NAME( ymz294 )*/ diff --git a/Frameworks/GME/vgmplay/chips/ay_intf.c b/Frameworks/GME/vgmplay/chips/ay_intf.c new file mode 100644 index 000000000..37702e2b2 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ay_intf.c @@ -0,0 +1,160 @@ +/**************************************************************** + + MAME / MESS functions + +****************************************************************/ + +#include // for memset +#include // for free +#include // for NULL +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "ay8910.h" // must be always included (for YM2149_PIN26_LOW) +#include "emu2149.h" +#include "ay_intf.h" + + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // AY8910 core from MAME +#endif +#define EC_EMU2149 0x00 // EMU2149 from NSFPlay + + +/* for stream system */ +typedef struct _ayxx_state ayxx_state; +struct _ayxx_state +{ + void *chip; + int EMU_CORE; +}; + +void ayxx_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + ayxx_state *info = (ayxx_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_update_one(info->chip, outputs, samples); + break; +#endif + case EC_EMU2149: + PSG_calc_stereo((PSG*)info->chip, outputs, samples); + break; + } +} + +int device_start_ayxx(void **_info, int EMU_CORE, int clock, UINT8 chip_type, UINT8 Flags, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + ayxx_state *info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_EMU2149; +#else + EMU_CORE = EC_EMU2149; +#endif + + info = (ayxx_state *) calloc(1, sizeof(ayxx_state)); + *_info = (void *) info; + + info->EMU_CORE = EMU_CORE; + if (Flags & YM2149_PIN26_LOW) + rate = clock / 16; + else + rate = clock / 8; + if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + + switch(EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + rate = ay8910_start(&info->chip, clock, chip_type, Flags); + break; +#endif + case EC_EMU2149: + if (Flags & YM2149_PIN26_LOW) + clock /= 2; + info->chip = PSG_new(clock, rate); + if (info->chip == NULL) + return 0; + PSG_setVolumeMode((PSG*)info->chip, (chip_type & 0x10) ? 1 : 2); + PSG_setFlags((PSG*)info->chip, Flags & ~YM2149_PIN26_LOW); + break; + } + + return rate; +} + +void device_stop_ayxx(void *_info) +{ + ayxx_state *info = (ayxx_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_stop_ym(info->chip); + break; +#endif + case EC_EMU2149: + PSG_delete((PSG*)info->chip); + break; + } + info->chip = NULL; + free(info); +} + +void device_reset_ayxx(void *_info) +{ + ayxx_state *info = (ayxx_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_reset_ym(info->chip); + break; +#endif + case EC_EMU2149: + PSG_reset((PSG*)info->chip); + break; + } +} + + +void ayxx_w(void *_info, offs_t offset, UINT8 data) +{ + ayxx_state *info = (ayxx_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_write_ym(info->chip, offset, data); + break; +#endif + case EC_EMU2149: + PSG_writeIO((PSG*)info->chip, offset, data); + break; + } +} + +void ayxx_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ayxx_state *info = (ayxx_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + ay8910_set_mute_mask_ym(info->chip, MuteMask); + break; +#endif + case EC_EMU2149: + PSG_setMask((PSG*)info->chip, MuteMask); + break; + } + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/ay_intf.h b/Frameworks/GME/vgmplay/chips/ay_intf.h new file mode 100644 index 000000000..348dc5f44 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ay_intf.h @@ -0,0 +1,11 @@ +#pragma once + +void ayxx_stream_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_ayxx(void **chip, int core, int clock, UINT8 chip_type, UINT8 Flags, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_ayxx(void *chip); +void device_reset_ayxx(void *chip); + +void ayxx_w(void *chip, offs_t offset, UINT8 data); + +void ayxx_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/gme/c140.c b/Frameworks/GME/vgmplay/chips/c140.c similarity index 89% rename from Frameworks/GME/gme/c140.c rename to Frameworks/GME/vgmplay/chips/c140.c index f0b4add98..fd81c17d8 100644 --- a/Frameworks/GME/gme/c140.c +++ b/Frameworks/GME/vgmplay/chips/c140.c @@ -1,621 +1,637 @@ -/* -C140.c - -Simulator based on AMUSE sources. -The C140 sound chip is used by Namco System 2 and System 21 -The 219 ASIC (which incorporates a modified C140) is used by Namco NA-1 and NA-2 -This chip controls 24 channels (C140) or 16 (219) of PCM. -16 bytes are associated with each channel. -Channels can be 8 bit signed PCM, or 12 bit signed PCM. - -Timer behavior is not yet handled. - -Unmapped registers: - 0x1f8:timer interval? (Nx0.1 ms) - 0x1fa:irq ack? timer restart? - 0x1fe:timer switch?(0:off 1:on) - --------------- - - ASIC "219" notes - - On the 219 ASIC used on NA-1 and NA-2, the high registers have the following - meaning instead: - 0x1f7: bank for voices 0-3 - 0x1f1: bank for voices 4-7 - 0x1f3: bank for voices 8-11 - 0x1f5: bank for voices 12-15 - - Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers - mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15. - - Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2. - Additionally, the 219's base pitch is the same as the C352's (42667). But these changes - are IMO not sufficient to make this a separate file - all the other registers are - fully compatible. - - Finally, the 219 only has 16 voices. -*/ -/* - 2000.06.26 CAB fixed compressed pcm playback - 2002.07.20 R.Belmont added support for multiple banking types - 2006.01.08 R.Belmont added support for NA-1/2 "219" derivative -*/ - - -//#include "emu.h" -#include -#include -#include "c140.h" - -#undef NULL -#define NULL ((void *)0) - -#define MAX_VOICE 24 - -struct voice_registers -{ - UINT8 volume_right; - UINT8 volume_left; - UINT8 frequency_msb; - UINT8 frequency_lsb; - UINT8 bank; - UINT8 mode; - UINT8 start_msb; - UINT8 start_lsb; - UINT8 end_msb; - UINT8 end_lsb; - UINT8 loop_msb; - UINT8 loop_lsb; - UINT8 reserved[4]; -}; - -typedef struct -{ - long ptoffset; - long pos; - long key; - //--work - long lastdt; - long prevdt; - long dltdt; - //--reg - long rvol; - long lvol; - long frequency; - long bank; - long mode; - - long sample_start; - long sample_end; - long sample_loop; - UINT8 Muted; -} VOICE; - -typedef struct _c140_state c140_state; -struct _c140_state -{ - int sample_rate; - //sound_stream *stream; - int banking_type; - /* internal buffers */ - INT16 *mixer_buffer_left; - INT16 *mixer_buffer_right; - - int baserate; - UINT32 pRomSize; - void *pRom; - UINT8 REG[0x200]; - - INT16 pcmtbl[8]; //2000.06.26 CAB - - VOICE voi[MAX_VOICE]; -}; - -/*INLINE c140_state *get_safe_token(device_t *device) -{ - assert(device != NULL); - assert(device->type() == C140); - return (c140_state *)downcast(device)->token(); -}*/ - - -static void init_voice( VOICE *v ) -{ - v->key=0; - v->ptoffset=0; - v->rvol=0; - v->lvol=0; - v->frequency=0; - v->bank=0; - v->mode=0; - v->sample_start=0; - v->sample_end=0; - v->sample_loop=0; -} -//READ8_DEVICE_HANDLER( c140_r ) -UINT8 c140_r(void *chip, offs_t offset) -{ - //c140_state *info = get_safe_token(device); - c140_state *info = (c140_state *) chip; - offset&=0x1ff; - return info->REG[offset]; -} - -/* - find_sample: compute the actual address of a sample given it's - address and banking registers, as well as the board type. - - I suspect in "real life" this works like the Sega MultiPCM where the banking - is done by a small PAL or GAL external to the sound chip, which can be switched - per-game or at least per-PCB revision as addressing range needs grow. - */ -static long find_sample(c140_state *info, long adrs, long bank, int voice) -{ - long newadr = 0; - - static const INT16 asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 }; - - adrs=(bank<<16)+adrs; - - switch (info->banking_type) - { - case C140_TYPE_SYSTEM2: - // System 2 banking - newadr = ((adrs&0x200000)>>2)|(adrs&0x7ffff); - break; - - case C140_TYPE_SYSTEM21_A: - // System 21 type A (simple) banking. - // similar to System 2's. - newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff); - break; - - case C140_TYPE_SYSTEM21_B: - // System 21 type B (chip select) banking - - // get base address of sample inside the bank - newadr = ((adrs&0x100000)>>2) + (adrs&0x3ffff); - - // now add the starting bank offsets based on the 2 - // chip select bits. - // 0x40000 picks individual 512k ROMs - if (adrs & 0x40000) - { - newadr += 0x80000; - } - - // and 0x200000 which group of chips... - if (adrs & 0x200000) - { - newadr += 0x100000; - } - break; - - case C140_TYPE_ASIC219: - // ASIC219's banking is fairly simple - newadr = ((info->REG[asic219banks[voice/4]]&0x3) * 0x20000) + adrs; - break; - } - - return (newadr); -} -//WRITE8_DEVICE_HANDLER( c140_w ) -void c140_w(void *chip, offs_t offset, UINT8 data) -{ - //c140_state *info = get_safe_token(device); - c140_state *info = (c140_state *) chip; - //info->stream->update(); - - offset&=0x1ff; - - // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE) - if ((offset >= 0x1f8) && (info->banking_type == C140_TYPE_ASIC219)) - { - offset -= 8; - } - - info->REG[offset]=data; - if( offset<0x180 ) - { - VOICE *v = &info->voi[offset>>4]; - - if( (offset&0xf)==0x5 ) - { - if( data&0x80 ) - { - const struct voice_registers *vreg = (struct voice_registers *) &info->REG[offset&0x1f0]; - v->key=1; - v->ptoffset=0; - v->pos=0; - v->lastdt=0; - v->prevdt=0; - v->dltdt=0; - v->bank = vreg->bank; - v->mode = data; - - // on the 219 asic, addresses are in words - if (info->banking_type == C140_TYPE_ASIC219) - { - v->sample_loop = (vreg->loop_msb*256 + vreg->loop_lsb)*2; - v->sample_start = (vreg->start_msb*256 + vreg->start_lsb)*2; - v->sample_end = (vreg->end_msb*256 + vreg->end_lsb)*2; - - #if 0 - logerror("219: play v %d mode %02x start %x loop %x end %x\n", - offset>>4, v->mode, - find_sample(info, v->sample_start, v->bank, offset>>4), - find_sample(info, v->sample_loop, v->bank, offset>>4), - find_sample(info, v->sample_end, v->bank, offset>>4)); - #endif - } - else - { - v->sample_loop = vreg->loop_msb*256 + vreg->loop_lsb; - v->sample_start = vreg->start_msb*256 + vreg->start_lsb; - v->sample_end = vreg->end_msb*256 + vreg->end_lsb; - } - } - else - { - v->key=0; - } - } - } -} - -//void c140_set_base(device_t *device, void *base) -void c140_set_base(void *chip, void *base) -{ - //c140_state *info = get_safe_token(device); - c140_state *info = (c140_state *) chip; - info->pRom = base; -} - -/*INLINE int limit(INT32 in) -{ - if(in>0x7fff) return 0x7fff; - else if(in<-0x8000) return -0x8000; - return in; -}*/ - -//static STREAM_UPDATE( update_stereo ) -void c140_update(void *chip, stream_sample_t **outputs, int samples) -{ - //c140_state *info = (c140_state *)param; - c140_state *info = (c140_state *) chip; - int i,j; - - INT32 rvol,lvol; - INT32 dt; - INT32 sdt; - INT32 st,ed,sz; - - INT8 *pSampleData; - INT32 frequency,delta,offset,pos; - INT32 cnt, voicecnt; - INT32 lastdt,prevdt,dltdt; - float pbase=(float)info->baserate*2.0f / (float)info->sample_rate; - - INT16 *lmix, *rmix; - - if(samples>info->sample_rate) samples=info->sample_rate; - - /* zap the contents of the mixer buffer */ - memset(info->mixer_buffer_left, 0, samples * sizeof(INT16)); - memset(info->mixer_buffer_right, 0, samples * sizeof(INT16)); - if (info->pRom == NULL) - return; - - /* get the number of voices to update */ - voicecnt = (info->banking_type == C140_TYPE_ASIC219) ? 16 : 24; - - //--- audio update - for( i=0;ivoi[i]; - const struct voice_registers *vreg = (struct voice_registers *)&info->REG[i*16]; - - if( v->key && ! v->Muted) - { - frequency= vreg->frequency_msb*256 + vreg->frequency_lsb; - - /* Abort voice if no frequency value set */ - if(frequency==0) continue; - - /* Delta = frequency * ((8MHz/374)*2 / sample rate) */ - delta=(long)((float)frequency * pbase); - - /* Calculate left/right channel volumes */ - lvol=(vreg->volume_left*32)/MAX_VOICE; //32ch -> 24ch - rvol=(vreg->volume_right*32)/MAX_VOICE; - - /* Set mixer outputs base pointers */ - lmix = info->mixer_buffer_left; - rmix = info->mixer_buffer_right; - - /* Retrieve sample start/end and calculate size */ - st=v->sample_start; - ed=v->sample_end; - sz=ed-st; - - /* Retrieve base pointer to the sample data */ - //pSampleData=(signed char*)((FPTR)info->pRom + find_sample(info, st, v->bank, i)); - pSampleData = (INT8*)info->pRom + find_sample(info, st, v->bank, i); - - /* Fetch back previous data pointers */ - offset=v->ptoffset; - pos=v->pos; - lastdt=v->lastdt; - prevdt=v->prevdt; - dltdt=v->dltdt; - - /* Switch on data type - compressed PCM is only for C140 */ - if ((v->mode&8) && (info->banking_type != C140_TYPE_ASIC219)) - { - //compressed PCM (maybe correct...) - /* Loop for enough to fill sample buffer as requested */ - for(j=0;j>16)&0x7fff; - offset &= 0xffff; - pos+=cnt; - //for(;cnt>0;cnt--) - { - /* Check for the end of the sample */ - if(pos >= sz) - { - /* Check if its a looping sample, either stop or loop */ - if(v->mode&0x10) - { - pos = (v->sample_loop - st); - } - else - { - v->key=0; - break; - } - } - - /* Read the chosen sample byte */ - dt=pSampleData[pos]; - - /* decompress to 13bit range */ //2000.06.26 CAB - sdt=dt>>3; //signed - if(sdt<0) sdt = (sdt<<(dt&7)) - info->pcmtbl[dt&7]; - else sdt = (sdt<<(dt&7)) + info->pcmtbl[dt&7]; - - prevdt=lastdt; - lastdt=sdt; - dltdt=(lastdt - prevdt); - } - - /* Caclulate the sample value */ - dt=((dltdt*offset)>>16)+prevdt; - - /* Write the data to the sample buffers */ - *lmix++ +=(dt*lvol)>>(5+5); - *rmix++ +=(dt*rvol)>>(5+5); - } - } - else - { - /* linear 8bit signed PCM */ - for(j=0;j>16)&0x7fff; - offset &= 0xffff; - pos += cnt; - /* Check for the end of the sample */ - if(pos >= sz) - { - /* Check if its a looping sample, either stop or loop */ - if( v->mode&0x10 ) - { - pos = (v->sample_loop - st); - } - else - { - v->key=0; - break; - } - } - - if( cnt ) - { - prevdt=lastdt; - - if (info->banking_type == C140_TYPE_ASIC219) - { - //lastdt = pSampleData[BYTE_XOR_BE(pos)]; - lastdt = pSampleData[pos ^ 0x01]; - - // Sign + magnitude format - if ((v->mode & 0x01) && (lastdt & 0x80)) - lastdt = -(lastdt & 0x7f); - - // Sign flip - if (v->mode & 0x40) - lastdt = -lastdt; - } - else - { - lastdt=pSampleData[pos]; - } - - dltdt = (lastdt - prevdt); - } - - /* Caclulate the sample value */ - dt=((dltdt*offset)>>16)+prevdt; - - /* Write the data to the sample buffers */ - *lmix++ +=(dt*lvol)>>5; - *rmix++ +=(dt*rvol)>>5; - } - } - - /* Save positional data for next callback */ - v->ptoffset=offset; - v->pos=pos; - v->lastdt=lastdt; - v->prevdt=prevdt; - v->dltdt=dltdt; - } - } - - /* render to MAME's stream buffer */ - lmix = info->mixer_buffer_left; - rmix = info->mixer_buffer_right; - { - stream_sample_t *dest1 = outputs[0]; - stream_sample_t *dest2 = outputs[1]; - for (i = 0; i < samples; i++) - { - //*dest1++ = limit(8*(*lmix++)); - //*dest2++ = limit(8*(*rmix++)); - *dest1++ = 8 * (*lmix ++); - *dest2++ = 8 * (*rmix ++); - } - } -} - -//static DEVICE_START( c140 ) -void * device_start_c140(int sample_rate, int clock, int banking_type) -{ - //const c140_interface *intf = (const c140_interface *)device->static_config(); - //c140_state *info = get_safe_token(device); - c140_state *info; - int i; - - info = (c140_state *) malloc(sizeof(c140_state)); - if (!info) return info; - - //info->sample_rate=info->baserate=device->clock(); - info->sample_rate = sample_rate; - info->baserate = clock; - - //info->banking_type = intf->banking_type; - info->banking_type = banking_type; - - //info->stream = device->machine().sound().stream_alloc(*device,0,2,info->sample_rate,info,update_stereo); - - //info->pRom=*device->region(); - info->pRomSize = 0x00; - info->pRom = NULL; - - /* make decompress pcm table */ //2000.06.26 CAB - { - INT32 segbase=0; - for(i=0;i<8;i++) - { - info->pcmtbl[i]=segbase; //segment base value - segbase += 16<REG,0,sizeof(info->REG)); - { - int i; - for(i=0;ivoi[i] ); - }*/ - - /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */ - //info->mixer_buffer_left = auto_alloc_array(device->machine(), INT16, 2 * info->sample_rate); - info->mixer_buffer_left = (INT16*)malloc(sizeof(INT16) * 2 * info->sample_rate); - info->mixer_buffer_right = info->mixer_buffer_left + info->sample_rate; - - for (i = 0; i < MAX_VOICE; i ++) - info->voi[i].Muted = 0x00; - - return info; -} - -void device_stop_c140(void *chip) -{ - c140_state *info = (c140_state *) chip; - - free(info->pRom); info->pRom = NULL; - free(info->mixer_buffer_left); - free(info); -} - -void device_reset_c140(void *chip) -{ - c140_state *info = (c140_state *) chip; - int i; - - memset(info->REG, 0, sizeof(info->REG)); - - for(i = 0; i < MAX_VOICE; i ++) - init_voice( &info->voi[i] ); - - return; -} - -void c140_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, - const UINT8* ROMData) -{ - c140_state *info = (c140_state *) chip; - - if (info->pRomSize != ROMSize) - { - info->pRom = (UINT8*)realloc(info->pRom, ROMSize); - info->pRomSize = ROMSize; - memset(info->pRom, 0xFF, ROMSize); - } - if (DataStart > ROMSize) - return; - if (DataStart + DataLength > ROMSize) - DataLength = ROMSize - DataStart; - - memcpy((INT8*)info->pRom + DataStart, ROMData, DataLength); - - return; -} - - -void c140_set_mute_mask(void *chip, UINT32 MuteMask) -{ - c140_state *info = (c140_state *) chip; - UINT8 CurChn; - - for (CurChn = 0; CurChn < MAX_VOICE; CurChn ++) - info->voi[CurChn].Muted = (MuteMask >> CurChn) & 0x01; - - return; -} - - - - -/************************************************************************** - * Generic get_info - **************************************************************************/ - -/*DEVICE_GET_INFO( c140 ) -{ - switch (state) - { - // --- the following bits of info are returned as 64-bit signed integers --- // - case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c140_state); break; - - // --- the following bits of info are returned as pointers to data or functions --- // - case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c140 ); break; - case DEVINFO_FCT_STOP: // nothing // break; - case DEVINFO_FCT_RESET: // nothing // break; - - // --- the following bits of info are returned as NULL-terminated strings --- // - case DEVINFO_STR_NAME: strcpy(info->s, "C140"); break; - case DEVINFO_STR_FAMILY: strcpy(info->s, "Namco PCM"); break; - case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; - case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; - case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; - } -} - - -DEFINE_LEGACY_SOUND_DEVICE(C140, c140);*/ +/* +C140.c + +Simulator based on AMUSE sources. +The C140 sound chip is used by Namco System 2 and System 21 +The 219 ASIC (which incorporates a modified C140) is used by Namco NA-1 and NA-2 +This chip controls 24 channels (C140) or 16 (219) of PCM. +16 bytes are associated with each channel. +Channels can be 8 bit signed PCM, or 12 bit signed PCM. + +Timer behavior is not yet handled. + +Unmapped registers: + 0x1f8:timer interval? (Nx0.1 ms) + 0x1fa:irq ack? timer restart? + 0x1fe:timer switch?(0:off 1:on) + +-------------- + + ASIC "219" notes + + On the 219 ASIC used on NA-1 and NA-2, the high registers have the following + meaning instead: + 0x1f7: bank for voices 0-3 + 0x1f1: bank for voices 4-7 + 0x1f3: bank for voices 8-11 + 0x1f5: bank for voices 12-15 + + Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers + mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15. + + Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2. + Additionally, the 219's base pitch is the same as the C352's (42667). But these changes + are IMO not sufficient to make this a separate file - all the other registers are + fully compatible. + + Finally, the 219 only has 16 voices. +*/ +/* + 2000.06.26 CAB fixed compressed pcm playback + 2002.07.20 R.Belmont added support for multiple banking types + 2006.01.08 R.Belmont added support for NA-1/2 "219" derivative +*/ + + +//#include "emu.h" +#include +#include +#include "mamedef.h" +#include "c140.h" + +#define NULL ((void *)0) + +#define MAX_VOICE 24 + +struct voice_registers +{ + UINT8 volume_right; + UINT8 volume_left; + UINT8 frequency_msb; + UINT8 frequency_lsb; + UINT8 bank; + UINT8 mode; + UINT8 start_msb; + UINT8 start_lsb; + UINT8 end_msb; + UINT8 end_lsb; + UINT8 loop_msb; + UINT8 loop_lsb; + UINT8 reserved[4]; +}; + +typedef struct +{ + long ptoffset; + long pos; + long key; + //--work + long lastdt; + long prevdt; + long dltdt; + //--reg + long rvol; + long lvol; + long frequency; + long bank; + long mode; + + long sample_start; + long sample_end; + long sample_loop; + UINT8 Muted; +} VOICE; + +typedef struct _c140_state c140_state; +struct _c140_state +{ + int sample_rate; + //sound_stream *stream; + int banking_type; + /* internal buffers */ + INT16 *mixer_buffer_left; + INT16 *mixer_buffer_right; + + int baserate; + UINT32 pRomSize; + void *pRom; + UINT8 REG[0x200]; + + INT16 pcmtbl[8]; //2000.06.26 CAB + + VOICE voi[MAX_VOICE]; +}; + +/*INLINE c140_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == C140); + return (c140_state *)downcast(device)->token(); +}*/ + + +static void init_voice( VOICE *v ) +{ + v->key=0; + v->ptoffset=0; + v->rvol=0; + v->lvol=0; + v->frequency=0; + v->bank=0; + v->mode=0; + v->sample_start=0; + v->sample_end=0; + v->sample_loop=0; +} +//READ8_DEVICE_HANDLER( c140_r ) +UINT8 c140_r(void *_info, offs_t offset) +{ + //c140_state *info = get_safe_token(device); + c140_state *info = (c140_state *)_info; + offset&=0x1ff; + return info->REG[offset]; +} + +/* + find_sample: compute the actual address of a sample given it's + address and banking registers, as well as the board type. + + I suspect in "real life" this works like the Sega MultiPCM where the banking + is done by a small PAL or GAL external to the sound chip, which can be switched + per-game or at least per-PCB revision as addressing range needs grow. + */ +static long find_sample(c140_state *info, long adrs, long bank, int voice) +{ + long newadr = 0; + + static const INT16 asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 }; + + adrs=(bank<<16)+adrs; + + switch (info->banking_type) + { + case C140_TYPE_SYSTEM2: + // System 2 banking + newadr = ((adrs&0x200000)>>2)|(adrs&0x7ffff); + break; + + case C140_TYPE_SYSTEM21: + // System 21 banking. + // similar to System 2's. + newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff); + break; + + /*case C140_TYPE_SYSTEM21_A: + // System 21 type A (simple) banking. + // similar to System 2's. + newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff); + break; + + case C140_TYPE_SYSTEM21_B: + // System 21 type B (chip select) banking + + // get base address of sample inside the bank + newadr = ((adrs&0x100000)>>2) + (adrs&0x3ffff); + + // now add the starting bank offsets based on the 2 + // chip select bits. + // 0x40000 picks individual 512k ROMs + if (adrs & 0x40000) + { + newadr += 0x80000; + } + + // and 0x200000 which group of chips... + if (adrs & 0x200000) + { + newadr += 0x100000; + } + break;*/ + + case C140_TYPE_ASIC219: + // ASIC219's banking is fairly simple + newadr = ((info->REG[asic219banks[voice/4]]&0x3) * 0x20000) + adrs; + break; + } + + return (newadr); +} +//WRITE8_DEVICE_HANDLER( c140_w ) +void c140_w(void *_info, offs_t offset, UINT8 data) +{ + //c140_state *info = get_safe_token(device); + c140_state *info = (c140_state *)_info; + //info->stream->update(); + + offset&=0x1ff; + + // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE) + if ((offset >= 0x1f8) && (info->banking_type == C140_TYPE_ASIC219)) + { + offset -= 8; + } + + info->REG[offset]=data; + if( offset<0x180 ) + { + VOICE *v = &info->voi[offset>>4]; + + if( (offset&0xf)==0x5 ) + { + if( data&0x80 ) + { + const struct voice_registers *vreg = (struct voice_registers *) &info->REG[offset&0x1f0]; + v->key=1; + v->ptoffset=0; + v->pos=0; + v->lastdt=0; + v->prevdt=0; + v->dltdt=0; + v->bank = vreg->bank; + v->mode = data; + + // on the 219 asic, addresses are in words + if (info->banking_type == C140_TYPE_ASIC219) + { + v->sample_loop = (vreg->loop_msb*256 + vreg->loop_lsb)*2; + v->sample_start = (vreg->start_msb*256 + vreg->start_lsb)*2; + v->sample_end = (vreg->end_msb*256 + vreg->end_lsb)*2; + + #if 0 + logerror("219: play v %d mode %02x start %x loop %x end %x\n", + offset>>4, v->mode, + find_sample(info, v->sample_start, v->bank, offset>>4), + find_sample(info, v->sample_loop, v->bank, offset>>4), + find_sample(info, v->sample_end, v->bank, offset>>4)); + #endif + } + else + { + v->sample_loop = vreg->loop_msb*256 + vreg->loop_lsb; + v->sample_start = vreg->start_msb*256 + vreg->start_lsb; + v->sample_end = vreg->end_msb*256 + vreg->end_lsb; + } + } + else + { + v->key=0; + } + } + } +} + +//void c140_set_base(device_t *device, void *base) +void c140_set_base(void *_info, void *base) +{ + //c140_state *info = get_safe_token(device); + c140_state *info = (c140_state *)_info; + info->pRom = base; +} + +/*INLINE int limit(INT32 in) +{ + if(in>0x7fff) return 0x7fff; + else if(in<-0x8000) return -0x8000; + return in; +}*/ + +//static STREAM_UPDATE( update_stereo ) +void c140_update(void *param, stream_sample_t **outputs, int samples) +{ + c140_state *info = (c140_state *)param; + int i,j; + + INT32 rvol,lvol; + INT32 dt; + INT32 sdt; + INT32 st,ed,sz; + + INT8 *pSampleData; + INT32 frequency,delta,offset,pos; + INT32 cnt, voicecnt; + INT32 lastdt,prevdt,dltdt; + float pbase=(float)info->baserate*2.0f / (float)info->sample_rate; + + INT16 *lmix, *rmix; + + if(samples>info->sample_rate) samples=info->sample_rate; + + /* zap the contents of the mixer buffer */ + memset(info->mixer_buffer_left, 0, samples * sizeof(INT16)); + memset(info->mixer_buffer_right, 0, samples * sizeof(INT16)); + if (info->pRom == NULL) + return; + + /* get the number of voices to update */ + voicecnt = (info->banking_type == C140_TYPE_ASIC219) ? 16 : 24; + + //--- audio update + for( i=0;ivoi[i]; + const struct voice_registers *vreg = (struct voice_registers *)&info->REG[i*16]; + + if( v->key && ! v->Muted) + { + frequency= vreg->frequency_msb*256 + vreg->frequency_lsb; + + /* Abort voice if no frequency value set */ + if(frequency==0) continue; + + /* Delta = frequency * ((8MHz/374)*2 / sample rate) */ + delta=(long)((float)frequency * pbase); + + /* Calculate left/right channel volumes */ + lvol=(vreg->volume_left*32)/MAX_VOICE; //32ch -> 24ch + rvol=(vreg->volume_right*32)/MAX_VOICE; + + /* Set mixer outputs base pointers */ + lmix = info->mixer_buffer_left; + rmix = info->mixer_buffer_right; + + /* Retrieve sample start/end and calculate size */ + st=v->sample_start; + ed=v->sample_end; + sz=ed-st; + + /* Retrieve base pointer to the sample data */ + //pSampleData=(signed char*)((FPTR)info->pRom + find_sample(info, st, v->bank, i)); + pSampleData = (INT8*)info->pRom + find_sample(info, st, v->bank, i); + + /* Fetch back previous data pointers */ + offset=v->ptoffset; + pos=v->pos; + lastdt=v->lastdt; + prevdt=v->prevdt; + dltdt=v->dltdt; + + /* Switch on data type - compressed PCM is only for C140 */ + if ((v->mode&8) && (info->banking_type != C140_TYPE_ASIC219)) + { + //compressed PCM (maybe correct...) + /* Loop for enough to fill sample buffer as requested */ + for(j=0;j>16)&0x7fff; + offset &= 0xffff; + pos+=cnt; + //for(;cnt>0;cnt--) + { + /* Check for the end of the sample */ + if(pos >= sz) + { + /* Check if its a looping sample, either stop or loop */ + if(v->mode&0x10) + { + pos = (v->sample_loop - st); + } + else + { + v->key=0; + break; + } + } + + /* Read the chosen sample byte */ + dt=pSampleData[pos]; + + /* decompress to 13bit range */ //2000.06.26 CAB + sdt=dt>>3; //signed + if(sdt<0) sdt = (sdt<<(dt&7)) - info->pcmtbl[dt&7]; + else sdt = (sdt<<(dt&7)) + info->pcmtbl[dt&7]; + + prevdt=lastdt; + lastdt=sdt; + dltdt=(lastdt - prevdt); + } + + /* Caclulate the sample value */ + dt=((dltdt*offset)>>16)+prevdt; + + /* Write the data to the sample buffers */ + *lmix++ +=(dt*lvol)>>(5+5); + *rmix++ +=(dt*rvol)>>(5+5); + } + } + else + { + /* linear 8bit signed PCM */ + for(j=0;j>16)&0x7fff; + offset &= 0xffff; + pos += cnt; + /* Check for the end of the sample */ + if(pos >= sz) + { + /* Check if its a looping sample, either stop or loop */ + if( v->mode&0x10 ) + { + pos = (v->sample_loop - st); + } + else + { + v->key=0; + break; + } + } + + if( cnt ) + { + prevdt=lastdt; + + if (info->banking_type == C140_TYPE_ASIC219) + { + //lastdt = pSampleData[BYTE_XOR_BE(pos)]; + lastdt = pSampleData[pos ^ 0x01]; + + // Sign + magnitude format + if ((v->mode & 0x01) && (lastdt & 0x80)) + lastdt = -(lastdt & 0x7f); + + // Sign flip + if (v->mode & 0x40) + lastdt = -lastdt; + } + else + { + lastdt=pSampleData[pos]; + } + + dltdt = (lastdt - prevdt); + } + + /* Caclulate the sample value */ + dt=((dltdt*offset)>>16)+prevdt; + + /* Write the data to the sample buffers */ + *lmix++ +=(dt*lvol)>>5; + *rmix++ +=(dt*rvol)>>5; + } + } + + /* Save positional data for next callback */ + v->ptoffset=offset; + v->pos=pos; + v->lastdt=lastdt; + v->prevdt=prevdt; + v->dltdt=dltdt; + } + } + + /* render to MAME's stream buffer */ + lmix = info->mixer_buffer_left; + rmix = info->mixer_buffer_right; + { + stream_sample_t *dest1 = outputs[0]; + stream_sample_t *dest2 = outputs[1]; + for (i = 0; i < samples; i++) + { + //*dest1++ = limit(8*(*lmix++)); + //*dest2++ = limit(8*(*rmix++)); + *dest1++ = 8 * (*lmix ++); + *dest2++ = 8 * (*rmix ++); + } + } +} + +//static DEVICE_START( c140 ) +int device_start_c140(void **_info, int clock, int banking_type, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + //const c140_interface *intf = (const c140_interface *)device->static_config(); + //c140_state *info = get_safe_token(device); + c140_state *info; + int i; + + info = (c140_state *) calloc(1, sizeof(c140_state)); + *_info = (void *) info; + + //info->sample_rate=info->baserate=device->clock(); + if (clock < 1000000) + info->baserate = clock; + else + info->baserate = clock / 384; // based on MAME's notes on Namco System II + info->sample_rate = info->baserate; + if (((CHIP_SAMPLING_MODE & 0x01) && info->sample_rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + info->sample_rate = CHIP_SAMPLE_RATE; + if (info->sample_rate >= 0x1000000) // limit to 16 MHz sample rate (32 MB buffer) + return 0; + + //info->banking_type = intf->banking_type; + info->banking_type = banking_type; + + //info->stream = device->machine().sound().stream_alloc(*device,0,2,info->sample_rate,info,update_stereo); + + //info->pRom=*device->region(); + info->pRomSize = 0x00; + info->pRom = NULL; + + /* make decompress pcm table */ //2000.06.26 CAB + { + INT32 segbase=0; + for(i=0;i<8;i++) + { + info->pcmtbl[i]=segbase; //segment base value + segbase += 16<REG,0,sizeof(info->REG)); + { + int i; + for(i=0;ivoi[i] ); + }*/ + + /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */ + //info->mixer_buffer_left = auto_alloc_array(device->machine(), INT16, 2 * info->sample_rate); + info->mixer_buffer_left = (INT16*)malloc(sizeof(INT16) * 2 * info->sample_rate); + info->mixer_buffer_right = info->mixer_buffer_left + info->sample_rate; + + for (i = 0; i < MAX_VOICE; i ++) + info->voi[i].Muted = 0x00; + + return info->sample_rate; +} + +void device_stop_c140(void *_info) +{ + c140_state *info = (c140_state *)_info; + + free(info->pRom); info->pRom = NULL; + free(info->mixer_buffer_left); + + free(info); + + return; +} + +void device_reset_c140(void *_info) +{ + c140_state *info = (c140_state *)_info; + int i; + + memset(info->REG, 0, sizeof(info->REG)); + + for(i = 0; i < MAX_VOICE; i ++) + init_voice( &info->voi[i] ); + + return; +} + +void c140_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + c140_state *info = (c140_state *)_info; + + if (info->pRomSize != ROMSize) + { + info->pRom = (UINT8*)realloc(info->pRom, ROMSize); + info->pRomSize = ROMSize; + memset(info->pRom, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy((INT8*)info->pRom + DataStart, ROMData, DataLength); + + return; +} + + +void c140_set_mute_mask(void *_info, UINT32 MuteMask) +{ + c140_state *info = (c140_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < MAX_VOICE; CurChn ++) + info->voi[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( c140 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c140_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c140 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: // nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "C140"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Namco PCM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(C140, c140);*/ diff --git a/Frameworks/GME/vgmplay/chips/c140.h b/Frameworks/GME/vgmplay/chips/c140.h new file mode 100644 index 000000000..30e58f89f --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c140.h @@ -0,0 +1,36 @@ +/* C140.h */ + +#pragma once + +void c140_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_c140(void **chip, int clock, int banking_type, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_c140(void *chip); +void device_reset_c140(void *chip); + +//READ8_DEVICE_HANDLER( c140_r ); +//WRITE8_DEVICE_HANDLER( c140_w ); +UINT8 c140_r(void *chip, offs_t offset); +void c140_w(void *chip, offs_t offset, UINT8 data); + +//void c140_set_base(device_t *device, void *base); +void c140_set_base(void *chip, void *base); + +enum +{ + C140_TYPE_SYSTEM2, + C140_TYPE_SYSTEM21, + C140_TYPE_ASIC219 +}; + +/*typedef struct _c140_interface c140_interface; +struct _c140_interface { + int banking_type; +};*/ + + +void c140_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void c140_set_mute_mask(void *chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(C140, c140); diff --git a/Frameworks/GME/vgmplay/chips/c352.c b/Frameworks/GME/vgmplay/chips/c352.c new file mode 100644 index 000000000..f22ae20fb --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c352.c @@ -0,0 +1,733 @@ +/* + c352.c - Namco C352 custom PCM chip emulation + v1.2 + By R. Belmont + Additional code by cync and the hoot development team + + Thanks to Cap of VivaNonno for info and The_Author for preliminary reverse-engineering + + Chip specs: + 32 voices + Supports 8-bit linear and 8-bit muLaw samples + Output: digital, 16 bit, 4 channels + Output sample rate is the input clock / (288 * 2). + */ + +//#include "emu.h" +//#include "streams.h" +#include +#include +#include +#include // for NULL +#include "mamedef.h" +#include "c352.h" + +#define VERBOSE (0) +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +// flags + +enum { + C352_FLG_BUSY = 0x8000, // channel is busy + C352_FLG_KEYON = 0x4000, // Keyon + C352_FLG_KEYOFF = 0x2000, // Keyoff + C352_FLG_LOOPTRG = 0x1000, // Loop Trigger + C352_FLG_LOOPHIST = 0x0800, // Loop History + C352_FLG_FM = 0x0400, // Frequency Modulation + C352_FLG_PHASERL = 0x0200, // Rear Left invert phase 180 degrees + C352_FLG_PHASEFL = 0x0100, // Front Left invert phase 180 degrees + C352_FLG_PHASEFR = 0x0080, // invert phase 180 degrees (e.g. flip sign of sample) + C352_FLG_LDIR = 0x0040, // loop direction + C352_FLG_LINK = 0x0020, // "long-format" sample (can't loop, not sure what else it means) + C352_FLG_NOISE = 0x0010, // play noise instead of sample + C352_FLG_MULAW = 0x0008, // sample is mulaw instead of linear 8-bit PCM + C352_FLG_FILTER = 0x0004, // don't apply filter + C352_FLG_REVLOOP = 0x0003, // loop backwards + C352_FLG_LOOP = 0x0002, // loop forward + C352_FLG_REVERSE = 0x0001, // play sample backwards +}; + +typedef struct +{ + UINT8 vol_l; + UINT8 vol_r; + UINT8 vol_l2; + UINT8 vol_r2; + UINT8 bank; + UINT8 Muted; + + INT16 noise; + INT16 noisebuf; + UINT16 noisecnt; + UINT16 pitch; + UINT16 start_addr; + UINT16 end_addr; + UINT16 repeat_addr; + UINT32 flag; + + UINT16 start; + UINT16 repeat; + UINT32 current_addr; + UINT32 pos; +} c352_ch_t; + +typedef struct _c352_state c352_state; +struct _c352_state +{ + //sound_stream *stream; + c352_ch_t c352_ch[32]; + unsigned char *c352_rom_samples; + UINT32 c352_rom_length; + int sample_rate_base; + + /*long channel_l[2048*2]; + long channel_r[2048*2]; + long channel_l2[2048*2]; + long channel_r2[2048*2];*/ + + short mulaw_table[256]; + unsigned int mseq_reg; +}; + +/*INLINE c352_state *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == C352); + return (c352_state *)downcast(device)->token(); +}*/ + +// noise generator +static int get_mseq_bit(c352_state *info) +{ + unsigned int mask = (1 << (7 - 1)); + unsigned int reg = info->mseq_reg; + unsigned int bit = reg & (1 << (17 - 1)); + + if (bit) + { + reg = ((reg ^ mask) << 1) | 1; + } + else + { + reg = reg << 1; + } + + info->mseq_reg = reg; + + return (reg & 1); +} + +/* ctr: this function gets the next sample for the lerp. If the sample position pointer is adjacent to the sample end pointer, + then lerping the sample with the "nextsample" variable causes clicks. To prevent this, simply check if the next sample is + the final sample and if so go to the beginning sample. + + If bidi samples causes problems, add a case for that as well. (they might) + */ +char getnextsample(c352_state *chip, c352_ch_t* c352ch, UINT32 pos) +{ + INT32 flag = c352ch->flag; + UINT32 bank = c352ch->bank << 16; + + if( flag & C352_FLG_REVERSE) + return (char) chip->c352_rom_samples[pos+1]; // todo: Bidi samples + + pos++; + if ( + (((pos&0xFFFF) > c352ch->end_addr) && ((pos&0xFFFF) < c352ch->start) && (c352ch->start > c352ch->end_addr) ) || + (((pos&0xFFFF) > c352ch->end_addr) && ((pos&0xFFFF) > c352ch->start) && (c352ch->start < c352ch->end_addr) ) || + ((pos > (bank|0xFFFF)) && (c352ch->end_addr == 0xFFFF)) + ) + { + if ( (flag & C352_FLG_LINK) && (flag & C352_FLG_LOOP) ) + pos = ((c352ch->start_addr & 0xFF)<<16) + c352ch->repeat_addr; + else if (flag & C352_FLG_LOOP) + pos = (pos & 0xFF0000) + c352ch->repeat; + else + { + // key off at this point, just return the previous value + return (char) chip->c352_rom_samples[pos-1]; + } + } + return (char) chip->c352_rom_samples[pos]; +} + +static void c352_mix_one_channel(c352_state *info, unsigned long ch, stream_sample_t **outputs, long sample_count) +{ + c352_ch_t* c352ch; + int i; + + signed short sample, nextsample; + signed short noisebuf; + UINT16 noisecnt; + INT32 delta, offset, cnt, flag; + UINT32 bank; + UINT32 pos; + + c352ch = &info->c352_ch[ch]; + delta = c352ch->pitch; + + pos = c352ch->current_addr; // sample pointer + offset = c352ch->pos; // 16.16 fixed-point offset into the sample + flag = c352ch->flag; + bank = c352ch->bank << 16; + + noisecnt = c352ch->noisecnt; + noisebuf = c352ch->noisebuf; + + for(i = 0 ; (i < sample_count) && (flag & C352_FLG_BUSY) ; i++) + { + offset += delta; + cnt = (offset>>16)&0x7fff; + if (cnt) // if there is a whole sample part, chop it off now that it's been applied + { + offset &= 0xffff; + } + + if (pos >= info->c352_rom_length) // pretty sure this should be >= instead of > -Valley Bell + { + c352ch->flag &= ~C352_FLG_BUSY; + //return; + break; // ensure that it saves the variables + } + + sample = (char)info->c352_rom_samples[pos]; + //nextsample = (char)info->c352_rom_samples[pos+cnt]; + nextsample = getnextsample(info, c352ch, pos); + + // sample is muLaw, not 8-bit linear (Fighting Layer uses this extensively) + if (flag & C352_FLG_MULAW) + { + sample = info->mulaw_table[(unsigned char)sample]; + nextsample = info->mulaw_table[(unsigned char)nextsample]; + } + else + { + sample <<= 8; + nextsample <<= 8; + } + + // play noise instead of sample data + if (flag & C352_FLG_NOISE) + { + int noise_level = 0x8000; + sample = c352ch->noise = (c352ch->noise << 1) | get_mseq_bit(info); + sample = (sample & (noise_level - 1)) - (noise_level >> 1); + if (sample > 0x7f) + { + sample = 0x7f; + } + else if (sample < 0) + { + sample = 0xff; + } + sample = info->mulaw_table[(unsigned char)sample]; + + if ( (pos+cnt) == pos ) + { + noisebuf += sample; + noisecnt++; + //sample = noisebuf / noisecnt; + if (noisecnt) // avoid Divide By Zero crash -Valley Bell + sample = noisebuf / noisecnt; + else + sample = info->mulaw_table[0x7f]; + } + else + { + if ( noisecnt ) + { + sample = noisebuf / noisecnt; + } + else + { + sample = info->mulaw_table[0x7f]; // Nearest sound(s) is here. + } + noisebuf = 0; + noisecnt = ( flag & C352_FLG_FILTER ) ? 0 : 1; + } + } + + // apply linear interpolation + if ( (flag & (C352_FLG_FILTER | C352_FLG_NOISE)) == 0 ) + { + sample = (short)(sample + ((nextsample-sample) * (((double)(0x0000ffff&offset) )/0x10000))); + } + + if ( flag & C352_FLG_PHASEFL ) + { + //info->channel_l[i] += ((-sample * c352ch->vol_l)>>8); + outputs[0][i] += ((-sample * c352ch->vol_l)>>8); + } + else + { + //info->channel_l[i] += ((sample * c352ch->vol_l)>>8); + outputs[0][i] += ((sample * c352ch->vol_l)>>8); + } + + if ( flag & C352_FLG_PHASEFR ) + { + //info->channel_r[i] += ((-sample * c352ch->vol_r)>>8); + outputs[1][i] += ((-sample * c352ch->vol_r)>>8); + } + else + { + //info->channel_r[i] += ((sample * c352ch->vol_r)>>8); + outputs[1][i] += ((sample * c352ch->vol_r)>>8); + } + + if ( flag & C352_FLG_PHASERL ) + { + //info->channel_l2[i] += ((-sample * c352ch->vol_l2)>>8); + outputs[0][i] += ((-sample * c352ch->vol_l2)>>8); + } + else + { + //info->channel_l2[i] += ((sample * c352ch->vol_l2)>>8); + outputs[0][i] += ((sample * c352ch->vol_l2)>>8); + } + //info->channel_r2[i] += ((sample * c352ch->vol_r2)>>8); + outputs[1][i] += ((sample * c352ch->vol_r2)>>8); + + if ( (flag & C352_FLG_REVERSE) && (flag & C352_FLG_LOOP) ) + { + if ( !(flag & C352_FLG_LDIR) ) + { + pos += cnt; + if ( + (((pos&0xFFFF) > c352ch->end_addr) && ((pos&0xFFFF) < c352ch->start) && (c352ch->start > c352ch->end_addr) ) || + (((pos&0xFFFF) > c352ch->end_addr) && ((pos&0xFFFF) > c352ch->start) && (c352ch->start < c352ch->end_addr) ) || + ((pos > (bank|0xFFFF)) && (c352ch->end_addr == 0xFFFF)) + ) + { + c352ch->flag |= C352_FLG_LDIR; + c352ch->flag |= C352_FLG_LOOPHIST; + } + } + else + { + pos -= cnt; + if ( + (((pos&0xFFFF) < c352ch->repeat) && ((pos&0xFFFF) < c352ch->end_addr) && (c352ch->end_addr > c352ch->start) ) || + (((pos&0xFFFF) < c352ch->repeat) && ((pos&0xFFFF) > c352ch->end_addr) && (c352ch->end_addr < c352ch->start) ) || + ((pos < bank) && (c352ch->repeat == 0x0000)) + ) + { + c352ch->flag &= ~C352_FLG_LDIR; + c352ch->flag |= C352_FLG_LOOPHIST; + } + } + } + else if ( flag & C352_FLG_REVERSE ) + { + pos -= cnt; + if ( + (((pos&0xFFFF) < c352ch->end_addr) && ((pos&0xFFFF) < c352ch->start) && (c352ch->start > c352ch->end_addr) ) || + (((pos&0xFFFF) < c352ch->end_addr) && ((pos&0xFFFF) > c352ch->start) && (c352ch->start < c352ch->end_addr) ) || + ((pos < bank) && (c352ch->end_addr == 0x0000)) + ) + { + if ( (flag & C352_FLG_LINK) && (flag & C352_FLG_LOOP) ) + { + c352ch->bank = c352ch->start_addr & 0xFF; + c352ch->start_addr = c352ch->repeat_addr; + c352ch->start = c352ch->start_addr; + c352ch->repeat = c352ch->repeat_addr; + pos = (c352ch->bank<<16) + c352ch->start_addr; + c352ch->flag |= C352_FLG_LOOPHIST; + } + else if (flag & C352_FLG_LOOP) + { + pos = (pos & 0xFF0000) + c352ch->repeat; + c352ch->flag |= C352_FLG_LOOPHIST; + } + else + { + c352ch->flag |= C352_FLG_KEYOFF; + c352ch->flag &= ~C352_FLG_BUSY; + //return; + break; + } + } + } else { + pos += cnt; + if ( + (((pos&0xFFFF) > c352ch->end_addr) && ((pos&0xFFFF) < c352ch->start) && (c352ch->start > c352ch->end_addr) ) || + (((pos&0xFFFF) > c352ch->end_addr) && ((pos&0xFFFF) > c352ch->start) && (c352ch->start < c352ch->end_addr) ) || + ((pos > (bank|0xFFFF)) && (c352ch->end_addr == 0xFFFF)) + ) + { + if ( (flag & C352_FLG_LINK) && (flag & C352_FLG_LOOP) ) + { + c352ch->bank = c352ch->start_addr & 0xFF; + c352ch->start_addr = c352ch->repeat_addr; + c352ch->start = c352ch->start_addr; + c352ch->repeat = c352ch->repeat_addr; + pos = (c352ch->bank<<16) + c352ch->start_addr; + c352ch->flag |= C352_FLG_LOOPHIST; + } + else if (flag & C352_FLG_LOOP) + { + pos = (pos & 0xFF0000) + c352ch->repeat; + c352ch->flag |= C352_FLG_LOOPHIST; + } + else + { + c352ch->flag |= C352_FLG_KEYOFF; + c352ch->flag &= ~C352_FLG_BUSY; + //return; + break; + } + } + } + } + + c352ch->noisecnt = noisecnt; + c352ch->noisebuf = noisebuf; + c352ch->pos = offset; + c352ch->current_addr = pos; +} + + +//static STREAM_UPDATE( c352_update ) +void c352_update(void *param, stream_sample_t **outputs, int samples) +{ + c352_state *info = (c352_state *)param; + int j; + /*stream_sample_t *bufferl = outputs[0]; + stream_sample_t *bufferr = outputs[1]; + stream_sample_t *bufferl2 = outputs[2]; + stream_sample_t *bufferr2 = outputs[3]; + + for(i = 0 ; i < samples ; i++) + { + info->channel_l[i] = info->channel_r[i] = info->channel_l2[i] = info->channel_r2[i] = 0; + }*/ + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + + for (j = 0 ; j < 32 ; j++) + { + //c352_mix_one_channel(info, j, samples); + if ((info->c352_ch[j].flag & C352_FLG_BUSY) && ! info->c352_ch[j].Muted) + c352_mix_one_channel(info, j, outputs, samples); + } + + /*for(i = 0 ; i < samples ; i++) + { + *bufferl++ = (short) (info->channel_l[i] >>3); + *bufferr++ = (short) (info->channel_r[i] >>3); + *bufferl2++ = (short) (info->channel_l2[i] >>3); + *bufferr2++ = (short) (info->channel_r2[i] >>3); + }*/ +} + +static unsigned short c352_read_reg16(c352_state *info, unsigned long address) +{ + unsigned long chan; + unsigned short val; + + //stream_update(info->stream); + + chan = (address >> 4) & 0xfff; + if (chan > 31) + { + val = 0; + } + else + { + if ((address & 0xf) == 6) + { + val = info->c352_ch[chan].flag; + } + else + { + val = 0; + } + } + return val; +} + +static void c352_write_reg16(c352_state *info, unsigned long address, unsigned short val) +{ + unsigned long chan; + int i; + + //stream_update(info->stream); + + chan = (address >> 4) & 0xfff; + + if ( address >= 0x400 ) + { + switch(address) + { + case 0x404: // execute key-ons/offs + for ( i = 0 ; i <= 31 ; i++ ) + { + if ( info->c352_ch[i].flag & C352_FLG_KEYON ) + { + if (info->c352_ch[i].start_addr != info->c352_ch[i].end_addr) + { + info->c352_ch[i].current_addr = (info->c352_ch[i].bank << 16) + info->c352_ch[i].start_addr; + info->c352_ch[i].start = info->c352_ch[i].start_addr; + info->c352_ch[i].repeat = info->c352_ch[i].repeat_addr; + info->c352_ch[i].noisebuf = 0; + info->c352_ch[i].noisecnt = 0; + info->c352_ch[i].flag &= ~(C352_FLG_KEYON | C352_FLG_LOOPHIST); + info->c352_ch[i].flag |= C352_FLG_BUSY; + } + } + else if ( info->c352_ch[i].flag & C352_FLG_KEYOFF ) + { + info->c352_ch[i].flag &= ~C352_FLG_BUSY; + info->c352_ch[i].flag &= ~(C352_FLG_KEYOFF); + } + } + break; + default: + break; + } + return; + } + + if (chan > 31) + { + LOG(("C352 CTRL %08lx %04x\n", address, val)); + return; + } + switch(address & 0xf) + { + case 0x0: + // volumes (output 1) + LOG(("CH %02ld LVOL %02x RVOL %02x\n", chan, val & 0xff, val >> 8)); + info->c352_ch[chan].vol_l = val & 0xff; + info->c352_ch[chan].vol_r = val >> 8; + break; + + case 0x2: + // volumes (output 2) + LOG(("CH %02ld RLVOL %02x RRVOL %02x\n", chan, val & 0xff, val >> 8)); + info->c352_ch[chan].vol_l2 = val & 0xff; + info->c352_ch[chan].vol_r2 = val >> 8; + break; + + case 0x4: + // pitch + LOG(("CH %02ld PITCH %04x\n", chan, val)); + info->c352_ch[chan].pitch = val; + break; + + case 0x6: + // flags + LOG(("CH %02ld FLAG %02x\n", chan, val)); + info->c352_ch[chan].flag = val; + break; + + case 0x8: + // bank (bits 16-31 of address); + info->c352_ch[chan].bank = val & 0xff; + LOG(("CH %02ld BANK %02x", chan, info->c352_ch[chan].bank)); + break; + + case 0xa: + // start address + LOG(("CH %02ld SADDR %04x\n", chan, val)); + info->c352_ch[chan].start_addr = val; + break; + + case 0xc: + // end address + LOG(("CH %02ld EADDR %04x\n", chan, val)); + info->c352_ch[chan].end_addr = val; + break; + + case 0xe: + // loop address + LOG(("CH %02ld LADDR %04x\n", chan, val)); + info->c352_ch[chan].repeat_addr = val; + break; + + default: + LOG(("CH %02ld UNKN %01lx %04x", chan, address & 0xf, val)); + break; + } +} + +//static DEVICE_START( c352 ) +int device_start_c352(void **_info, int clock, int clkdiv) +{ + //c352_state *info = get_safe_token(device); + c352_state *info; + int i; + double x_max = 32752.0; + double y_max = 127.0; + double u = 10.0; + + info = (c352_state *) calloc(1, sizeof(c352_state)); + *_info = (void *) info; + + //info->c352_rom_samples = *device->region(); + //info->c352_rom_length = device->region()->bytes(); + info->c352_rom_samples = NULL; + info->c352_rom_length = 0x00; + + if (! clkdiv) + clkdiv = 288; + //info->sample_rate_base = device->clock() / 288; + info->sample_rate_base = clock / clkdiv; + + //info->stream = stream_create(device, 0, 4, info->sample_rate_base, info, c352_update); + + // generate mulaw table for mulaw format samples + for (i = 0; i < 256; i++) + { + double y = (double) (i & 0x7f); + double x = (exp (y / y_max * log (1.0 + u)) - 1.0) * x_max / u; + + if (i & 0x80) + { + x = -x; + } + info->mulaw_table[i] = (short)x; + } + + // register save state info + for (i = 0; i < 32; i++) + { + /*state_save_register_device_item(device, i, info->c352_ch[i].vol_l); + state_save_register_device_item(device, i, info->c352_ch[i].vol_r); + state_save_register_device_item(device, i, info->c352_ch[i].vol_l2); + state_save_register_device_item(device, i, info->c352_ch[i].vol_r2); + state_save_register_device_item(device, i, info->c352_ch[i].bank); + state_save_register_device_item(device, i, info->c352_ch[i].noise); + state_save_register_device_item(device, i, info->c352_ch[i].noisebuf); + state_save_register_device_item(device, i, info->c352_ch[i].noisecnt); + state_save_register_device_item(device, i, info->c352_ch[i].pitch); + state_save_register_device_item(device, i, info->c352_ch[i].start_addr); + state_save_register_device_item(device, i, info->c352_ch[i].end_addr); + state_save_register_device_item(device, i, info->c352_ch[i].repeat_addr); + state_save_register_device_item(device, i, info->c352_ch[i].flag); + state_save_register_device_item(device, i, info->c352_ch[i].start); + state_save_register_device_item(device, i, info->c352_ch[i].repeat); + state_save_register_device_item(device, i, info->c352_ch[i].current_addr); + state_save_register_device_item(device, i, info->c352_ch[i].pos);*/ + info->c352_ch[i].Muted = 0x00; + } + + return info->sample_rate_base; +} + +void device_stop_c352(void *_info) +{ + c352_state *info = (c352_state *)_info; + + free(info->c352_rom_samples); + info->c352_rom_samples = NULL; + + free(info); + + return; +} + +void device_reset_c352(void *_info) +{ + c352_state *info = (c352_state *)_info; + + // clear all channels states + memset(info->c352_ch, 0, sizeof(c352_ch_t)*32); + + // init noise generator + info->mseq_reg = 0x12345678; + + return; +} + + +//READ16_DEVICE_HANDLER( c352_r ) +UINT16 c352_r(void *_info, offs_t offset) +{ + c352_state *info = (c352_state *)_info; + return(c352_read_reg16(info, offset*2)); + //return(c352_read_reg16(get_safe_token(device), offset*2)); +} + +//WRITE16_DEVICE_HANDLER( c352_w ) +void c352_w(void *_info, offs_t offset, UINT16 data) +{ + c352_state *info = (c352_state *)_info; + /*if (mem_mask == 0xffff) + { + c352_write_reg16(get_safe_token(device), offset*2, data); + } + else + { + logerror("C352: byte-wide write unsupported at this time!\n"); + }*/ + c352_write_reg16(info, offset*2, data); +} + + +void c352_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + c352_state *info = (c352_state *)_info; + + if (info->c352_rom_length != ROMSize) + { + info->c352_rom_samples = (UINT8*)realloc(info->c352_rom_samples, ROMSize); + info->c352_rom_length = ROMSize; + memset(info->c352_rom_samples, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(info->c352_rom_samples + DataStart, ROMData, DataLength); + + return; +} + + +void c352_set_mute_mask(void *_info, UINT32 MuteMask) +{ + c352_state *info = (c352_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 32; CurChn ++) + info->c352_ch[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( c352 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c352_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c352 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: // nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "C352"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Namco PCM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.1"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(C352, c352);*/ diff --git a/Frameworks/GME/vgmplay/chips/c352.h b/Frameworks/GME/vgmplay/chips/c352.h new file mode 100644 index 000000000..d6079fdaf --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c352.h @@ -0,0 +1,26 @@ +#pragma once + +#ifndef __C352_H__ +#define __C352_H__ + +//#include "devlegcy.h" + +void c352_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_c352(void **chip, int clock, int clkdiv); +void device_stop_c352(void *chip); +void device_reset_c352(void *chip); + +//READ16_DEVICE_HANDLER( c352_r ); +//WRITE16_DEVICE_HANDLER( c352_w ); +UINT16 c352_r(void *chip, offs_t offset); +void c352_w(void *chip, offs_t offset, UINT16 data); + +void c352_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void c352_set_mute_mask(void *chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(C352, c352); + +#endif /* __C352_H__ */ + diff --git a/Frameworks/GME/vgmplay/chips/c6280.c b/Frameworks/GME/vgmplay/chips/c6280.c new file mode 100644 index 000000000..53ca3e15f --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c6280.c @@ -0,0 +1,452 @@ +/* + HuC6280 sound chip emulator + by Charles MacDonald + E-mail: cgfm2@hotmail.com + WWW: http://cgfm2.emuviews.com + + Thanks to: + + - Paul Clifford for his PSG documentation. + - Richard Bannister for the TGEmu-specific sound updating code. + - http://www.uspto.gov for the PSG patents. + - All contributors to the tghack-list. + + Changes: + + (03/30/2003) + - Removed TGEmu specific code and added support functions for MAME. + - Modified setup code to handle multiple chips with different clock and + volume settings. + + Missing features / things to do: + + - Add LFO support. But do any games actually use it? + + - Add shared index for waveform playback and sample writes. Almost every + game will reset the index prior to playback so this isn't an issue. + + - While the noise emulation is complete, the data for the pseudo-random + bitstream is calculated by machine.rand() and is not a representation of what + the actual hardware does. + + For some background on Hudson Soft's C62 chipset: + + - http://www.hudsonsoft.net/ww/about/about.html + - http://www.hudson.co.jp/corp/eng/coinfo/history.html + + Legal information: + + Copyright Charles MacDonald + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +//#include "emu.h" +#include // for rand() +#include // for memset() +#include // for pow() +#include "mamedef.h" +#include "c6280.h" + +typedef struct { + UINT16 frequency; + UINT8 control; + UINT8 balance; + UINT8 waveform[32]; + UINT8 index; + INT16 dda; + UINT8 noise_control; + UINT32 noise_counter; + UINT32 counter; + UINT8 Muted; +} t_channel; + +typedef struct { + //sound_stream *stream; + //device_t *device; + //device_t *cpudevice; + UINT8 select; + UINT8 balance; + UINT8 lfo_frequency; + UINT8 lfo_control; + t_channel channel[8]; // is 8, because: p->select = data & 0x07; + INT16 volume_table[32]; + UINT32 noise_freq_tab[32]; + UINT32 wave_freq_tab[4096]; +} c6280_t; + +/*INLINE c6280_t *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == C6280); + return (c6280_t *)downcast(device)->token(); +}*/ + + +/* only needed for io_buffer */ +//#include "cpu/h6280/h6280.h" + + +static void c6280_init(/*device_t *device,*/ c6280_t *p, double clk, double rate) +{ + //const c6280_interface *intf = (const c6280_interface *)device->static_config(); + int i; + double step; + + /* Loudest volume level for table */ + //double level = 65535.0 / 6.0 / 32.0; + double level = 65536.0 / 6.0 / 32.0; + + /* Clear context */ + memset(p, 0, sizeof(c6280_t)); + + //p->device = device; + //p->cpudevice = device->machine().device(intf->cpu); + //if (p->cpudevice == NULL) + // fatalerror("c6280_init: no CPU found with tag of '%s'\n", device->tag()); + + /* Make waveform frequency table */ + for(i = 0; i < 4096; i += 1) + { + step = ((clk / rate) * 4096) / (i+1); + p->wave_freq_tab[(1 + i) & 0xFFF] = (UINT32)step; + } + + /* Make noise frequency table */ + for(i = 0; i < 32; i += 1) + { + step = ((clk / rate) * 32) / (i+1); + p->noise_freq_tab[i] = (UINT32)step; + } + + /* Make volume table */ + /* PSG has 48dB volume range spread over 32 steps */ + step = 48.0 / 32.0; + for(i = 0; i < 31; i++) + { + p->volume_table[i] = (UINT16)level; + level /= pow(10.0, step / 20.0); + } + p->volume_table[31] = 0; +} + + +static void c6280_write(c6280_t *p, int offset, int data) +{ + t_channel *q = &p->channel[p->select]; + + /* Update stream */ + //p->stream->update(); + + switch(offset & 0x0F) + { + case 0x00: /* Channel select */ + p->select = data & 0x07; + break; + + case 0x01: /* Global balance */ + p->balance = data; + break; + + case 0x02: /* Channel frequency (LSB) */ + q->frequency = (q->frequency & 0x0F00) | data; + q->frequency &= 0x0FFF; + break; + + case 0x03: /* Channel frequency (MSB) */ + q->frequency = (q->frequency & 0x00FF) | (data << 8); + q->frequency &= 0x0FFF; + break; + + case 0x04: /* Channel control (key-on, DDA mode, volume) */ + + /* 1-to-0 transition of DDA bit resets waveform index */ + if((q->control & 0x40) && ((data & 0x40) == 0)) + { + q->index = 0; + } + q->control = data; + break; + + case 0x05: /* Channel balance */ + q->balance = data; + break; + + case 0x06: /* Channel waveform data */ + + switch(q->control & 0xC0) + { + case 0x00: + q->waveform[q->index & 0x1F] = data & 0x1F; + q->index = (q->index + 1) & 0x1F; + break; + + case 0x40: + break; + + case 0x80: + q->waveform[q->index & 0x1F] = data & 0x1F; + q->index = (q->index + 1) & 0x1F; + break; + + case 0xC0: + q->dda = data & 0x1F; + break; + } + + break; + + case 0x07: /* Noise control (enable, frequency) */ + q->noise_control = data; + break; + + case 0x08: /* LFO frequency */ + p->lfo_frequency = data; + break; + + case 0x09: /* LFO control (enable, mode) */ + p->lfo_control = data; + break; + + default: + break; + } +} + + +//static STREAM_UPDATE( c6280_update ) +void c6280m_update(void* param, stream_sample_t **outputs, int samples) +{ + static const int scale_tab[] = { + 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, + 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F + }; + int ch; + int i; + c6280_t *p = (c6280_t *)param; + + int lmal = (p->balance >> 4) & 0x0F; + int rmal = (p->balance >> 0) & 0x0F; + int vll, vlr; + + lmal = scale_tab[lmal]; + rmal = scale_tab[rmal]; + + /* Clear buffer */ + for(i = 0; i < samples; i++) + { + outputs[0][i] = 0; + outputs[1][i] = 0; + } + + for(ch = 0; ch < 6; ch++) + { + /* Only look at enabled channels */ + if((p->channel[ch].control & 0x80) && ! p->channel[ch].Muted) + { + int lal = (p->channel[ch].balance >> 4) & 0x0F; + int ral = (p->channel[ch].balance >> 0) & 0x0F; + int al = p->channel[ch].control & 0x1F; + + lal = scale_tab[lal]; + ral = scale_tab[ral]; + + /* Calculate volume just as the patent says */ + vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal); + if(vll > 0x1F) vll = 0x1F; + + vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal); + if(vlr > 0x1F) vlr = 0x1F; + + vll = p->volume_table[vll]; + vlr = p->volume_table[vlr]; + + /* Check channel mode */ + if((ch >= 4) && (p->channel[ch].noise_control & 0x80)) + { + /* Noise mode */ + UINT32 step = p->noise_freq_tab[(p->channel[ch].noise_control & 0x1F) ^ 0x1F]; + for(i = 0; i < samples; i += 1) + { + static int data = 0; + p->channel[ch].noise_counter += step; + if(p->channel[ch].noise_counter >= 0x800) + { + //data = (p->device->machine().rand() & 1) ? 0x1F : 0; + data = (rand() & 1) ? 0x1F : 0; + } + p->channel[ch].noise_counter &= 0x7FF; + outputs[0][i] += (INT16)(vll * (data - 16)); + outputs[1][i] += (INT16)(vlr * (data - 16)); + } + } + else + if(p->channel[ch].control & 0x40) + { + /* DDA mode */ + for(i = 0; i < samples; i++) + { + outputs[0][i] += (INT16)(vll * (p->channel[ch].dda - 16)); + outputs[1][i] += (INT16)(vlr * (p->channel[ch].dda - 16)); + } + } + else + { + /* Waveform mode */ + UINT32 step = p->wave_freq_tab[p->channel[ch].frequency]; + for(i = 0; i < samples; i += 1) + { + int offset; + INT16 data; + offset = (p->channel[ch].counter >> 12) & 0x1F; + p->channel[ch].counter += step; + p->channel[ch].counter &= 0x1FFFF; + data = p->channel[ch].waveform[offset]; + outputs[0][i] += (INT16)(vll * (data - 16)); + outputs[1][i] += (INT16)(vlr * (data - 16)); + } + } + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* MAME specific code */ +/*--------------------------------------------------------------------------*/ + +//static DEVICE_START( c6280 ) +void* device_start_c6280m(int clock, int rate) +{ + //int rate = device->clock()/16; + //c6280_t *info = get_safe_token(device); + c6280_t *info; + UINT8 CurChn; + + info = (c6280_t*)malloc(sizeof(c6280_t)); + if (info == NULL) + return 0; + memset(info, 0x00, sizeof(c6280_t)); + + /* Initialize PSG emulator */ + //c6280_init(device, info, device->clock(), rate); + c6280_init(info, clock & 0x7FFFFFFF, rate); + + /* Create stereo stream */ + //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, rate, info, c6280_update); + + for (CurChn = 0; CurChn < 6; CurChn ++) + info->channel[CurChn].Muted = 0x00; + + return info; +} + +void device_stop_c6280m(void* chip) +{ + c6280_t *info = (c6280_t *)chip; + + free(info); + + return; +} + +void device_reset_c6280m(void* chip) +{ + c6280_t *info = (c6280_t *)chip; + UINT8 CurChn; + t_channel* TempChn; + + info->select = 0x00; + info->balance = 0x00; + info->lfo_frequency = 0x00; + info->lfo_control = 0x00; + + for (CurChn = 0; CurChn < 6; CurChn ++) + { + TempChn = &info->channel[CurChn]; + + TempChn->frequency = 0x00; + TempChn->control = 0x00; + TempChn->balance = 0x00; + memset(TempChn->waveform, 0x00, 0x20); + TempChn->index = 0x00; + TempChn->dda = 0x00; + TempChn->noise_control = 0x00; + TempChn->noise_counter = 0x00; + TempChn->counter = 0x00; + } + + return; +} + +//READ8_DEVICE_HANDLER( c6280_r ) +UINT8 c6280m_r(void* chip, offs_t offset) +{ + //c6280_t *info = get_safe_token(device); + c6280_t *info = (c6280_t *)chip; + //return h6280io_get_buffer(info->cpudevice); + return 0; +} + +//WRITE8_DEVICE_HANDLER( c6280_w ) +void c6280m_w(void* chip, offs_t offset, UINT8 data) +{ + //c6280_t *info = get_safe_token(device); + c6280_t *info = (c6280_t *)chip; + //h6280io_set_buffer(info->cpudevice, data); + c6280_write(info, offset, data); +} + + +void c6280m_set_mute_mask(void* chip, UINT32 MuteMask) +{ + c6280_t *info = (c6280_t *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + info->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( c6280 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c6280_t); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c6280 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: // nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "HuC6280"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "????"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(C6280, c6280);*/ diff --git a/Frameworks/GME/vgmplay/chips/c6280.h b/Frameworks/GME/vgmplay/chips/c6280.h new file mode 100644 index 000000000..03cdcf80c --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c6280.h @@ -0,0 +1,24 @@ +#pragma once + +//#include "devlegcy.h" + +typedef struct _c6280_interface c6280_interface; +struct _c6280_interface +{ + const char * cpu; +}; + +/* Function prototypes */ +//WRITE8_DEVICE_HANDLER( c6280_w ); +//READ8_DEVICE_HANDLER( c6280_r ); +void c6280m_w(void* chip, offs_t offset, UINT8 data); +UINT8 c6280m_r(void* chip, offs_t offset); + +void c6280m_update(void* param, stream_sample_t **outputs, int samples); +void* device_start_c6280m(int clock, int rate); +void device_stop_c6280m(void* chip); +void device_reset_c6280m(void* chip); + +void c6280m_set_mute_mask(void* chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(C6280, c6280); diff --git a/Frameworks/GME/vgmplay/chips/c6280intf.c b/Frameworks/GME/vgmplay/chips/c6280intf.c new file mode 100644 index 000000000..ed0db9dc2 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c6280intf.c @@ -0,0 +1,167 @@ +#include +#include "mamedef.h" +#ifdef ENABLE_ALL_CORES +#include "c6280.h" +#endif +#include "Ootake_PSG.h" + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 +#endif +#define EC_OOTAKE 0x00 + +#define NULL ((void *)0) + +typedef struct _c6280_state +{ + void* chip; + int EMU_CORE; +} c6280_state; + +void c6280_update(void *_info, stream_sample_t **outputs, int samples) +{ + c6280_state* info = (c6280_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + c6280m_update(info->chip, outputs, samples); + break; +#endif + case EC_OOTAKE: + PSG_Mix(info->chip, outputs, samples); + break; + } +} + +int device_start_c6280(void **_info, int EMU_CORE, int clock, int SampleRate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + c6280_state* info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_OOTAKE; +#else + EMU_CORE = EC_OOTAKE; +#endif + + info = (c6280_state *) calloc(1, sizeof(c6280_state)); + *_info = (void *) info; + + info->EMU_CORE = EMU_CORE; + switch(EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + rate = (clock & 0x7FFFFFFF)/16; + if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + + info->chip = device_start_c6280m(clock, rate); + if (info->chip == NULL) + return 0; + break; +#endif + case EC_OOTAKE: + rate = SampleRate; + info->chip = PSG_Init(clock, rate); + if (info->chip == NULL) + return 0; + break; + } + + return rate; +} + +void device_stop_c6280(void *_info) +{ + c6280_state* info = (c6280_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + device_stop_c6280m(info->chip); + break; +#endif + case EC_OOTAKE: + PSG_Deinit(info->chip); + break; + } + info->chip = NULL; + + free(info); + + return; +} + +void device_reset_c6280(void *_info) +{ + c6280_state* info = (c6280_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + device_reset_c6280m(info->chip); + break; +#endif + case EC_OOTAKE: + PSG_ResetVolumeReg(info->chip); + break; + } + return; +} + +UINT8 c6280_r(void *_info, offs_t offset) +{ + c6280_state* info = (c6280_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + return c6280m_r(info->chip, offset); +#endif + case EC_OOTAKE: + return PSG_Read(info->chip, offset); + default: + return 0x00; + } +} + +void c6280_w(void *_info, offs_t offset, UINT8 data) +{ + c6280_state* info = (c6280_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + c6280m_w(info->chip, offset, data); + break; +#endif + case EC_OOTAKE: + PSG_Write(info->chip, offset, data); + break; + } + + return; +} + + +void c6280_set_mute_mask(void *_info, UINT32 MuteMask) +{ + c6280_state* info = (c6280_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + c6280m_set_mute_mask(info->chip, MuteMask); + break; +#endif + case EC_OOTAKE: + PSG_SetMuteMask(info->chip, MuteMask); + break; + } + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/c6280intf.h b/Frameworks/GME/vgmplay/chips/c6280intf.h new file mode 100644 index 000000000..d298611f4 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/c6280intf.h @@ -0,0 +1,11 @@ +#pragma once + +void c6280_w(void *chip, offs_t offset, UINT8 data); +UINT8 c6280_r(void *chip, offs_t offset); + +void c6280_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_c6280(void **chip, int core, int clock, int samplerate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_c6280(void *chip); +void device_reset_c6280(void *chip); + +void c6280_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/gme/dac_control.c b/Frameworks/GME/vgmplay/chips/dac_control.c similarity index 53% rename from Frameworks/GME/gme/dac_control.c rename to Frameworks/GME/vgmplay/chips/dac_control.c index 867e4d858..db574ebeb 100644 --- a/Frameworks/GME/gme/dac_control.c +++ b/Frameworks/GME/vgmplay/chips/dac_control.c @@ -1,367 +1,472 @@ - /************************ - * DAC Stream Control * - ***********************/ -// (Custom Driver to handle PCM Streams of YM2612 DAC and PWM.) -// -// Written on 3 February 2011 by Valley Bell -// Last Update: 25 April 2011 -// -// Only for usage in non-commercial, VGM file related software. - -/* How it basically works: - -1. send command X with data Y at frequency F to chip C -2. do that until you receive a STOP command, or until you sent N commands - -*/ - -#include "dac_control.h" - -#include - -#define INLINE static __inline - -void chip_reg_write(void * context, UINT32 Sample, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data); - -typedef struct _dac_control -{ - UINT32 SampleRate; - - // Commands sent to dest-chip - UINT8 DstChipType; - UINT8 DstChipID; - UINT16 DstCommand; - UINT8 CmdSize; - - UINT32 Frequency; // Frequency (Hz) at which the commands are sent - UINT32 DataLen; // to protect from reading beyond End Of Data - const UINT8* Data; - UINT32 DataStart; // Position where to start - UINT8 StepSize; // usually 1, set to 2 for L/R interleaved data - UINT8 StepBase; // usually 0, set to 0/1 for L/R interleaved data - UINT32 CmdsToSend; - - // Running Bits: 0 (01) - is playing - // 2 (04) - loop sample (simple loop from start to end) - // 4 (10) - already sent this command - // 7 (80) - disabled - UINT8 Running; - UINT32 Step; - UINT32 Pos; - UINT32 RemainCmds; - UINT8 DataStep; // always StepSize * CmdSize - - void * context; // context data sent to chip_reg_write -} dac_control; - -#ifndef NULL -#define NULL (void*)0 -#endif - -static void daccontrol_SendCommand(dac_control *chip, UINT32 Sample) -{ - UINT8 Port; - UINT8 Command; - UINT8 Data; - const UINT8* ChipData; - - if (chip->Running & 0x10) // command already sent - return; - if (chip->DataStart + chip->Pos >= chip->DataLen) - return; - - ChipData = chip->Data + (chip->DataStart + chip->Pos); - switch(chip->DstChipType) - { - // Support for the important chips - case 0x02: // YM2612 - Port = (chip->DstCommand & 0xFF00) >> 8; - Command = (chip->DstCommand & 0x00FF) >> 0; - Data = ChipData[0x00]; - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, Port, Command, Data); - break; - case 0x11: // PWM - Port = (chip->DstCommand & 0x000F) >> 0; - Command = ChipData[0x01] & 0x0F; - Data = ChipData[0x00]; - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, Port, Command, Data); - break; - // (Generic) Support for other chips (just for completeness) - case 0x00: // SN76496 - Command = (chip->DstCommand & 0x00F0) >> 0; - Data = ChipData[0x00] & 0x0F; - if (Command & 0x10) - { - // Volume Change (4-Bit value) - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data); - } - else - { - // Frequency Write (10-Bit value) - Port = ((ChipData[0x01] & 0x03) << 4) | ((ChipData[0x00] & 0xF0) >> 4); - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data); - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Port); - } - break; - case 0x01: // YM2413 - case 0x03: // YM2151 - case 0x09: // YM3812 - case 0x0A: // YM3526 - case 0x0B: // Y8950 - case 0x0F: // YMZ280B - case 0x12: // AY8910 - Command = (chip->DstCommand & 0x00FF) >> 0; - Data = ChipData[0x00]; - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, Command, Data); - break; - case 0x06: // YM2203 - case 0x07: // YM2608 - case 0x08: // YM2610/B - case 0x0C: // YMF262 - case 0x0D: // YMF278B - case 0x0E: // YMF271 - Port = (chip->DstCommand & 0xFF00) >> 8; - Command = (chip->DstCommand & 0x00FF) >> 0; - Data = ChipData[0x00]; - chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, Port, Command, Data); - break; - } - chip->Running |= 0x10; - - return; -} - -INLINE UINT32 muldiv64round(UINT32 Multiplicand, UINT32 Multiplier, UINT32 Divisor) -{ - // Yes, I'm correctly rounding the values. - return (UINT32)(((UINT64)Multiplicand * Multiplier + Multiplier / 2) / Divisor); -} - -void daccontrol_update(void *_chip, UINT32 base_clock, UINT32 samples) -{ - dac_control *chip = (dac_control *) _chip; - UINT32 NewPos; - UINT32 Sample; - - if (chip->Running & 0x80) // disabled - return; - if (! (chip->Running & 0x01)) // stopped - return; - - /*if (samples > 0x20) - { - // very effective Speed Hack for fast seeking - NewPos = chip->Step + (samples - 0x10); - NewPos = muldiv64round(NewPos * chip->DataStep, chip->Frequency, chip->SampleRate); - while(chip->RemainCmds && chip->Pos < NewPos) - { - chip->Pos += chip->DataStep; - chip->RemainCmds --; - } - }*/ - - Sample = 0; - chip->Step += samples; - // Formula: Step * Freq / SampleRate - NewPos = muldiv64round(chip->Step * chip->DataStep, chip->Frequency, chip->SampleRate); - - while(chip->RemainCmds && chip->Pos < NewPos) - { - daccontrol_SendCommand(chip, base_clock + muldiv64round(Sample, chip->SampleRate, chip->Frequency)); - Sample++; - chip->Pos += chip->DataStep; - chip->Running &= ~0x10; - chip->RemainCmds --; - } - - if (! chip->RemainCmds && (chip->Running & 0x04)) - { - // loop back to start - chip->RemainCmds = chip->CmdsToSend; - chip->Step = 0x00; - chip->Pos = 0x00; - } - - if (! chip->RemainCmds) - chip->Running &= ~0x01; // stop - - return; -} - -void * device_start_daccontrol(UINT32 samplerate, void * context) -{ - dac_control *chip; - - chip = (dac_control *) calloc(1, sizeof(dac_control)); - - chip->SampleRate = samplerate; - chip->context = context; - - chip->DstChipType = 0xFF; - chip->DstChipID = 0x00; - chip->DstCommand = 0x0000; - - chip->Running = 0xFF; // disable all actions (except setup_chip) - - return chip; -} - -void device_stop_daccontrol(void *_chip) -{ - dac_control *chip = (dac_control *) _chip; - - free( chip ); -} - -void device_reset_daccontrol(void *_chip) -{ - dac_control *chip = (dac_control *) _chip; - - chip->DstChipType = 0x00; - chip->DstChipID = 0x00; - chip->DstCommand = 0x00; - chip->CmdSize = 0x00; - - chip->Frequency = 0; - chip->DataLen = 0x00; - chip->Data = NULL; - chip->DataStart = 0x00; - chip->StepSize = 0x00; - chip->StepBase = 0x00; - - chip->Running = 0x00; - chip->Step = 0x00; - chip->Pos = 0x00; - chip->RemainCmds = 0x00; - chip->DataStep = 0x00; - - return; -} - -void daccontrol_setup_chip(void *_chip, UINT8 ChType, UINT8 ChNum, UINT16 Command) -{ - dac_control *chip = (dac_control *) _chip; - - chip->DstChipType = ChType; // TypeID (e.g. 0x02 for YM2612) - chip->DstChipID = ChNum; // chip number (to send commands to 1st or 2nd chip) - chip->DstCommand = Command; // Port and Command (would be 0x02A for YM2612) - - switch(chip->DstChipType) - { - case 0x00: // SN76496 - if (chip->DstCommand & 0x0010) - chip->CmdSize = 0x01; // Volume Write - else - chip->CmdSize = 0x02; // Frequency Write - break; - case 0x02: // YM2612 - chip->CmdSize = 0x01; - break; - case 0x11: // PWM - chip->CmdSize = 0x02; - break; - default: - chip->CmdSize = 0x01; - break; - } - chip->DataStep = chip->CmdSize * chip->StepSize; - - return; -} - -void daccontrol_set_data(void *_chip, const UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase) -{ - dac_control *chip = (dac_control *) _chip; - - if (chip->Running & 0x80) - return; - - if (DataLen && Data != NULL) - { - chip->DataLen = DataLen; - chip->Data = Data; - } - else - { - chip->DataLen = 0x00; - chip->Data = NULL; - } - chip->StepSize = StepSize ? StepSize : 1; - chip->StepBase = StepBase; - chip->DataStep = chip->CmdSize * chip->StepSize; - - return; -} - -void daccontrol_set_frequency(void *_chip, UINT32 Frequency) -{ - dac_control *chip = (dac_control *) _chip; - - if (chip->Running & 0x80) - return; - - chip->Frequency = Frequency; - - return; -} - -void daccontrol_start(void *_chip, UINT32 DataPos, UINT8 LenMode, UINT32 Length) -{ - dac_control *chip = (dac_control *) _chip; - UINT16 CmdStepBase; - - if (chip->Running & 0x80) - return; - - CmdStepBase = chip->CmdSize * chip->StepBase; - if (DataPos != 0xFFFFFFFF) // skip setting DataStart, if Pos == -1 - { - chip->DataStart = DataPos + CmdStepBase; - if (chip->DataStart > chip->DataLen) // catch bad value and force silence - chip->DataStart = chip->DataLen; - } - - switch(LenMode & 0x0F) - { - case DCTRL_LMODE_IGNORE: // Length is already set - ignore - break; - case DCTRL_LMODE_CMDS: // Length = number of commands - chip->CmdsToSend = Length; - break; - case DCTRL_LMODE_MSEC: // Length = time in msec - chip->CmdsToSend = 1000 * Length / chip->Frequency; - break; - case DCTRL_LMODE_TOEND: // play unti stop-command is received (or data-end is reached) - chip->CmdsToSend = (chip->DataLen - (chip->DataStart - CmdStepBase)) / chip->DataStep; - break; - case DCTRL_LMODE_BYTES: // raw byte count - chip->CmdsToSend = Length / chip->DataStep; - break; - default: - chip->CmdsToSend = 0x00; - break; - } - chip->RemainCmds = chip->CmdsToSend; - chip->Step = 0x00; - chip->Pos = 0x00; - - chip->Running &= ~0x04; - chip->Running |= (LenMode & 0x80) ? 0x04 : 0x00; // set loop mode - - chip->Running |= 0x01; // start - chip->Running &= ~0x10; // command isn't yet sent - - return; -} - -void daccontrol_stop(void *_chip) -{ - dac_control *chip = (dac_control *) _chip; - - if (chip->Running & 0x80) - return; - - chip->Running &= ~0x01; // stop - - return; -} +// TODO: SCSP and (especially) WonderSwan + /************************ + * DAC Stream Control * + ***********************/ +// (Custom Driver to handle PCM Streams of YM2612 DAC and PWM.) +// +// Written on 3 February 2011 by Valley Bell +// Last Update: 13 April 2014 +// +// Only for usage in non-commercial, VGM file related software. + +/* How it basically works: + +1. send command X with data Y at frequency F to chip C +2. do that until you receive a STOP command, or until you sent N commands + +*/ + +#include "mamedef.h" +#include "dac_control.h" + +//#include "../ChipMapper.h" +void chip_reg_write(void *param, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data); + +#define DAC_SMPL_RATE chip->SampleRate + +typedef struct _dac_control +{ + // Commands sent to dest-chip + UINT8 DstChipType; + UINT8 DstChipID; + UINT16 DstCommand; + UINT8 CmdSize; + + UINT32 Frequency; // Frequency (Hz) at which the commands are sent + UINT32 DataLen; // to protect from reading beyond End Of Data + const UINT8* Data; + UINT32 DataStart; // Position where to start + UINT8 StepSize; // usually 1, set to 2 for L/R interleaved data + UINT8 StepBase; // usually 0, set to 0/1 for L/R interleaved data + UINT32 CmdsToSend; + + // Running Bits: 0 (01) - is playing + // 2 (04) - loop sample (simple loop from start to end) + // 4 (10) - already sent this command + // 7 (80) - disabled + UINT8 Running; + UINT8 Reverse; + UINT32 Step; // Position in Player SampleRate + UINT32 Pos; // Position in Data SampleRate + UINT32 RemainCmds; + UINT32 RealPos; // true Position in Data (== Pos, if Reverse is off) + UINT8 DataStep; // always StepSize * CmdSize + + void* param; + UINT32 SampleRate; +} dac_control; + +#define NULL (void*)0 + +INLINE void daccontrol_SendCommand(dac_control *chip) +{ + UINT8 Port; + UINT8 Command; + UINT8 Data; + const UINT8* ChipData; + + if (chip->Running & 0x10) // command already sent + return; + if (chip->DataStart + chip->RealPos >= chip->DataLen) + return; + + //if (! chip->Reverse) + ChipData = chip->Data + (chip->DataStart + chip->RealPos); + //else + // ChipData = chip->Data + (chip->DataStart + chip->CmdsToSend - 1 - chip->Pos); + switch(chip->DstChipType) + { + // Support for the important chips + case 0x02: // YM2612 (16-bit Register (actually 9 Bit), 8-bit Data) + Port = (chip->DstCommand & 0xFF00) >> 8; + Command = (chip->DstCommand & 0x00FF) >> 0; + Data = ChipData[0x00]; + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, Port, Command, Data); + break; + case 0x11: // PWM (4-bit Register, 12-bit Data) + Port = (chip->DstCommand & 0x000F) >> 0; + Command = ChipData[0x01] & 0x0F; + Data = ChipData[0x00]; + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, Port, Command, Data); + break; + // Support for other chips (mainly for completeness) + case 0x00: // SN76496 (4-bit Register, 4-bit/10-bit Data) + Command = (chip->DstCommand & 0x00F0) >> 0; + Data = ChipData[0x00] & 0x0F; + if (Command & 0x10) + { + // Volume Change (4-Bit value) + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data); + } + else + { + // Frequency Write (10-Bit value) + Port = ((ChipData[0x01] & 0x03) << 4) | ((ChipData[0x00] & 0xF0) >> 4); + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data); + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Port); + } + break; + case 0x18: // OKIM6295 - TODO: verify + Command = (chip->DstCommand & 0x00FF) >> 0; + Data = ChipData[0x00]; + + if (! Command) + { + Port = (chip->DstCommand & 0x0F00) >> 8; + if (Data & 0x80) + { + // Sample Start + // write sample ID + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command, Data); + // write channel(s) that should play the sample + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command, Port << 4); + } + else + { + // Sample Stop + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command, Port << 3); + } + } + else + { + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command, Data); + } + break; + // Generic support: 8-bit Register, 8-bit Data + case 0x01: // YM2413 + case 0x03: // YM2151 + case 0x06: // YM2203 + case 0x09: // YM3812 + case 0x0A: // YM3526 + case 0x0B: // Y8950 + case 0x0F: // YMZ280B + case 0x12: // AY8910 + case 0x13: // GameBoy DMG + case 0x14: // NES APU +// case 0x15: // MultiPCM + case 0x16: // UPD7759 + case 0x17: // OKIM6258 + case 0x1D: // K053260 - TODO: Verify + case 0x1E: // Pokey - TODO: Verify + Command = (chip->DstCommand & 0x00FF) >> 0; + Data = ChipData[0x00]; + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command, Data); + break; + // Generic support: 16-bit Register, 8-bit Data + case 0x07: // YM2608 + case 0x08: // YM2610/B + case 0x0C: // YMF262 + case 0x0D: // YMF278B + case 0x0E: // YMF271 + case 0x19: // K051649 - TODO: Verify + case 0x1A: // K054539 - TODO: Verify + case 0x1C: // C140 - TODO: Verify + Port = (chip->DstCommand & 0xFF00) >> 8; + Command = (chip->DstCommand & 0x00FF) >> 0; + Data = ChipData[0x00]; + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, Port, Command, Data); + break; + // Generic support: 8-bit Register with Channel Select, 8-bit Data + case 0x05: // RF5C68 + case 0x10: // RF5C164 + case 0x1B: // HuC6280 + Port = (chip->DstCommand & 0xFF00) >> 8; + Command = (chip->DstCommand & 0x00FF) >> 0; + Data = ChipData[0x00]; + + if (Port != 0xFF) // Send Channel Select + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command >> 4, Port); + // Send Data + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, 0x00, Command & 0x0F, Data); + break; + // Generic support: 8-bit Register, 16-bit Data + case 0x1F: // QSound + Command = (chip->DstCommand & 0x00FF) >> 0; + chip_reg_write(chip->param, chip->DstChipType, chip->DstChipID, ChipData[0x00], ChipData[0x01], Command); + break; + } + chip->Running |= 0x10; + + return; +} + +INLINE UINT32 muldiv64round(UINT32 Multiplicand, UINT32 Multiplier, UINT32 Divisor) +{ + // Yes, I'm correctly rounding the values. + return (UINT32)(((UINT64)Multiplicand * Multiplier + Divisor / 2) / Divisor); +} + +void daccontrol_update(void *_info, UINT32 samples) +{ + dac_control *chip = (dac_control *)_info; + UINT32 NewPos; + INT16 RealDataStp; + + if (chip->Running & 0x80) // disabled + return; + if (! (chip->Running & 0x01)) // stopped + return; + + if (! chip->Reverse) + RealDataStp = chip->DataStep; + else + RealDataStp = -chip->DataStep; + + if (samples > 0x20) + { + // very effective Speed Hack for fast seeking + NewPos = chip->Step + (samples - 0x10); + NewPos = muldiv64round(NewPos * chip->DataStep, chip->Frequency, DAC_SMPL_RATE); + while(chip->RemainCmds && chip->Pos < NewPos) + { + chip->Pos += chip->DataStep; + chip->RealPos += RealDataStp; + chip->RemainCmds --; + } + } + + chip->Step += samples; + // Formula: Step * Freq / SampleRate + NewPos = muldiv64round(chip->Step * chip->DataStep, chip->Frequency, DAC_SMPL_RATE); + daccontrol_SendCommand(chip); + + while(chip->RemainCmds && chip->Pos < NewPos) + { + daccontrol_SendCommand(chip); + chip->Pos += chip->DataStep; + chip->RealPos += RealDataStp; + chip->Running &= ~0x10; + chip->RemainCmds --; + } + + if (! chip->RemainCmds && (chip->Running & 0x04)) + { + // loop back to start + chip->RemainCmds = chip->CmdsToSend; + chip->Step = 0x00; + chip->Pos = 0x00; + if (! chip->Reverse) + chip->RealPos = 0x00; + else + chip->RealPos = (chip->CmdsToSend - 0x01) * chip->DataStep; + } + + if (! chip->RemainCmds) + chip->Running &= ~0x01; // stop + + return; +} + +UINT8 device_start_daccontrol(void **_info, void *param, int SampleRate) +{ + dac_control *chip; + + chip = (dac_control *) calloc(1, sizeof(dac_control)); + *_info = (void *) chip; + + chip->param = param; + chip->SampleRate = SampleRate; + + chip->DstChipType = 0xFF; + chip->DstChipID = 0x00; + chip->DstCommand = 0x0000; + + chip->Running = 0xFF; // disable all actions (except setup_chip) + + return 1; +} + +void device_stop_daccontrol(void *_info) +{ + dac_control *chip = (dac_control *)_info; + + chip->Running = 0xFF; + + free(chip); + + return; +} + +void device_reset_daccontrol(void *_info) +{ + dac_control *chip = (dac_control *)_info; + + chip->DstChipType = 0x00; + chip->DstChipID = 0x00; + chip->DstCommand = 0x00; + chip->CmdSize = 0x00; + + chip->Frequency = 0; + chip->DataLen = 0x00; + chip->Data = NULL; + chip->DataStart = 0x00; + chip->StepSize = 0x00; + chip->StepBase = 0x00; + + chip->Running = 0x00; + chip->Reverse = 0x00; + chip->Step = 0x00; + chip->Pos = 0x00; + chip->RealPos = 0x00; + chip->RemainCmds = 0x00; + chip->DataStep = 0x00; + + return; +} + +void daccontrol_setup_chip(void *_info, UINT8 ChType, UINT8 ChNum, UINT16 Command) +{ + dac_control *chip = (dac_control *)_info; + + chip->DstChipType = ChType; // TypeID (e.g. 0x02 for YM2612) + chip->DstChipID = ChNum; // chip number (to send commands to 1st or 2nd chip) + chip->DstCommand = Command; // Port and Command (would be 0x02A for YM2612) + + switch(chip->DstChipType) + { + case 0x00: // SN76496 + if (chip->DstCommand & 0x0010) + chip->CmdSize = 0x01; // Volume Write + else + chip->CmdSize = 0x02; // Frequency Write + break; + case 0x02: // YM2612 + chip->CmdSize = 0x01; + break; + case 0x11: // PWM + case 0x1F: // QSound + chip->CmdSize = 0x02; + break; + default: + chip->CmdSize = 0x01; + break; + } + chip->DataStep = chip->CmdSize * chip->StepSize; + + return; +} + +void daccontrol_set_data(void *_info, UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase) +{ + dac_control *chip = (dac_control *)_info; + + if (chip->Running & 0x80) + return; + + if (DataLen && Data != NULL) + { + chip->DataLen = DataLen; + chip->Data = Data; + } + else + { + chip->DataLen = 0x00; + chip->Data = NULL; + } + chip->StepSize = StepSize ? StepSize : 1; + chip->StepBase = StepBase; + chip->DataStep = chip->CmdSize * chip->StepSize; + + return; +} + +void daccontrol_refresh_data(void *_info, UINT8* Data, UINT32 DataLen) +{ + // Should be called to fix the data pointer. (e.g. after a realloc) + dac_control *chip = (dac_control *)_info; + + if (chip->Running & 0x80) + return; + + if (DataLen && Data != NULL) + { + chip->DataLen = DataLen; + chip->Data = Data; + } + else + { + chip->DataLen = 0x00; + chip->Data = NULL; + } + + return; +} + +void daccontrol_set_frequency(void *_info, UINT32 Frequency) +{ + dac_control *chip = (dac_control *)_info; + + if (chip->Running & 0x80) + return; + + chip->Frequency = Frequency; + + return; +} + +void daccontrol_start(void *_info, UINT32 DataPos, UINT8 LenMode, UINT32 Length) +{ + dac_control *chip = (dac_control *)_info; + UINT16 CmdStepBase; + + if (chip->Running & 0x80) + return; + + CmdStepBase = chip->CmdSize * chip->StepBase; + if (DataPos != 0xFFFFFFFF) // skip setting DataStart, if Pos == -1 + { + chip->DataStart = DataPos + CmdStepBase; + if (chip->DataStart > chip->DataLen) // catch bad value and force silence + chip->DataStart = chip->DataLen; + } + + switch(LenMode & 0x0F) + { + case DCTRL_LMODE_IGNORE: // Length is already set - ignore + break; + case DCTRL_LMODE_CMDS: // Length = number of commands + chip->CmdsToSend = Length; + break; + case DCTRL_LMODE_MSEC: // Length = time in msec + chip->CmdsToSend = 1000 * Length / chip->Frequency; + break; + case DCTRL_LMODE_TOEND: // play unti stop-command is received (or data-end is reached) + chip->CmdsToSend = (chip->DataLen - (chip->DataStart - CmdStepBase)) / chip->DataStep; + break; + case DCTRL_LMODE_BYTES: // raw byte count + chip->CmdsToSend = Length / chip->DataStep; + break; + default: + chip->CmdsToSend = 0x00; + break; + } + chip->Reverse = (LenMode & 0x10) >> 4; + + chip->RemainCmds = chip->CmdsToSend; + chip->Step = 0x00; + chip->Pos = 0x00; + if (! chip->Reverse) + chip->RealPos = 0x00; + else + chip->RealPos = (chip->CmdsToSend - 0x01) * chip->DataStep; + + chip->Running &= ~0x04; + chip->Running |= (LenMode & 0x80) ? 0x04 : 0x00; // set loop mode + + chip->Running |= 0x01; // start + chip->Running &= ~0x10; // command isn't yet sent + + return; +} + +void daccontrol_stop(void *_info) +{ + dac_control *chip = (dac_control *)_info; + + if (chip->Running & 0x80) + return; + + chip->Running &= ~0x01; // stop + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/dac_control.h b/Frameworks/GME/vgmplay/chips/dac_control.h new file mode 100644 index 000000000..7c5e75d29 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/dac_control.h @@ -0,0 +1,16 @@ +void daccontrol_update(void *chip, UINT32 samples); +UINT8 device_start_daccontrol(void **chip, void *param, int samplerate); +void device_stop_daccontrol(void *chip); +void device_reset_daccontrol(void *chip); +void daccontrol_setup_chip(void *chip, UINT8 ChType, UINT8 ChNum, UINT16 Command); +void daccontrol_set_data(void *chip, UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase); +void daccontrol_refresh_data(void *chip, UINT8* Data, UINT32 DataLen); +void daccontrol_set_frequency(void *chip, UINT32 Frequency); +void daccontrol_start(void *chip, UINT32 DataPos, UINT8 LenMode, UINT32 Length); +void daccontrol_stop(void *chip); + +#define DCTRL_LMODE_IGNORE 0x00 +#define DCTRL_LMODE_CMDS 0x01 +#define DCTRL_LMODE_MSEC 0x02 +#define DCTRL_LMODE_TOEND 0x03 +#define DCTRL_LMODE_BYTES 0x0F diff --git a/Frameworks/GME/vgmplay/chips/emu2149.c b/Frameworks/GME/vgmplay/chips/emu2149.c new file mode 100644 index 000000000..5cbbab0e8 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/emu2149.c @@ -0,0 +1,524 @@ +/**************************************************************************** + + emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 + + 2001 04-28 : Version 1.00beta -- 1st Beta Release. + 2001 08-14 : Version 1.10 + 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). + 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. + 2002 10-13 : Version 1.14 -- Fixed the envelope unit. + 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask + 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope + frequency register is written before key-on. + + References: + psg.vhd -- 2000 written by Kazuhiro Tsujikawa. + s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). + ay8910.c -- 1998-2001 Author unknown (MAME). + MSX-Datapack -- 1991 ASCII Corp. + AY-3-8910 data sheet + +*****************************************************************************/ +#include +#include +#include +#include "emu2149.h" + +static e_uint32 voltbl[2][32] = { + {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, + 0x0B, 0x0D, 0x0F, 0x12, + 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, + 0xB4, 0xD6, 0xEB, 0xFF}, + {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, + 0x0B, 0x0B, 0x0F, 0x0F, + 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, + 0xB4, 0xB4, 0xFF, 0xFF} +}; + +#define GETA_BITS 24 + +static void +internal_refresh (PSG * psg) +{ + if (psg->quality) + { + psg->base_incr = 1 << GETA_BITS; + psg->realstep = (e_uint32) ((1 << 31) / psg->rate); + psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 8)); + psg->psgtime = 0; + } + else + { + psg->base_incr = + (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (8.0 * psg->rate)); + } +} + +EMU2149_API void +PSG_set_clock(PSG * psg, e_uint32 c) +{ + psg->clk = c; + internal_refresh(psg); +} + +EMU2149_API void +PSG_set_rate (PSG * psg, e_uint32 r) +{ + psg->rate = r ? r : 44100; + internal_refresh (psg); +} + +EMU2149_API void +PSG_set_quality (PSG * psg, e_uint32 q) +{ + psg->quality = q; + internal_refresh (psg); +} + +EMU2149_API PSG * +PSG_new (e_uint32 c, e_uint32 r) +{ + PSG *psg; + + psg = (PSG *) malloc (sizeof (PSG)); + if (psg == NULL) + return NULL; + memset(psg, 0x00, sizeof(PSG)); + + PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); + psg->clk = c; + psg->rate = r ? r : 44100; + PSG_set_quality (psg, 0); + psg->stereo_mask[0] = 0x03; + psg->stereo_mask[1] = 0x03; + psg->stereo_mask[2] = 0x03; + + return psg; +} + +EMU2149_API void +PSG_setFlags (PSG * psg, e_uint8 flags) +{ + if (flags & EMU2149_ZX_STEREO) + { + // ABC Stereo + psg->stereo_mask[0] = 0x01; + psg->stereo_mask[1] = 0x03; + psg->stereo_mask[2] = 0x02; + } + else + { + psg->stereo_mask[0] = 0x03; + psg->stereo_mask[1] = 0x03; + psg->stereo_mask[2] = 0x03; + } + + return; +} + +EMU2149_API void +PSG_setVolumeMode (PSG * psg, int type) +{ + switch (type) + { + case 1: + psg->voltbl = voltbl[EMU2149_VOL_YM2149]; + break; + case 2: + psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; + break; + default: + psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; + break; + } +} + +EMU2149_API e_uint32 +PSG_setMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask = mask; + } + return ret; +} + +EMU2149_API e_uint32 +PSG_toggleMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask ^= mask; + } + return ret; +} + +EMU2149_API void +PSG_reset (PSG * psg) +{ + int i; + + psg->base_count = 0; + + for (i = 0; i < 3; i++) + { + psg->cout[i] = 0; + psg->count[i] = 0x1000; + psg->freq[i] = 0; + psg->edge[i] = 0; + psg->volume[i] = 0; + } + + psg->mask = 0; + + for (i = 0; i < 16; i++) + psg->reg[i] = 0; + psg->adr = 0; + + psg->noise_seed = 0xffff; + psg->noise_count = 0x40; + psg->noise_freq = 0; + + psg->env_volume = 0; + psg->env_ptr = 0; + psg->env_freq = 0; + psg->env_count = 0; + psg->env_pause = 1; + + psg->out = 0; +} + +EMU2149_API void +PSG_delete (PSG * psg) +{ + free (psg); +} + +EMU2149_API e_uint8 +PSG_readIO (PSG * psg) +{ + return (e_uint8) (psg->reg[psg->adr]); +} + +EMU2149_API e_uint8 +PSG_readReg (PSG * psg, e_uint32 reg) +{ + return (e_uint8) (psg->reg[reg & 0x1f]); + +} + +EMU2149_API void +PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + PSG_writeReg (psg, psg->adr, val); + else + psg->adr = val & 0x1f; +} + +INLINE static e_int16 +calc (PSG * psg) +{ + + int i, noise; + e_uint32 incr; + e_int32 mix = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; // BS maintaining cout for stereo mix + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + mix += psg->cout[i]; + } + } + + return (e_int16) mix; +} + +EMU2149_API e_int16 +PSG_calc (PSG * psg) +{ + if (!psg->quality) + return (e_int16) (calc (psg) << 4); + + /* Simple rate converter */ + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->out += calc (psg); + psg->out >>= 1; + } + + psg->psgtime = psg->psgtime - psg->realstep; + + return (e_int16) (psg->out << 4); +} + +INLINE static void +calc_stereo (PSG * psg, e_int32 out[2]) +{ + int i, noise; + e_uint32 incr; + e_int32 l = 0, r = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; // BS maintaining cout for stereo mix + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + if (psg->stereo_mask[i] & 0x01) + l += psg->cout[i]; + if (psg->stereo_mask[i] & 0x02) + r += psg->cout[i]; + } + } + + out[0] = l << 5; + out[1] = r << 5; + + return; +} + +EMU2149_API void +PSG_calc_stereo (PSG * psg, e_int32 **out, e_int32 samples) +{ + e_int32 *bufMO = out[0]; + e_int32 *bufRO = out[1]; + e_int32 buffers[2]; + + int i; + + for (i = 0; i < samples; i ++) + { + if (!psg->quality) + { + calc_stereo (psg, buffers); + bufMO[i] = buffers[0]; + bufRO[i] = buffers[1]; + } + else + { + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->sprev[0] = psg->snext[0]; + psg->sprev[1] = psg->snext[1]; + calc_stereo (psg, psg->snext); + } + + psg->psgtime -= psg->realstep; + bufMO[i] = (e_int32) (((double) psg->snext[0] * (psg->psgstep - psg->psgtime) + + (double) psg->sprev[0] * psg->psgtime) / psg->psgstep); + bufRO[i] = (e_int32) (((double) psg->snext[1] * (psg->psgstep - psg->psgtime) + + (double) psg->sprev[1] * psg->psgtime) / psg->psgstep); + } + } +} + +EMU2149_API void +PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) +{ + int c; + + if (reg > 15) return; + + psg->reg[reg] = (e_uint8) (val & 0xff); + switch (reg) + { + case 0: + case 2: + case 4: + case 1: + case 3: + case 5: + c = reg >> 1; + psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; + break; + + case 6: + psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); + break; + + case 7: + psg->tmask[0] = (val & 1); + psg->tmask[1] = (val & 2); + psg->tmask[2] = (val & 4); + psg->nmask[0] = (val & 8); + psg->nmask[1] = (val & 16); + psg->nmask[2] = (val & 32); + break; + + case 8: + case 9: + case 10: + psg->volume[reg - 8] = val << 1; + + break; + + case 11: + case 12: + psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; + break; + + case 13: + psg->env_continue = (val >> 3) & 1; + psg->env_attack = (val >> 2) & 1; + psg->env_alternate = (val >> 1) & 1; + psg->env_hold = val & 1; + psg->env_face = psg->env_attack; + psg->env_pause = 0; + psg->env_count = 0x10000 - psg->env_freq; + psg->env_ptr = psg->env_face?0:0x1f; + break; + + case 14: + case 15: + default: + break; + } + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/emu2149.h b/Frameworks/GME/vgmplay/chips/emu2149.h new file mode 100644 index 000000000..7048ffade --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/emu2149.h @@ -0,0 +1,102 @@ +/* emu2149.h */ +#ifndef _EMU2149_H_ +#define _EMU2149_H_ +#include "emutypes.h" + +/*#ifdef EMU2149_DLL_EXPORTS +#define EMU2149_API __declspec(dllexport) +#elif EMU2149_DLL_IMPORTS +#define EMU2149_API __declspec(dllimport) +#else*/ +#define EMU2149_API +//#endif + +#define EMU2149_VOL_DEFAULT 1 +#define EMU2149_VOL_YM2149 0 +#define EMU2149_VOL_AY_3_8910 1 + +#define EMU2149_ZX_STEREO 0x80 + +#define PSG_MASK_CH(x) (1<<(x)) + +/*#ifdef __cplusplus +extern "C" +{ +#endif*/ + + typedef struct __PSG + { + + /* Volume Table */ + e_uint32 *voltbl; + + e_uint8 reg[0x20]; + e_int32 out; + e_int32 cout[3]; + + e_uint32 clk, rate, base_incr, quality; + + e_uint32 count[3]; + e_uint32 volume[3]; + e_uint32 freq[3]; + e_uint32 edge[3]; + e_uint32 tmask[3]; + e_uint32 nmask[3]; + e_uint32 mask; + e_uint32 stereo_mask[3]; + + e_uint32 base_count; + + e_uint32 env_volume; + e_uint32 env_ptr; + e_uint32 env_face; + + e_uint32 env_continue; + e_uint32 env_attack; + e_uint32 env_alternate; + e_uint32 env_hold; + e_uint32 env_pause; + e_uint32 env_reset; + + e_uint32 env_freq; + e_uint32 env_count; + + e_uint32 noise_seed; + e_uint32 noise_count; + e_uint32 noise_freq; + + /* rate converter */ + e_uint32 realstep; + e_uint32 psgtime; + e_uint32 psgstep; + e_int32 prev, next; + e_int32 sprev[2], snext[2]; + + /* I/O Ctrl */ + e_uint32 adr; + + } + PSG; + + EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); + EMU2149_API void PSG_set_clock(PSG * psg, e_uint32 c); + EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); + EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); + EMU2149_API void PSG_reset (PSG *); + EMU2149_API void PSG_delete (PSG *); + EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); + EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); + EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); + EMU2149_API e_uint8 PSG_readIO (PSG * psg); + EMU2149_API e_int16 PSG_calc (PSG *); + EMU2149_API void PSG_calc_stereo (PSG * psg, e_int32 **out, e_int32 samples); + EMU2149_API void PSG_setFlags (PSG * psg, e_uint8 flags); + EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); + EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); + EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); + +/*#ifdef __cplusplus +} +#endif*/ + +#endif diff --git a/Frameworks/GME/vgmplay/chips/emu2413.c b/Frameworks/GME/vgmplay/chips/emu2413.c new file mode 100644 index 000000000..b368f192a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/emu2413.c @@ -0,0 +1,2127 @@ +/*********************************************************************************** + + emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001 + + 2001 01-08 : Version 0.10 -- 1st version. + 2001 01-15 : Version 0.20 -- semi-public version. + 2001 01-16 : Version 0.30 -- 1st public version. + 2001 01-17 : Version 0.31 -- Fixed bassdrum problem. + : Version 0.32 -- LPF implemented. + 2001 01-18 : Version 0.33 -- Fixed the drum problem, refine the mix-down method. + -- Fixed the LFO bug. + 2001 01-24 : Version 0.35 -- Fixed the drum problem, + support undocumented EG behavior. + 2001 02-02 : Version 0.38 -- Improved the performance. + Fixed the hi-hat and cymbal model. + Fixed the default percussive datas. + Noise reduction. + Fixed the feedback problem. + 2001 03-03 : Version 0.39 -- Fixed some drum bugs. + Improved the performance. + 2001 03-04 : Version 0.40 -- Improved the feedback. + Change the default table size. + Clock and Rate can be changed during play. + 2001 06-24 : Version 0.50 -- Improved the hi-hat and the cymbal tone. + Added VRC7 patch (OPLL_reset_patch is changed). + Fixed OPLL_reset() bug. + Added OPLL_setMask, OPLL_getMask and OPLL_toggleMask. + Added OPLL_writeIO. + 2001 09-28 : Version 0.51 -- Removed the noise table. + 2002 01-28 : Version 0.52 -- Added Stereo mode. + 2002 02-07 : Version 0.53 -- Fixed some drum bugs. + 2002 02-20 : Version 0.54 -- Added the best quality mode. + 2002 03-02 : Version 0.55 -- Removed OPLL_init & OPLL_close. + 2002 05-30 : Version 0.60 -- Fixed HH&CYM generator and all voice datas. + 2004 04-10 : Version 0.61 -- Added YMF281B tone (defined by Chabin). + + References: + fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). + fmopl.c(fixed) -- (C) 2002 Jarek Burczynski. + s_opl.c -- 2001 written by Mamiya (NEZplug development). + fmgen.cpp -- 1999,2000 written by cisc. + fmpac.ill -- 2000 created by NARUTO. + MSX-Datapack + YMU757 data sheet + YM2143 data sheet + +**************************************************************************************/ + +/** + * Additions by Maxim: + * - per-channel panning + * + **/ +#include +#include +#include +#include +#include "mamedef.h" +#undef INLINE +#include "emu2413.h" +#include "panning.h" // Maxim + +/*#ifdef EMU2413_COMPACTION +#define OPLL_TONE_NUM 1 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 16] = { + { +#include "2413tone.h" + } +}; +#else +#define OPLL_TONE_NUM 3 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 16] = { + { +#include "2413tone.h" + }, + { +#include "vrc7tone.h" + }, + { +#include "281btone.h" + } +}; +#endif*/ + +// Note: Dump size changed to 8 per instrument, since 9-15 were unused. -VB +#define OPLL_TONE_NUM 1 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { + { +#include "2413tone.h" + } +}; + +/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.) */ +#define PG_BITS 9 +#define PG_WIDTH (1<>(b)) + +/* Leave the lower b bit(s). */ +#define LOWBITS(c,b) ((c)&((1<<(b))-1)) + +/* Expand x which is s bits to d bits. */ +#define EXPAND_BITS(x,s,d) ((x)<<((d)-(s))) + +/* Expand x which is s bits to d bits and fill expanded bits '1' */ +#define EXPAND_BITS_X(x,s,d) (((x)<<((d)-(s)))|((1<<((d)-(s)))-1)) + +/* Adjust envelope speed which depends on sampling rate. */ +#define RATE_ADJUST(x) (rate==49716?x:(e_uint32)((double)(x)*clk/72/rate + 0.5)) /* added 0.5 to round the value*/ + +#define MOD(o,x) (&(o)->slot[(x)<<1]) +#define CAR(o,x) (&(o)->slot[((x)<<1)|1]) + +#define BIT(s,b) (((s)>>(b))&1) + +/* Input clock */ +static e_uint32 clk = 844451141; +/* Sampling rate */ +static e_uint32 rate = 3354932; + +/* WaveTable for each envelope amp */ +static e_uint16 fullsintable[PG_WIDTH]; +static e_uint16 halfsintable[PG_WIDTH]; + +static e_uint16 *waveform[2] = { fullsintable, halfsintable }; + +/* LFO Table */ +static e_int32 pmtable[PM_PG_WIDTH]; +static e_int32 amtable[AM_PG_WIDTH]; + +/* Phase delta for LFO */ +static e_uint32 pm_dphase; +static e_uint32 am_dphase; + +/* dB to Liner table */ +static e_int16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2]; + +/* Liner to Log curve conversion table (for Attack rate). */ +static e_uint16 AR_ADJUST_TABLE[1 << EG_BITS]; + +/* Empty voice data */ +static OPLL_PATCH null_patch = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* Basic voice Data */ +static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; + +/* Definition of envelope mode */ +enum OPLL_EG_STATE +{ READY, ATTACK, DECAY, SUSHOLD, SUSTINE, RELEASE, SETTLE, FINISH }; + +/* Phase incr table for Attack */ +static e_uint32 dphaseARTable[16][16]; +/* Phase incr table for Decay and Release */ +static e_uint32 dphaseDRTable[16][16]; + +/* KSL + TL Table */ +static e_uint32 tllTable[16][8][1 << TL_BITS][4]; +static e_int32 rksTable[2][8][2]; + +/* Phase incr table for PG */ +static e_uint32 dphaseTable[512][8][16]; + +/*************************************************** + + Create tables + +****************************************************/ +INLINE static e_int32 +Min (e_int32 i, e_int32 j) +{ + if (i < j) + return i; + else + return j; +} + +/* Table for AR to LogCurve. */ +static void +makeAdjustTable (void) +{ + e_int32 i; + + AR_ADJUST_TABLE[0] = (1 << EG_BITS) - 1; + for (i = 1; i < (1<= DB_MUTE) DB2LIN_TABLE[i] = 0; + DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (e_int16) (-DB2LIN_TABLE[i]); + } +} + +/* Liner(+0.0 - +1.0) to dB((1<> (20 - DP_BITS)); +} + +static void +makeTllTable (void) +{ +#define dB2(x) ((x)*2) + + static double kltable[16] = { + dB2 (0.000), dB2 (9.000), dB2 (12.000), dB2 (13.875), dB2 (15.000), dB2 (16.125), dB2 (16.875), dB2 (17.625), + dB2 (18.000), dB2 (18.750), dB2 (19.125), dB2 (19.500), dB2 (19.875), dB2 (20.250), dB2 (20.625), dB2 (21.000) + }; + + e_int32 tmp; + e_int32 fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) + for (block = 0; block < 8; block++) + for (TL = 0; TL < 64; TL++) + for (KL = 0; KL < 4; KL++) + { + if (KL == 0) + { + tllTable[fnum][block][TL][KL] = TL2EG (TL); + } + else + { + tmp = (e_int32) (kltable[fnum] - dB2 (3.000) * (7 - block)); + if (tmp <= 0) + tllTable[fnum][block][TL][KL] = TL2EG (TL); + else + tllTable[fnum][block][TL][KL] = (e_uint32) ((tmp >> (3 - KL)) / EG_STEP) + TL2EG (TL); + } + } +} + +#ifdef USE_SPEC_ENV_SPEED +static double attacktime[16][4] = { + {0, 0, 0, 0}, + {1730.15, 1400.60, 1153.43, 988.66}, + {865.08, 700.30, 576.72, 494.33}, + {432.54, 350.15, 288.36, 247.16}, + {216.27, 175.07, 144.18, 123.58}, + {108.13, 87.54, 72.09, 61.79}, + {54.07, 43.77, 36.04, 30.90}, + {27.03, 21.88, 18.02, 15.45}, + {13.52, 10.94, 9.01, 7.72}, + {6.76, 5.47, 4.51, 3.86}, + {3.38, 2.74, 2.25, 1.93}, + {1.69, 1.37, 1.13, 0.97}, + {0.84, 0.70, 0.60, 0.54}, + {0.50, 0.42, 0.34, 0.30}, + {0.28, 0.22, 0.18, 0.14}, + {0.00, 0.00, 0.00, 0.00} +}; + +static double decaytime[16][4] = { + {0, 0, 0, 0}, + {20926.60, 16807.20, 14006.00, 12028.60}, + {10463.30, 8403.58, 7002.98, 6014.32}, + {5231.64, 4201.79, 3501.49, 3007.16}, + {2615.82, 2100.89, 1750.75, 1503.58}, + {1307.91, 1050.45, 875.37, 751.79}, + {653.95, 525.22, 437.69, 375.90}, + {326.98, 262.61, 218.84, 187.95}, + {163.49, 131.31, 109.42, 93.97}, + {81.74, 65.65, 54.71, 46.99}, + {40.87, 32.83, 27.36, 23.49}, + {20.44, 16.41, 13.68, 11.75}, + {10.22, 8.21, 6.84, 5.87}, + {5.11, 4.10, 3.42, 2.94}, + {2.55, 2.05, 1.71, 1.47}, + {1.27, 1.27, 1.27, 1.27} +}; +#endif + +/* Rate Table for Attack */ +static void +makeDphaseARTable (void) +{ + e_int32 AR, Rks, RM, RL; + +#ifdef USE_SPEC_ENV_SPEED + e_uint32 attacktable[16][4]; + + for (RM = 0; RM < 16; RM++) + for (RL = 0; RL < 4; RL++) + { + if (RM == 0) + attacktable[RM][RL] = 0; + else if (RM == 15) + attacktable[RM][RL] = EG_DP_WIDTH; + else + attacktable[RM][RL] = (e_uint32) ((double) (1 << EG_DP_BITS) / (attacktime[RM][RL] * 3579545 / 72000)); + + } +#endif + + for (AR = 0; AR < 16; AR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = AR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (AR) + { + case 0: + dphaseARTable[AR][Rks] = 0; + break; + case 15: + dphaseARTable[AR][Rks] = 0;/*EG_DP_WIDTH;*/ + break; + default: +#ifdef USE_SPEC_ENV_SPEED + dphaseARTable[AR][Rks] = RATE_ADJUST (attacktable[RM][RL]); +#else + dphaseARTable[AR][Rks] = RATE_ADJUST ((3 * (RL + 4) << (RM + 1))); +#endif + break; + } + } +} + +/* Rate Table for Decay and Release */ +static void +makeDphaseDRTable (void) +{ + e_int32 DR, Rks, RM, RL; + +#ifdef USE_SPEC_ENV_SPEED + e_uint32 decaytable[16][4]; + + for (RM = 0; RM < 16; RM++) + for (RL = 0; RL < 4; RL++) + if (RM == 0) + decaytable[RM][RL] = 0; + else + decaytable[RM][RL] = (e_uint32) ((double) (1 << EG_DP_BITS) / (decaytime[RM][RL] * 3579545 / 72000)); +#endif + + for (DR = 0; DR < 16; DR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = DR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (DR) + { + case 0: + dphaseDRTable[DR][Rks] = 0; + break; + default: +#ifdef USE_SPEC_ENV_SPEED + dphaseDRTable[DR][Rks] = RATE_ADJUST (decaytable[RM][RL]); +#else + dphaseDRTable[DR][Rks] = RATE_ADJUST ((RL + 4) << (RM - 1)); +#endif + break; + } + } +} + +static void +makeRksTable (void) +{ + + e_int32 fnum8, block, KR; + + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) + for (KR = 0; KR < 2; KR++) + { + if (KR != 0) + rksTable[fnum8][block][KR] = (block << 1) + fnum8; + else + rksTable[fnum8][block][KR] = block >> 1; + } +} + +void +OPLL_dump2patch (const e_uint8 * dump, OPLL_PATCH * patch) +{ + patch[0].AM = (dump[0] >> 7) & 1; + patch[1].AM = (dump[1] >> 7) & 1; + patch[0].PM = (dump[0] >> 6) & 1; + patch[1].PM = (dump[1] >> 6) & 1; + patch[0].EG = (dump[0] >> 5) & 1; + patch[1].EG = (dump[1] >> 5) & 1; + patch[0].KR = (dump[0] >> 4) & 1; + patch[1].KR = (dump[1] >> 4) & 1; + patch[0].ML = (dump[0]) & 15; + patch[1].ML = (dump[1]) & 15; + patch[0].KL = (dump[2] >> 6) & 3; + patch[1].KL = (dump[3] >> 6) & 3; + patch[0].TL = (dump[2]) & 63; + patch[0].FB = (dump[3]) & 7; + patch[0].WF = (dump[3] >> 3) & 1; + patch[1].WF = (dump[3] >> 4) & 1; + patch[0].AR = (dump[4] >> 4) & 15; + patch[1].AR = (dump[5] >> 4) & 15; + patch[0].DR = (dump[4]) & 15; + patch[1].DR = (dump[5]) & 15; + patch[0].SL = (dump[6] >> 4) & 15; + patch[1].SL = (dump[7] >> 4) & 15; + patch[0].RR = (dump[6]) & 15; + patch[1].RR = (dump[7]) & 15; +} + +void +OPLL_getDefaultPatch (e_int32 type, e_int32 num, OPLL_PATCH * patch) +{ + OPLL_dump2patch (default_inst[type] + num * 8, patch); +} + +static void +makeDefaultPatch () +{ + e_int32 i, j; + + for (i = 0; i < OPLL_TONE_NUM; i++) + for (j = 0; j < 19; j++) + OPLL_getDefaultPatch (i, j, &default_patch[i][j * 2]); + +} + +void +OPLL_setPatch (OPLL * opll, const e_uint8 * dump) +{ + OPLL_PATCH patch[2]; + int i; + + for (i = 0; i < 19; i++) + { + OPLL_dump2patch (dump + i * 8, patch); + memcpy (&opll->patch[i*2+0], &patch[0], sizeof (OPLL_PATCH)); + memcpy (&opll->patch[i*2+1], &patch[1], sizeof (OPLL_PATCH)); + } +} + +void +OPLL_patch2dump (const OPLL_PATCH * patch, e_uint8 * dump) +{ + dump[0] = (e_uint8) ((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); + dump[1] = (e_uint8) ((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); + dump[2] = (e_uint8) ((patch[0].KL << 6) + patch[0].TL); + dump[3] = (e_uint8) ((patch[1].KL << 6) + (patch[1].WF << 4) + (patch[0].WF << 3) + patch[0].FB); + dump[4] = (e_uint8) ((patch[0].AR << 4) + patch[0].DR); + dump[5] = (e_uint8) ((patch[1].AR << 4) + patch[1].DR); + dump[6] = (e_uint8) ((patch[0].SL << 4) + patch[0].RR); + dump[7] = (e_uint8) ((patch[1].SL << 4) + patch[1].RR); + dump[8] = 0; + dump[9] = 0; + dump[10] = 0; + dump[11] = 0; + dump[12] = 0; + dump[13] = 0; + dump[14] = 0; + dump[15] = 0; +} + +/************************************************************ + + Calc Parameters + +************************************************************/ + +INLINE static e_uint32 +calc_eg_dphase (OPLL_SLOT * slot) +{ + + switch (slot->eg_mode) + { + case ATTACK: + return dphaseARTable[slot->patch->AR][slot->rks]; + + case DECAY: + return dphaseDRTable[slot->patch->DR][slot->rks]; + + case SUSHOLD: + return 0; + + case SUSTINE: + return dphaseDRTable[slot->patch->RR][slot->rks]; + + case RELEASE: + if (slot->sustine) + return dphaseDRTable[5][slot->rks]; + else if (slot->patch->EG) + return dphaseDRTable[slot->patch->RR][slot->rks]; + else + return dphaseDRTable[7][slot->rks]; + + case SETTLE: + return dphaseDRTable[15][0]; + + case FINISH: + return 0; + + default: + return 0; + } +} + +/************************************************************* + + OPLL internal interfaces + +*************************************************************/ +#define SLOT_BD1 12 +#define SLOT_BD2 13 +#define SLOT_HH 14 +#define SLOT_SD 15 +#define SLOT_TOM 16 +#define SLOT_CYM 17 + +#define UPDATE_PG(S) (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch->ML] +#define UPDATE_TLL(S)\ +(((S)->type==0)?\ +((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->patch->TL][(S)->patch->KL]):\ +((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->volume][(S)->patch->KL])) +#define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum)>>8][(S)->block][(S)->patch->KR] +#define UPDATE_WF(S) (S)->sintbl = waveform[(S)->patch->WF] +#define UPDATE_EG(S) (S)->eg_dphase = calc_eg_dphase(S) +#define UPDATE_ALL(S)\ + UPDATE_PG(S);\ + UPDATE_TLL(S);\ + UPDATE_RKS(S);\ + UPDATE_WF(S); \ + UPDATE_EG(S) /* EG should be updated last. */ + + +/* Slot key on */ +INLINE static void +slotOn (OPLL_SLOT * slot) +{ + slot->eg_mode = ATTACK; + slot->eg_phase = 0; + slot->phase = 0; + UPDATE_EG(slot); +} + +/* Slot key on without reseting the phase */ +INLINE static void +slotOn2 (OPLL_SLOT * slot) +{ + slot->eg_mode = ATTACK; + slot->eg_phase = 0; + UPDATE_EG(slot); +} + +/* Slot key off */ +INLINE static void +slotOff (OPLL_SLOT * slot) +{ + if (slot->eg_mode == ATTACK) + slot->eg_phase = EXPAND_BITS (AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS); + slot->eg_mode = RELEASE; + UPDATE_EG(slot); +} + +/* Channel key on */ +INLINE static void +keyOn (OPLL * opll, e_int32 i) +{ + if (!opll->slot_on_flag[i * 2]) + slotOn (MOD(opll,i)); + if (!opll->slot_on_flag[i * 2 + 1]) + slotOn (CAR(opll,i)); + opll->key_status[i] = 1; +} + +/* Channel key off */ +INLINE static void +keyOff (OPLL * opll, e_int32 i) +{ + if (opll->slot_on_flag[i * 2 + 1]) + slotOff (CAR(opll,i)); + opll->key_status[i] = 0; +} + +INLINE static void +keyOn_BD (OPLL * opll) +{ + keyOn (opll, 6); +} +INLINE static void +keyOn_SD (OPLL * opll) +{ + if (!opll->slot_on_flag[SLOT_SD]) + slotOn (CAR(opll,7)); +} +INLINE static void +keyOn_TOM (OPLL * opll) +{ + if (!opll->slot_on_flag[SLOT_TOM]) + slotOn (MOD(opll,8)); +} +INLINE static void +keyOn_HH (OPLL * opll) +{ + if (!opll->slot_on_flag[SLOT_HH]) + slotOn2 (MOD(opll,7)); +} +INLINE static void +keyOn_CYM (OPLL * opll) +{ + if (!opll->slot_on_flag[SLOT_CYM]) + slotOn2 (CAR(opll,8)); +} + +/* Drum key off */ +INLINE static void +keyOff_BD (OPLL * opll) +{ + keyOff (opll, 6); +} +INLINE static void +keyOff_SD (OPLL * opll) +{ + if (opll->slot_on_flag[SLOT_SD]) + slotOff (CAR(opll,7)); +} +INLINE static void +keyOff_TOM (OPLL * opll) +{ + if (opll->slot_on_flag[SLOT_TOM]) + slotOff (MOD(opll,8)); +} +INLINE static void +keyOff_HH (OPLL * opll) +{ + if (opll->slot_on_flag[SLOT_HH]) + slotOff (MOD(opll,7)); +} +INLINE static void +keyOff_CYM (OPLL * opll) +{ + if (opll->slot_on_flag[SLOT_CYM]) + slotOff (CAR(opll,8)); +} + +/* Change a voice */ +INLINE static void +setPatch (OPLL * opll, e_int32 i, e_int32 num) +{ + opll->patch_number[i] = num; + MOD(opll,i)->patch = &opll->patch[num * 2 + 0]; + CAR(opll,i)->patch = &opll->patch[num * 2 + 1]; +} + +/* Change a rhythm voice */ +INLINE static void +setSlotPatch (OPLL_SLOT * slot, OPLL_PATCH * patch) +{ + slot->patch = patch; +} + +/* Set sustine parameter */ +INLINE static void +setSustine (OPLL * opll, e_int32 c, e_int32 sustine) +{ + CAR(opll,c)->sustine = sustine; + if (MOD(opll,c)->type) + MOD(opll,c)->sustine = sustine; +} + +/* Volume : 6bit ( Volume register << 2 ) */ +INLINE static void +setVolume (OPLL * opll, e_int32 c, e_int32 volume) +{ + CAR(opll,c)->volume = volume; +} + +INLINE static void +setSlotVolume (OPLL_SLOT * slot, e_int32 volume) +{ + slot->volume = volume; +} + +/* Set F-Number ( fnum : 9bit ) */ +INLINE static void +setFnumber (OPLL * opll, e_int32 c, e_int32 fnum) +{ + CAR(opll,c)->fnum = fnum; + MOD(opll,c)->fnum = fnum; +} + +/* Set Block data (block : 3bit ) */ +INLINE static void +setBlock (OPLL * opll, e_int32 c, e_int32 block) +{ + CAR(opll,c)->block = block; + MOD(opll,c)->block = block; +} + +/* Change Rhythm Mode */ +INLINE static void +update_rhythm_mode (OPLL * opll) +{ + if (opll->patch_number[6] & 0x10) + { + if (!(opll->slot_on_flag[SLOT_BD2] | (opll->reg[0x0e] & 0x20))) + { + opll->slot[SLOT_BD1].eg_mode = FINISH; + opll->slot[SLOT_BD2].eg_mode = FINISH; + setPatch (opll, 6, opll->reg[0x36] >> 4); + } + } + else if (opll->reg[0x0e] & 0x20) + { + opll->patch_number[6] = 16; + opll->slot[SLOT_BD1].eg_mode = FINISH; + opll->slot[SLOT_BD2].eg_mode = FINISH; + setSlotPatch (&opll->slot[SLOT_BD1], &opll->patch[16 * 2 + 0]); + setSlotPatch (&opll->slot[SLOT_BD2], &opll->patch[16 * 2 + 1]); + } + + if (opll->patch_number[7] & 0x10) + { + if (!((opll->slot_on_flag[SLOT_HH] && opll->slot_on_flag[SLOT_SD]) | (opll->reg[0x0e] & 0x20))) + { + opll->slot[SLOT_HH].type = 0; + opll->slot[SLOT_HH].eg_mode = FINISH; + opll->slot[SLOT_SD].eg_mode = FINISH; + setPatch (opll, 7, opll->reg[0x37] >> 4); + } + } + else if (opll->reg[0x0e] & 0x20) + { + opll->patch_number[7] = 17; + opll->slot[SLOT_HH].type = 1; + opll->slot[SLOT_HH].eg_mode = FINISH; + opll->slot[SLOT_SD].eg_mode = FINISH; + setSlotPatch (&opll->slot[SLOT_HH], &opll->patch[17 * 2 + 0]); + setSlotPatch (&opll->slot[SLOT_SD], &opll->patch[17 * 2 + 1]); + } + + if (opll->patch_number[8] & 0x10) + { + if (!((opll->slot_on_flag[SLOT_CYM] && opll->slot_on_flag[SLOT_TOM]) | (opll->reg[0x0e] & 0x20))) + { + opll->slot[SLOT_TOM].type = 0; + opll->slot[SLOT_TOM].eg_mode = FINISH; + opll->slot[SLOT_CYM].eg_mode = FINISH; + setPatch (opll, 8, opll->reg[0x38] >> 4); + } + } + else if (opll->reg[0x0e] & 0x20) + { + opll->patch_number[8] = 18; + opll->slot[SLOT_TOM].type = 1; + opll->slot[SLOT_TOM].eg_mode = FINISH; + opll->slot[SLOT_CYM].eg_mode = FINISH; + setSlotPatch (&opll->slot[SLOT_TOM], &opll->patch[18 * 2 + 0]); + setSlotPatch (&opll->slot[SLOT_CYM], &opll->patch[18 * 2 + 1]); + } +} + +INLINE static void +update_key_status (OPLL * opll) +{ + int ch; + + for (ch = 0; ch < 9; ch++) + opll->slot_on_flag[ch * 2] = opll->slot_on_flag[ch * 2 + 1] = (opll->reg[0x20 + ch]) & 0x10; + + if (opll->reg[0x0e] & 0x20) + { + opll->slot_on_flag[SLOT_BD1] |= (opll->reg[0x0e] & 0x10); + opll->slot_on_flag[SLOT_BD2] |= (opll->reg[0x0e] & 0x10); + opll->slot_on_flag[SLOT_SD] |= (opll->reg[0x0e] & 0x08); + opll->slot_on_flag[SLOT_HH] |= (opll->reg[0x0e] & 0x01); + opll->slot_on_flag[SLOT_TOM] |= (opll->reg[0x0e] & 0x04); + opll->slot_on_flag[SLOT_CYM] |= (opll->reg[0x0e] & 0x02); + } +} + +void +OPLL_copyPatch (OPLL * opll, e_int32 num, OPLL_PATCH * patch) +{ + memcpy (&opll->patch[num], patch, sizeof (OPLL_PATCH)); +} + +/*********************************************************** + + Initializing + +***********************************************************/ + +static void +OPLL_SLOT_reset (OPLL_SLOT * slot, int type) +{ + slot->type = type; + slot->sintbl = waveform[0]; + slot->phase = 0; + slot->dphase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->feedback = 0; + slot->eg_mode = FINISH; + slot->eg_phase = EG_DP_WIDTH; + slot->eg_dphase = 0; + slot->rks = 0; + slot->tll = 0; + slot->sustine = 0; + slot->fnum = 0; + slot->block = 0; + slot->volume = 0; + slot->pgout = 0; + slot->egout = 0; + slot->patch = &null_patch; +} + +static void +internal_refresh (void) +{ + makeDphaseTable (); + makeDphaseARTable (); + makeDphaseDRTable (); + pm_dphase = (e_uint32) RATE_ADJUST (PM_SPEED * PM_DP_WIDTH / (clk / 72)); + am_dphase = (e_uint32) RATE_ADJUST (AM_SPEED * AM_DP_WIDTH / (clk / 72)); +} + +static void +maketables (e_uint32 c, e_uint32 r) +{ + if (c != clk) + { + clk = c; + makePmTable (); + makeAmTable (); + makeDB2LinTable (); + makeAdjustTable (); + makeTllTable (); + makeRksTable (); + makeSinTable (); + makeDefaultPatch (); + } + + if (r != rate) + { + rate = r; + internal_refresh (); + } +} + +OPLL * +OPLL_new (e_uint32 clk, e_uint32 rate) +{ + OPLL *opll; + e_int32 i; + + maketables (clk, rate); + + opll = (OPLL *) calloc (sizeof (OPLL), 1); + if (opll == NULL) + return NULL; + + opll->vrc7_mode = 0x00; + + for (i = 0; i < 19 * 2; i++) + memcpy(&opll->patch[i],&null_patch,sizeof(OPLL_PATCH)); + for (i = 0; i < 14; i++) + centre_panning( opll->pan[i] ); + + opll->mask = 0; + + OPLL_reset (opll); + OPLL_reset_patch (opll, 0); + + return opll; +} + + +void +OPLL_delete (OPLL * opll) +{ + free (opll); +} + + +/* Reset patch datas by system default. */ +void +OPLL_reset_patch (OPLL * opll, e_int32 type) +{ + e_int32 i; + + for (i = 0; i < 19 * 2; i++) + OPLL_copyPatch (opll, i, &default_patch[type % OPLL_TONE_NUM][i]); +} + +/* Reset whole of OPLL except patch datas. */ +void +OPLL_reset (OPLL * opll) +{ + e_int32 i; + + if (!opll) + return; + + opll->adr = 0; + opll->out = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->noise_seed = 0xffff; + //opll->mask = 0; + + for (i = 0; i <18; i++) + OPLL_SLOT_reset(&opll->slot[i], i%2); + + for (i = 0; i < 9; i++) + { + opll->key_status[i] = 0; + setPatch (opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg (opll, i, 0); + +#ifndef EMU2413_COMPACTION + opll->realstep = (e_uint32) ((1 << 31) / rate); + opll->opllstep = (e_uint32) ((1 << 31) / (clk / 72)); + opll->oplltime = 0; + /*for (i = 0; i < 14; i++) + { + //centre_panning( opll->pan[i] ); + opll->pan[i][0] = 1.0f; + opll->pan[i][1] = 1.0f; + }*/ + opll->sprev[0] = opll->sprev[1] = 0; + opll->snext[0] = opll->snext[1] = 0; +#endif +} + +/* Force Refresh (When external program changes some parameters). */ +void +OPLL_forceRefresh (OPLL * opll) +{ + e_int32 i; + + if (opll == NULL) + return; + + for (i = 0; i < 9; i++) + setPatch(opll,i,opll->patch_number[i]); + + for (i = 0; i < 18; i++) + { + UPDATE_PG (&opll->slot[i]); + UPDATE_RKS (&opll->slot[i]); + UPDATE_TLL (&opll->slot[i]); + UPDATE_WF (&opll->slot[i]); + UPDATE_EG (&opll->slot[i]); + } +} + +void +OPLL_set_rate (OPLL * opll, e_uint32 r) +{ + if (opll->quality) + rate = 49716; + else + rate = r; + internal_refresh (); + rate = r; +} + +void +OPLL_set_quality (OPLL * opll, e_uint32 q) +{ + opll->quality = q; + OPLL_set_rate (opll, rate); +} + +/********************************************************* + + Generate wave data + +*********************************************************/ +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */ +#if ( SLOT_AMP_BITS - PG_BITS ) > 0 +#define wave2_2pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS )) +#else +#define wave2_2pi(e) ( (e) << ( PG_BITS - SLOT_AMP_BITS )) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */ +#if ( SLOT_AMP_BITS - PG_BITS - 1 ) == 0 +#define wave2_4pi(e) (e) +#elif ( SLOT_AMP_BITS - PG_BITS - 1 ) > 0 +#define wave2_4pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 1 )) +#else +#define wave2_4pi(e) ( (e) << ( 1 + PG_BITS - SLOT_AMP_BITS )) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */ +#if ( SLOT_AMP_BITS - PG_BITS - 2 ) == 0 +#define wave2_8pi(e) (e) +#elif ( SLOT_AMP_BITS - PG_BITS - 2 ) > 0 +#define wave2_8pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 2 )) +#else +#define wave2_8pi(e) ( (e) << ( 2 + PG_BITS - SLOT_AMP_BITS )) +#endif + +/* Update AM, PM unit */ +static void +update_ampm (OPLL * opll) +{ + opll->pm_phase = (opll->pm_phase + pm_dphase) & (PM_DP_WIDTH - 1); + opll->am_phase = (opll->am_phase + am_dphase) & (AM_DP_WIDTH - 1); + opll->lfo_am = amtable[HIGHBITS (opll->am_phase, AM_DP_BITS - AM_PG_BITS)]; + opll->lfo_pm = pmtable[HIGHBITS (opll->pm_phase, PM_DP_BITS - PM_PG_BITS)]; +} + +/* PG */ +INLINE static void +calc_phase (OPLL_SLOT * slot, e_int32 lfo) +{ + if (slot->patch->PM) + slot->phase += (slot->dphase * lfo) >> PM_AMP_BITS; + else + slot->phase += slot->dphase; + + slot->phase &= (DP_WIDTH - 1); + + slot->pgout = HIGHBITS (slot->phase, DP_BASE_BITS); +} + +/* Update Noise unit */ +static void +update_noise (OPLL * opll) +{ + if(opll->noise_seed&1) opll->noise_seed ^= 0x8003020; + opll->noise_seed >>= 1; +} + +/* EG */ +static void +calc_envelope (OPLL_SLOT * slot, e_int32 lfo) +{ +#define S2E(x) (SL2EG((e_int32)(x/SL_STEP))<<(EG_DP_BITS-EG_BITS)) + + static e_uint32 SL[16] = { + S2E (0.0), S2E (3.0), S2E (6.0), S2E (9.0), S2E (12.0), S2E (15.0), S2E (18.0), S2E (21.0), + S2E (24.0), S2E (27.0), S2E (30.0), S2E (33.0), S2E (36.0), S2E (39.0), S2E (42.0), S2E (48.0) + }; + + e_uint32 egout; + + switch (slot->eg_mode) + { + case ATTACK: + egout = AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)]; + slot->eg_phase += slot->eg_dphase; + if((EG_DP_WIDTH & slot->eg_phase)||(slot->patch->AR==15)) + { + egout = 0; + slot->eg_phase = 0; + slot->eg_mode = DECAY; + UPDATE_EG (slot); + } + break; + + case DECAY: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (slot->eg_phase >= SL[slot->patch->SL]) + { + if (slot->patch->EG) + { + slot->eg_phase = SL[slot->patch->SL]; + slot->eg_mode = SUSHOLD; + UPDATE_EG (slot); + } + else + { + slot->eg_phase = SL[slot->patch->SL]; + slot->eg_mode = SUSTINE; + UPDATE_EG (slot); + } + } + break; + + case SUSHOLD: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + if (slot->patch->EG == 0) + { + slot->eg_mode = SUSTINE; + UPDATE_EG (slot); + } + break; + + case SUSTINE: + case RELEASE: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (egout >= (1 << EG_BITS)) + { + slot->eg_mode = FINISH; + egout = (1 << EG_BITS) - 1; + } + break; + + case SETTLE: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (egout >= (1 << EG_BITS)) + { + slot->eg_mode = ATTACK; + egout = (1 << EG_BITS) - 1; + UPDATE_EG(slot); + } + break; + + case FINISH: + egout = (1 << EG_BITS) - 1; + break; + + default: + egout = (1 << EG_BITS) - 1; + break; + } + + if (slot->patch->AM) + egout = EG2DB (egout + slot->tll) + lfo; + else + egout = EG2DB (egout + slot->tll); + + if (egout >= DB_MUTE) + egout = DB_MUTE - 1; + + slot->egout = egout | 3; +} + +/* CARRIOR */ +INLINE static e_int32 +calc_slot_car (OPLL_SLOT * slot, e_int32 fm) +{ + if (slot->egout >= (DB_MUTE - 1)) + { + slot->output[0] = 0; + } + else + { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout]; + } + + slot->output[1] = (slot->output[1] + slot->output[0]) >> 1; + return slot->output[1]; +} + +/* MODULATOR */ +INLINE static e_int32 +calc_slot_mod (OPLL_SLOT * slot) +{ + e_int32 fm; + + slot->output[1] = slot->output[0]; + + if (slot->egout >= (DB_MUTE - 1)) + { + slot->output[0] = 0; + } + else if (slot->patch->FB != 0) + { + fm = wave2_4pi (slot->feedback) >> (7 - slot->patch->FB); + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+fm)&(PG_WIDTH-1)] + slot->egout]; + } + else + { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; + } + + slot->feedback = (slot->output[1] + slot->output[0]) >> 1; + + return slot->feedback; + +} + +/* TOM */ +INLINE static e_int32 +calc_slot_tom (OPLL_SLOT * slot) +{ + if (slot->egout >= (DB_MUTE - 1)) + return 0; + + return DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; + +} + +/* SNARE */ +INLINE static e_int32 +calc_slot_snare (OPLL_SLOT * slot, e_uint32 noise) +{ + if(slot->egout>=(DB_MUTE-1)) + return 0; + + if(BIT(slot->pgout,7)) + return DB2LIN_TABLE[(noise?DB_POS(0.0):DB_POS(15.0))+slot->egout]; + else + return DB2LIN_TABLE[(noise?DB_NEG(0.0):DB_NEG(15.0))+slot->egout]; +} + +/* + TOP-CYM + */ +INLINE static e_int32 +calc_slot_cym (OPLL_SLOT * slot, e_uint32 pgout_hh) +{ + e_uint32 dbout; + + if (slot->egout >= (DB_MUTE - 1)) + return 0; + else if( + /* the same as fmopl.c */ + ((BIT(pgout_hh,PG_BITS-8)^BIT(pgout_hh,PG_BITS-1))|BIT(pgout_hh,PG_BITS-7)) ^ + /* different from fmopl.c */ + (BIT(slot->pgout,PG_BITS-7)&!BIT(slot->pgout,PG_BITS-5)) + ) + dbout = DB_NEG(3.0); + else + dbout = DB_POS(3.0); + + return DB2LIN_TABLE[dbout + slot->egout]; +} + +/* + HI-HAT +*/ +INLINE static e_int32 +calc_slot_hat (OPLL_SLOT *slot, e_int32 pgout_cym, e_uint32 noise) +{ + e_uint32 dbout; + + if (slot->egout >= (DB_MUTE - 1)) + return 0; + else if( + /* the same as fmopl.c */ + ((BIT(slot->pgout,PG_BITS-8)^BIT(slot->pgout,PG_BITS-1))|BIT(slot->pgout,PG_BITS-7)) ^ + /* different from fmopl.c */ + (BIT(pgout_cym,PG_BITS-7)&!BIT(pgout_cym,PG_BITS-5)) + ) + { + if(noise) + dbout = DB_NEG(12.0); + else + dbout = DB_NEG(24.0); + } + else + { + if(noise) + dbout = DB_POS(12.0); + else + dbout = DB_POS(24.0); + } + + return DB2LIN_TABLE[dbout + slot->egout]; +} + +static e_int16 +calc (OPLL * opll) +{ + e_int32 inst = 0, perc = 0, out = 0; + e_int32 i; + + update_ampm (opll); + update_noise (opll); + + for (i = 0; i < 18; i++) + { + calc_phase(&opll->slot[i],opll->lfo_pm); + calc_envelope(&opll->slot[i],opll->lfo_am); + } + + for (i = 0; i < 6; i++) + if (!(opll->mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH)) + inst += calc_slot_car (CAR(opll,i), calc_slot_mod(MOD(opll,i))); + + if (! opll->vrc7_mode) + { + /* CH6 */ + if (opll->patch_number[6] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH (6)) && (CAR(opll,6)->eg_mode != FINISH)) + inst += calc_slot_car (CAR(opll,6), calc_slot_mod(MOD(opll,6))); + } + else + { + if (!(opll->mask & OPLL_MASK_BD) && (CAR(opll,6)->eg_mode != FINISH)) + perc += calc_slot_car (CAR(opll,6), calc_slot_mod(MOD(opll,6))); + } + + /* CH7 */ + if (opll->patch_number[7] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH (7)) && (CAR(opll,7)->eg_mode != FINISH)) + inst += calc_slot_car (CAR(opll,7), calc_slot_mod(MOD(opll,7))); + } + else + { + if (!(opll->mask & OPLL_MASK_HH) && (MOD(opll,7)->eg_mode != FINISH)) + perc += calc_slot_hat (MOD(opll,7), CAR(opll,8)->pgout, opll->noise_seed&1); + if (!(opll->mask & OPLL_MASK_SD) && (CAR(opll,7)->eg_mode != FINISH)) + perc -= calc_slot_snare (CAR(opll,7), opll->noise_seed&1); + } + + /* CH8 */ + if (opll->patch_number[8] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH(8)) && (CAR(opll,8)->eg_mode != FINISH)) + inst += calc_slot_car (CAR(opll,8), calc_slot_mod (MOD(opll,8))); + } + else + { + if (!(opll->mask & OPLL_MASK_TOM) && (MOD(opll,8)->eg_mode != FINISH)) + perc += calc_slot_tom (MOD(opll,8)); + if (!(opll->mask & OPLL_MASK_CYM) && (CAR(opll,8)->eg_mode != FINISH)) + perc -= calc_slot_cym (CAR(opll,8), MOD(opll,7)->pgout); + } + } // end if (! opll->vrc7_mode) + + out = inst + (perc << 1); + return (e_int16) out; +} + +#ifdef EMU2413_COMPACTION +e_int16 +OPLL_calc (OPLL * opll) +{ + return calc (opll); +} +#else +e_int16 +OPLL_calc (OPLL * opll) +{ + if (!opll->quality) + return calc (opll); + + while (opll->realstep > opll->oplltime) + { + opll->oplltime += opll->opllstep; + opll->prev = opll->next; + opll->next = calc (opll); + } + + opll->oplltime -= opll->realstep; + opll->out = (e_int16) (((double) opll->next * (opll->opllstep - opll->oplltime) + + (double) opll->prev * opll->oplltime) / opll->opllstep); + + return (e_int16) opll->out; +} +#endif + +/*e_uint32 +OPLL_setMask (OPLL * opll, e_uint32 mask) +{ + e_uint32 ret; + + if (opll) + { + ret = opll->mask; + opll->mask = mask; + return ret; + } + else + return 0; +}*/ + +void OPLL_SetMuteMask(OPLL* opll, e_uint32 MuteMask) +{ + unsigned char CurChn; + e_uint32 ChnMsk; + + for (CurChn = 0; CurChn < 14; CurChn ++) + { + if (CurChn < 9) + { + ChnMsk = OPLL_MASK_CH(CurChn); + } + else + { + switch(CurChn) + { + case 9: + ChnMsk = OPLL_MASK_BD; + break; + case 10: + ChnMsk = OPLL_MASK_SD; + break; + case 11: + ChnMsk = OPLL_MASK_TOM; + break; + case 12: + ChnMsk = OPLL_MASK_CYM; + break; + case 13: + ChnMsk = OPLL_MASK_HH; + break; + default: + ChnMsk = 0; + break; + } + } + if ((MuteMask >> CurChn) & 0x01) + opll->mask |= ChnMsk; + else + opll->mask &= ~ChnMsk; + } + + return; +} + +/*e_uint32 +OPLL_toggleMask (OPLL * opll, e_uint32 mask) +{ + e_uint32 ret; + + if (opll) + { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } + else + return 0; +}*/ + +void OPLL_SetChipMode(OPLL* opll, e_uint8 Mode) +{ + // Enable/Disable VRC7 Mode (with only 6 instead of 9 channels and no rhythm part) + opll->vrc7_mode = Mode; + + return; +} + +/**************************************************** + + I/O Ctrl + +*****************************************************/ + +void +OPLL_writeReg (OPLL * opll, e_uint32 reg, e_uint32 data) +{ + e_int32 i, v, ch; + + data = data & 0xff; + reg = reg & 0x3f; + opll->reg[reg] = (e_uint8) data; + + switch (reg) + { + case 0x00: + opll->patch[0].AM = (data >> 7) & 1; + opll->patch[0].PM = (data >> 6) & 1; + opll->patch[0].EG = (data >> 5) & 1; + opll->patch[0].KR = (data >> 4) & 1; + opll->patch[0].ML = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_PG (MOD(opll,i)); + UPDATE_RKS (MOD(opll,i)); + UPDATE_EG (MOD(opll,i)); + } + } + break; + + case 0x01: + opll->patch[1].AM = (data >> 7) & 1; + opll->patch[1].PM = (data >> 6) & 1; + opll->patch[1].EG = (data >> 5) & 1; + opll->patch[1].KR = (data >> 4) & 1; + opll->patch[1].ML = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_PG (CAR(opll,i)); + UPDATE_RKS (CAR(opll,i)); + UPDATE_EG (CAR(opll,i)); + } + } + break; + + case 0x02: + opll->patch[0].KL = (data >> 6) & 3; + opll->patch[0].TL = (data) & 63; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_TLL(MOD(opll,i)); + } + } + break; + + case 0x03: + opll->patch[1].KL = (data >> 6) & 3; + opll->patch[1].WF = (data >> 4) & 1; + opll->patch[0].WF = (data >> 3) & 1; + opll->patch[0].FB = (data) & 7; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_WF(MOD(opll,i)); + UPDATE_WF(CAR(opll,i)); + } + } + break; + + case 0x04: + opll->patch[0].AR = (data >> 4) & 15; + opll->patch[0].DR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG (MOD(opll,i)); + } + } + break; + + case 0x05: + opll->patch[1].AR = (data >> 4) & 15; + opll->patch[1].DR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG(CAR(opll,i)); + } + } + break; + + case 0x06: + opll->patch[0].SL = (data >> 4) & 15; + opll->patch[0].RR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG (MOD(opll,i)); + } + } + break; + + case 0x07: + opll->patch[1].SL = (data >> 4) & 15; + opll->patch[1].RR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll->patch_number[i] == 0) + { + UPDATE_EG (CAR(opll,i)); + } + } + break; + + case 0x0e: + if (opll->vrc7_mode) + break; + update_rhythm_mode (opll); + if (data & 0x20) + { + if (data & 0x10) + keyOn_BD (opll); + else + keyOff_BD (opll); + if (data & 0x8) + keyOn_SD (opll); + else + keyOff_SD (opll); + if (data & 0x4) + keyOn_TOM (opll); + else + keyOff_TOM (opll); + if (data & 0x2) + keyOn_CYM (opll); + else + keyOff_CYM (opll); + if (data & 0x1) + keyOn_HH (opll); + else + keyOff_HH (opll); + } + update_key_status (opll); + + UPDATE_ALL (MOD(opll,6)); + UPDATE_ALL (CAR(opll,6)); + UPDATE_ALL (MOD(opll,7)); + UPDATE_ALL (CAR(opll,7)); + UPDATE_ALL (MOD(opll,8)); + UPDATE_ALL (CAR(opll,8)); + + break; + + case 0x0f: + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + ch = reg - 0x10; + if (opll->vrc7_mode && ch >= 6) + break; + setFnumber (opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); + UPDATE_ALL (MOD(opll,ch)); + UPDATE_ALL (CAR(opll,ch)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + ch = reg - 0x20; + if (opll->vrc7_mode && ch >= 6) + break; + setFnumber (opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); + setBlock (opll, ch, (data >> 1) & 7); + setSustine (opll, ch, (data >> 5) & 1); + if (ch < 0x06 || ! (opll->reg[0x0E] & 0x20)) + { + // Valley Bell Fix: prevent commands 0x26-0x28 from turning + // the drums (BD, SD, CYM) off + if (data & 0x10) + keyOn (opll, ch); + else + keyOff (opll, ch); + } + UPDATE_ALL (MOD(opll,ch)); + UPDATE_ALL (CAR(opll,ch)); + update_key_status (opll); + update_rhythm_mode (opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + if (opll->vrc7_mode && reg >= 0x36) + break; + i = (data >> 4) & 15; + v = data & 15; + if ((opll->reg[0x0e] & 0x20) && (reg >= 0x36)) + { + switch (reg) + { + case 0x37: + setSlotVolume (MOD(opll,7), i << 2); + break; + case 0x38: + setSlotVolume (MOD(opll,8), i << 2); + break; + default: + break; + } + } + else + { + setPatch (opll, reg - 0x30, i); + } + setVolume (opll, reg - 0x30, v << 2); + UPDATE_ALL (MOD(opll,reg - 0x30)); + UPDATE_ALL (CAR(opll,reg - 0x30)); + break; + + default: + break; + + } +} + +void +OPLL_writeIO (OPLL * opll, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + OPLL_writeReg (opll, opll->adr, val); + else + opll->adr = val; +} + +#ifndef EMU2413_COMPACTION +/* STEREO MODE (OPT) */ +void +OPLL_set_pan (OPLL * opll, e_uint32 ch, e_int32 pan) +{ + e_uint32 fnl_ch; + + if (ch >= 14) + return; + + if (ch < 9) + fnl_ch = ch; + else + fnl_ch = 13 - (ch - 9); + calc_panning( opll->pan[fnl_ch], pan ); // Maxim +} + +static void +calc_stereo (OPLL * opll, e_int32 out[2], e_int32 ch) +{ + /* Maxim: added stereo control (multiply each side by a float in opll->pan[ch][side]) */ + e_int32 l=0,r=0; +/* e_int32 b[4] = { 0, 0, 0, 0 }; /* Ignore, Right, Left, Center */ +/* e_int32 r[4] = { 0, 0, 0, 0 }; /* Ignore, Right, Left, Center */ + e_int32 i; + e_int32 channel; + + if (ch < 0) + { + update_ampm (opll); + update_noise (opll); + + for(i=0;i<18;i++) + { + calc_phase(&opll->slot[i],opll->lfo_pm); + calc_envelope(&opll->slot[i],opll->lfo_am); + } + } + + for (i = 0; i < 6; i++) + { + if (ch >= 0 && i != ch) continue; + if (!(opll->mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH)) + { + channel = calc_slot_car (CAR(opll,i), calc_slot_mod (MOD(opll,i))); + if ( opll->pan[i][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[i][0] ); + r += (e_int32)( channel * opll->pan[i][1] ); + } + } + } + + + if (! opll->vrc7_mode) + { + if (ch < 0 || ch == 6) + { + if (opll->patch_number[6] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH (6)) && (CAR(opll,6)->eg_mode != FINISH)) + { + channel = calc_slot_car (CAR(opll,6), calc_slot_mod (MOD(opll,6))); + if ( opll->pan[6][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[6][0] ); + r += (e_int32)( channel * opll->pan[6][1] ); + } + } + } + else + { + if (!(opll->mask & OPLL_MASK_BD) && (CAR(opll,6)->eg_mode != FINISH)) + { + channel = calc_slot_car (CAR(opll,6), calc_slot_mod (MOD(opll,6))) << 1; + if ( opll->pan[9][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[9][0] ); + r += (e_int32)( channel * opll->pan[9][1] ); + } + } + } + } + + if (ch < 0 || ch == 7) + { + if (opll->patch_number[7] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH (7)) && (CAR (opll,7)->eg_mode != FINISH)) + { + channel = calc_slot_car (CAR (opll,7), calc_slot_mod (MOD (opll,7))); + if ( opll->pan[7][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[7][0] ); + r += (e_int32)( channel * opll->pan[7][1] ); + } + } + } + else + { + if (!(opll->mask & OPLL_MASK_HH) && (MOD (opll,7)->eg_mode != FINISH)) + { + channel = calc_slot_hat (MOD (opll,7), CAR(opll,8)->pgout, opll->noise_seed&1) << 1; + if ( opll->pan[10][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[10][0] ); + r += (e_int32)( channel * opll->pan[10][1] ); + } + } + if (!(opll->mask & OPLL_MASK_SD) && (CAR (opll,7)->eg_mode != FINISH)) + { + channel = -(calc_slot_snare (CAR (opll,7), opll->noise_seed&1) << 1); // this one is negated + if ( opll->pan[11][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[11][0] ); + r += (e_int32)( channel * opll->pan[11][1] ); + } + } + } + } + + if (ch < 0 || ch == 8) + { + if (opll->patch_number[8] <= 15) + { + if (!(opll->mask & OPLL_MASK_CH (8)) && (CAR (opll,8)->eg_mode != FINISH)) + { + channel = calc_slot_car (CAR (opll,8), calc_slot_mod (MOD (opll,8))); + if ( opll->pan[8][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[8][0] ); + r += (e_int32)( channel * opll->pan[8][1] ); + } + } + } + else + { + if (!(opll->mask & OPLL_MASK_TOM) && (MOD (opll,8)->eg_mode != FINISH)) + { + channel = calc_slot_tom (MOD (opll,8)) << 1; + if ( opll->pan[12][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[12][0] ); + r += (e_int32)( channel * opll->pan[12][1] ); + } + } + if (!(opll->mask & OPLL_MASK_CYM) && (CAR (opll,8)->eg_mode != FINISH)) + { + channel = -(calc_slot_cym (CAR (opll,8), MOD(opll,7)->pgout) << 1); // negated + if ( opll->pan[13][0] == 1.0f ) + { + l += channel; + r += channel; + } + else + { + l += (e_int32)( channel * opll->pan[13][0] ); + r += (e_int32)( channel * opll->pan[13][1] ); + } + } + } + } + } // end if (! opll->vrc7_mode) +/* + out[1] = (b[1] + b[3] + ((r[1] + r[3]) << 1)) <<3; + out[0] = (b[2] + b[3] + ((r[2] + r[3]) << 1)) <<3; + */ + out[0] = l << 3; + out[1] = r << 3; +} + +/*void +OPLL_calc_stereo (OPLL * opll, e_int32 out[2], samples) +{ + if (!opll->quality) + { + calc_stereo (opll, out); + return; + } + + while (opll->realstep > opll->oplltime) + { + opll->oplltime += opll->opllstep; + opll->sprev[0] = opll->snext[0]; + opll->sprev[1] = opll->snext[1]; + calc_stereo (opll, opll->snext); + } + + opll->oplltime -= opll->realstep; + out[0] = (e_int16) (((double) opll->snext[0] * (opll->opllstep - opll->oplltime) + + (double) opll->sprev[0] * opll->oplltime) / opll->opllstep); + out[1] = (e_int16) (((double) opll->snext[1] * (opll->opllstep - opll->oplltime) + + (double) opll->sprev[1] * opll->oplltime) / opll->opllstep); +}*/ +void +OPLL_advance (OPLL * opll) +{ + e_int32 buffers[2]; + calc_stereo (opll, buffers, -1); +} + +void +OPLL_calc_stereo (OPLL * opll, e_int32 **out, e_int32 samples, e_int32 ch) +{ + e_int32 *bufMO = out[0]; + e_int32 *bufRO = out[1]; + e_int32 buffers[2]; + + int i; + + for( i=0; i < samples ; i++ ) + { + if (!opll->quality) + { + calc_stereo (opll, buffers, ch); + bufMO[i] = buffers[0]; + bufRO[i] = buffers[1]; + } + else + { + while (opll->realstep > opll->oplltime) + { + opll->oplltime += opll->opllstep; + opll->sprev[0] = opll->snext[0]; + opll->sprev[1] = opll->snext[1]; + calc_stereo (opll, opll->snext, ch); + } + + opll->oplltime -= opll->realstep; + bufMO[i] = (e_int32) (((double) opll->snext[0] * (opll->opllstep - opll->oplltime) + + (double) opll->sprev[0] * opll->oplltime) / opll->opllstep); + bufRO[i] = (e_int32) (((double) opll->snext[1] * (opll->opllstep - opll->oplltime) + + (double) opll->sprev[1] * opll->oplltime) / opll->opllstep); + } + } +} +#endif /* EMU2413_COMPACTION */ diff --git a/Frameworks/GME/vgmplay/chips/emu2413.h b/Frameworks/GME/vgmplay/chips/emu2413.h new file mode 100644 index 000000000..79625d6b4 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/emu2413.h @@ -0,0 +1,151 @@ +#ifndef _EMU2413_H_ +#define _EMU2413_H_ + +#include "emutypes.h" + +#ifdef EMU2413_DLL_EXPORTS + #define EMU2413_API __declspec(dllexport) +#elif defined(EMU2413_DLL_IMPORTS) + #define EMU2413_API __declspec(dllimport) +#else + #define EMU2413_API +#endif + +#define PI 3.14159265358979323846 + +enum OPLL_TONE_ENUM {OPLL_2413_TONE=0, OPLL_VRC7_TONE=1, OPLL_281B_TONE=2} ; + +/* voice data */ +typedef struct __OPLL_PATCH { + e_uint32 TL,FB,EG,ML,AR,DR,SL,RR,KR,KL,AM,PM,WF ; +} OPLL_PATCH ; + +/* slot */ +typedef struct __OPLL_SLOT { + + OPLL_PATCH *patch; + + e_int32 type ; /* 0 : modulator 1 : carrier */ + + /* OUTPUT */ + e_int32 feedback ; + e_int32 output[2] ; /* Output value of slot */ + + /* for Phase Generator (PG) */ + e_uint16 *sintbl ; /* Wavetable */ + e_uint32 phase ; /* Phase */ + e_uint32 dphase ; /* Phase increment amount */ + e_uint32 pgout ; /* output */ + + /* for Envelope Generator (EG) */ + e_int32 fnum ; /* F-Number */ + e_int32 block ; /* Block */ + e_int32 volume ; /* Current volume */ + e_int32 sustine ; /* Sustine 1 = ON, 0 = OFF */ + e_uint32 tll ; /* Total Level + Key scale level*/ + e_uint32 rks ; /* Key scale offset (Rks) */ + e_int32 eg_mode ; /* Current state */ + e_uint32 eg_phase ; /* Phase */ + e_uint32 eg_dphase ; /* Phase increment amount */ + e_uint32 egout ; /* output */ + +} OPLL_SLOT ; + +/* Mask */ +#define OPLL_MASK_CH(x) (1<<(x)) +#define OPLL_MASK_HH (1<<(9)) +#define OPLL_MASK_CYM (1<<(10)) +#define OPLL_MASK_TOM (1<<(11)) +#define OPLL_MASK_SD (1<<(12)) +#define OPLL_MASK_BD (1<<(13)) +#define OPLL_MASK_RHYTHM ( OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD ) + +/* opll */ +typedef struct __OPLL { + + e_uint8 vrc7_mode; + e_uint8 adr ; + //e_uint32 adr ; + e_int32 out ; + +#ifndef EMU2413_COMPACTION + e_uint32 realstep ; + e_uint32 oplltime ; + e_uint32 opllstep ; + e_int32 prev, next ; + e_int32 sprev[2],snext[2]; + float pan[14][2]; +#endif + + /* Register */ + e_uint8 reg[0x40] ; + e_int32 slot_on_flag[18] ; + + /* Pitch Modulator */ + e_uint32 pm_phase ; + e_int32 lfo_pm ; + + /* Amp Modulator */ + e_int32 am_phase ; + e_int32 lfo_am ; + + e_uint32 quality; + + /* Noise Generator */ + e_uint32 noise_seed ; + + /* Channel Data */ + e_int32 patch_number[9]; + e_int32 key_status[9] ; + + /* Slot */ + OPLL_SLOT slot[18] ; + + /* Voice Data */ + OPLL_PATCH patch[19*2] ; + e_int32 patch_update[2] ; /* flag for check patch update */ + + e_uint32 mask ; + +} OPLL ; + +/* Create Object */ +EMU2413_API OPLL *OPLL_new(e_uint32 clk, e_uint32 rate) ; +EMU2413_API void OPLL_delete(OPLL *) ; + +/* Setup */ +EMU2413_API void OPLL_reset(OPLL *) ; +EMU2413_API void OPLL_reset_patch(OPLL *, e_int32) ; +EMU2413_API void OPLL_set_rate(OPLL *opll, e_uint32 r) ; +EMU2413_API void OPLL_set_quality(OPLL *opll, e_uint32 q) ; +EMU2413_API void OPLL_set_pan(OPLL *, e_uint32 ch, e_int32 pan); + +/* Port/Register access */ +EMU2413_API void OPLL_writeIO(OPLL *, e_uint32 reg, e_uint32 val) ; +EMU2413_API void OPLL_writeReg(OPLL *, e_uint32 reg, e_uint32 val) ; + +/* Advance engine */ +EMU2413_API void OPLL_advance (OPLL *); + +/* Synthsize */ +EMU2413_API e_int16 OPLL_calc(OPLL *) ; +EMU2413_API void OPLL_calc_stereo(OPLL *, e_int32 **out, e_int32 samples, e_int32 ch) ; /* ch = -1 for normal operation */ + +/* Misc */ +EMU2413_API void OPLL_setPatch(OPLL *, const e_uint8 *dump) ; +EMU2413_API void OPLL_copyPatch(OPLL *, e_int32, OPLL_PATCH *) ; +EMU2413_API void OPLL_forceRefresh(OPLL *) ; +/* Utility */ +EMU2413_API void OPLL_dump2patch(const e_uint8 *dump, OPLL_PATCH *patch) ; +EMU2413_API void OPLL_patch2dump(const OPLL_PATCH *patch, e_uint8 *dump) ; +EMU2413_API void OPLL_getDefaultPatch(e_int32 type, e_int32 num, OPLL_PATCH *) ; + +/* Channel Mask */ +//EMU2413_API e_uint32 OPLL_setMask(OPLL *, e_uint32 mask) ; +//EMU2413_API e_uint32 OPLL_toggleMask(OPLL *, e_uint32 mask) ; +void OPLL_SetMuteMask(OPLL* opll, e_uint32 MuteMask); +void OPLL_SetChipMode(OPLL* opll, e_uint8 Mode); + +#define dump2patch OPLL_dump2patch + +#endif diff --git a/Frameworks/GME/vgmplay/chips/emu2413_NESpatches.txt b/Frameworks/GME/vgmplay/chips/emu2413_NESpatches.txt new file mode 100644 index 000000000..6ed3cc6c2 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/emu2413_NESpatches.txt @@ -0,0 +1 @@ +http://forums.nesdev.com/viewtopic.php?f=6&t=9141 diff --git a/Frameworks/GME/vgmplay/chips/emutypes.h b/Frameworks/GME/vgmplay/chips/emutypes.h new file mode 100644 index 000000000..6ae272157 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/emutypes.h @@ -0,0 +1,31 @@ +#ifndef _EMUTYPES_H_ +#define _EMUTYPES_H_ + +#ifndef INLINE + +#if defined(_MSC_VER) +//#define INLINE __forceinline +#define INLINE __inline +#elif defined(__GNUC__) +#define INLINE __inline__ +#elif defined(_MWERKS_) +#define INLINE inline +#else +#define INLINE +#endif + +#endif + +typedef unsigned int e_uint; +typedef signed int e_int; + +typedef unsigned char e_uint8 ; +typedef signed char e_int8 ; + +typedef unsigned short e_uint16 ; +typedef signed short e_int16 ; + +typedef unsigned int e_uint32 ; +typedef signed int e_int32 ; + +#endif diff --git a/Frameworks/GME/vgmplay/chips/es5503.c b/Frameworks/GME/vgmplay/chips/es5503.c new file mode 100644 index 000000000..894ea7ef1 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/es5503.c @@ -0,0 +1,624 @@ +/* + + ES5503 - Ensoniq ES5503 "DOC" emulator v2.1.1 + By R. Belmont. + + Copyright R. Belmont. + + This software is dual-licensed: it may be used in MAME and properly licensed + MAME derivatives under the terms of the MAME license. For use outside of + MAME and properly licensed derivatives, it is available under the + terms of the GNU Lesser General Public License (LGPL), version 2.1. + You may read the LGPL at http://www.gnu.org/licenses/lgpl.html + + History: the ES5503 was the next design after the famous C64 "SID" by Bob Yannes. + It powered the legendary Mirage sampler (the first affordable pro sampler) as well + as the ESQ-1 synth/sequencer. The ES5505 (used in Taito's F3 System) and 5506 + (used in the "Soundscape" series of ISA PC sound cards) followed on a fundamentally + similar architecture. + + Bugs: On the real silicon, oscillators 30 and 31 have random volume fluctuations and are + unusable for playback. We don't attempt to emulate that. :-) + + Additionally, in "swap" mode, there's one cycle when the switch takes place where the + oscillator's output is 0x80 (centerline) regardless of the sample data. This can + cause audible clicks and a general degradation of audio quality if the correct sample + data at that point isn't 0x80 or very near it. + + Changes: + 0.2 (RB) - improved behavior for volumes > 127, fixes missing notes in Nucleus & missing voices in Thexder + 0.3 (RB) - fixed extraneous clicking, improved timing behavior for e.g. Music Construction Set & Music Studio + 0.4 (RB) - major fixes to IRQ semantics and end-of-sample handling. + 0.5 (RB) - more flexible wave memory hookup (incl. banking) and save state support. + 1.0 (RB) - properly respects the input clock + 2.0 (RB) - C++ conversion, more accurate oscillator IRQ timing + 2.1 (RB) - Corrected phase when looping; synthLAB, Arkanoid, and Arkanoid II no longer go out of tune + 2.1.1 (RB) - Fixed issue introduced in 2.0 where IRQs were delayed +*/ + +//#include "emu.h" +//#include "streams.h" +#include +#include +#include "mamedef.h" +#include "es5503.h" + +typedef struct +{ + UINT16 freq; + UINT16 wtsize; + UINT8 control; + UINT8 vol; + UINT8 data; + UINT32 wavetblpointer; + UINT8 wavetblsize; + UINT8 resolution; + + UINT32 accumulator; + UINT8 irqpend; + + UINT8 Muted; +} ES5503Osc; + +typedef struct +{ + ES5503Osc oscillators[32]; + + UINT32 dramsize; + UINT8 *docram; + + //sound_stream * stream; + + //void (*irq_callback)(running_device *, int); // IRQ callback + + //read8_device_func adc_read; // callback for the 5503's built-in analog to digital converter + + INT8 oscsenabled; // # of oscillators enabled + int rege0; // contents of register 0xe0 + + UINT8 channel_strobe; + + UINT32 clock; + int output_channels; + int outchn_mask; + UINT32 output_rate; + //running_device *device; + + SRATE_CALLBACK SmpRateFunc; + void* SmpRateData; +} ES5503Chip; + +/*INLINE ES5503Chip *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == ES5503); + return (ES5503Chip *)downcast(device)->token(); +}*/ + +static const UINT16 wavesizes[8] = { 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; +static const UINT32 wavemasks[8] = { 0x1ff00, 0x1fe00, 0x1fc00, 0x1f800, 0x1f000, 0x1e000, 0x1c000, 0x18000 }; +static const UINT32 accmasks[8] = { 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff }; +static const int resshifts[8] = { 9, 10, 11, 12, 13, 14, 15, 16 }; + +enum +{ + MODE_FREE = 0, + MODE_ONESHOT = 1, + MODE_SYNCAM = 2, + MODE_SWAP = 3 +}; + +// halt_osc: handle halting an oscillator +// chip = chip ptr +// onum = oscillator # +// type = 1 for 0 found in sample data, 0 for hit end of table size +static void es5503_halt_osc(ES5503Chip *chip, int onum, int type, UINT32 *accumulator, int resshift) +{ + ES5503Osc *pOsc = &chip->oscillators[onum]; + ES5503Osc *pPartner = &chip->oscillators[onum^1]; + int mode = (pOsc->control>>1) & 3; + int omode = (pPartner->control>>1) & 3; + + // if 0 found in sample data or mode is not free-run, halt this oscillator + if ((mode != MODE_FREE) || (type != 0)) + { + pOsc->control |= 1; + } + else // preserve the relative phase of the oscillator when looping + { + UINT16 wtsize = pOsc->wtsize - 1; + UINT32 altram = (*accumulator) >> resshift; + + if (altram > wtsize) + { + altram -= wtsize; + } + else + { + altram = 0; + } + + *accumulator = altram << resshift; + } + + // if swap mode, start the partner + // Note: The swap mode fix breaks Silpheed and other games. + if ((mode == MODE_SWAP) /*|| (omode == MODE_SWAP)*/) + { + pPartner->control &= ~1; // clear the halt bit + pPartner->accumulator = 0; // and make sure it starts from the top (does this also need phase preservation?) + } + + // IRQ enabled for this voice? + if (pOsc->control & 0x08) + { + pOsc->irqpend = 1; + + /*if (chip->irq_callback) + { + chip->irq_callback(chip->device, 1); + }*/ + } +} + +//static STREAM_UPDATE( es5503_pcm_update ) +void es5503_pcm_update(void *param, stream_sample_t **outputs, int samples) +{ + // Note: The advantage of NOT using this buffer is not only less RAM usage, + // but also a huge speedup. This is, because the array is not marked + // as 'static' and thus re-allocated for every single call. + //INT32 mix[48000*2]; + //INT32 *mixp; + int osc, snum; + UINT32 ramptr; + ES5503Chip *chip = (ES5503Chip *)param; + int chnsStereo, chan; + + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + //memset(mix, 0, sizeof(mix)); + + chnsStereo = chip->output_channels & ~1; + for (osc = 0; osc < chip->oscsenabled; osc++) + { + ES5503Osc *pOsc = &chip->oscillators[osc]; + + if (!(pOsc->control & 1) && ! pOsc->Muted) + { + UINT32 wtptr = pOsc->wavetblpointer & wavemasks[pOsc->wavetblsize], altram; + UINT32 acc = pOsc->accumulator; + UINT16 wtsize = pOsc->wtsize - 1; + //UINT8 ctrl = pOsc->control; + UINT16 freq = pOsc->freq; + INT16 vol = pOsc->vol; + //INT8 data = -128; + UINT8 chnMask = (pOsc->control >> 4) & 0x0F; + int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize; + UINT32 sizemask = accmasks[pOsc->wavetblsize]; + INT32 outData; + //mixp = &mix[0] + chan; + + chnMask &= chip->outchn_mask; + for (snum = 0; snum < samples; snum++) + { + altram = acc >> resshift; + ramptr = altram & sizemask; + + acc += freq; + + // channel strobe is always valid when reading; this allows potentially banking per voice + //chip->channel_strobe = (pOsc->control>>4) & 0xf; + //data = (INT32)chip->docram[ramptr + wtptr] ^ 0x80; + pOsc->data = chip->docram[ramptr + wtptr]; + + if (pOsc->data == 0x00) + { + es5503_halt_osc(chip, osc, 1, &acc, resshift); + } + else + { + outData = (pOsc->data - 0x80) * vol; + //*mixp += outData; + //mixp += output_channels; + + // send groups of 2 channels to L or R + for (chan = 0; chan < chnsStereo; chan ++) + { + if (chan == chnMask) + outputs[chan & 1][snum] += outData; + } + outData = (outData * 181) >> 8; // outData *= sqrt(2) + // send remaining channels to L+R + for (; chan < chip->output_channels; chan ++) + { + if (chan == chnMask) + { + outputs[0][snum] += outData; + outputs[1][snum] += outData; + } + } + + if (altram >= wtsize) + { + es5503_halt_osc(chip, osc, 0, &acc, resshift); + } + } + + // if oscillator halted, we've got no more samples to generate + if (pOsc->control & 1) + { + //pOsc->control |= 1; + break; + } + } + + //pOsc->control = ctrl; + pOsc->accumulator = acc; + //pOsc->data = data ^ 0x80; + } + } + +/* mixp = &mix[0]; + for (i = 0; i < samples; i++) + for (int chan = 0; chan < output_channels; chan++) + outputs[chan][i] = (*mixp++)>>1;*/ +} + + +//static DEVICE_START( es5503 ) +int device_start_es5503(void **_info, int clock, int channels) +{ + //const es5503_interface *intf; + int osc; + //ES5503Chip *chip = get_safe_token(device); + ES5503Chip *chip; + + chip = (ES5503Chip *) calloc(1, sizeof(ES5503Chip)); + *_info = (void *) chip; + + //intf = (const es5503_interface *)device->baseconfig().static_config(); + + //chip->irq_callback = intf->irq_callback; + //chip->adc_read = intf->adc_read; + //chip->docram = intf->wave_memory; + chip->dramsize = 0x20000; // 128 KB + chip->docram = (UINT8*)malloc(chip->dramsize); + //chip->clock = device->clock(); + //chip->device = device; + chip->clock = clock; + + chip->output_channels = channels; + chip->outchn_mask = 1; + while(chip->outchn_mask < chip->output_channels) + chip->outchn_mask <<= 1; + chip->outchn_mask --; + chip->rege0 = 0xff; + + /*for (osc = 0; osc < 32; osc++) + { + state_save_register_device_item(device, osc, chip->oscillators[osc].freq); + state_save_register_device_item(device, osc, chip->oscillators[osc].wtsize); + state_save_register_device_item(device, osc, chip->oscillators[osc].control); + state_save_register_device_item(device, osc, chip->oscillators[osc].vol); + state_save_register_device_item(device, osc, chip->oscillators[osc].data); + state_save_register_device_item(device, osc, chip->oscillators[osc].wavetblpointer); + state_save_register_device_item(device, osc, chip->oscillators[osc].wavetblsize); + state_save_register_device_item(device, osc, chip->oscillators[osc].resolution); + state_save_register_device_item(device, osc, chip->oscillators[osc].accumulator); + state_save_register_device_item(device, osc, chip->oscillators[osc].irqpend); + }*/ + + //chip->output_rate = (device->clock()/8)/34; // (input clock / 8) / # of oscs. enabled + 2 + //chip->stream = stream_create(device, 0, 2, chip->output_rate, chip, es5503_pcm_update); + chip->output_rate = (chip->clock/8)/34; // (input clock / 8) / # of oscs. enabled + 2 + + for (osc = 0; osc < 32; osc ++) + chip->oscillators[osc].Muted = 0x00; + + return chip->output_rate; +} + +void device_stop_es5503(void *_info) +{ + ES5503Chip *chip = (ES5503Chip *)_info; + + free(chip->docram); chip->docram = NULL; + + free(chip); + + return; +} + +void device_reset_es5503(void *_info) +{ + ES5503Chip *chip = (ES5503Chip *)_info; + int osc; + ES5503Osc* tempOsc; + + for (osc = 0; osc < 32; osc++) + { + tempOsc = &chip->oscillators[osc]; + tempOsc->freq = 0; + tempOsc->wtsize = 0; + tempOsc->control = 0; + tempOsc->vol = 0; + tempOsc->data = 0x80; + tempOsc->wavetblpointer = 0; + tempOsc->wavetblsize = 0; + tempOsc->resolution = 0; + tempOsc->accumulator = 0; + tempOsc->irqpend = 0; + } + + chip->oscsenabled = 1; + + chip->channel_strobe = 0; + memset(chip->docram, 0x00, chip->dramsize); + + chip->output_rate = (chip->clock/8)/(2+chip->oscsenabled); // (input clock / 8) / # of oscs. enabled + 2 + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->output_rate); + + return; +} + + +//READ8_DEVICE_HANDLER( es5503_r ) +UINT8 es5503_r(void *_info, offs_t offset) +{ + UINT8 retval; + int i; + //ES5503Chip *chip = get_safe_token(device); + ES5503Chip *chip = (ES5503Chip *)_info; + + //stream_update(chip->stream); + + if (offset < 0xe0) + { + int osc = offset & 0x1f; + + switch(offset & 0xe0) + { + case 0: // freq lo + return (chip->oscillators[osc].freq & 0xff); + + case 0x20: // freq hi + return (chip->oscillators[osc].freq >> 8); + + case 0x40: // volume + return chip->oscillators[osc].vol; + + case 0x60: // data + return chip->oscillators[osc].data; + + case 0x80: // wavetable pointer + return (chip->oscillators[osc].wavetblpointer>>8) & 0xff; + + case 0xa0: // oscillator control + return chip->oscillators[osc].control; + + case 0xc0: // bank select / wavetable size / resolution + retval = 0; + if (chip->oscillators[osc].wavetblpointer & 0x10000) + { + retval |= 0x40; + } + + retval |= (chip->oscillators[osc].wavetblsize<<3); + retval |= chip->oscillators[osc].resolution; + return retval; + } + } + else // global registers + { + switch (offset) + { + case 0xe0: // interrupt status + retval = chip->rege0; + + //m_irq_func(0); + + // scan all oscillators + for (i = 0; i < chip->oscsenabled; i++) + { + if (chip->oscillators[i].irqpend) + { + // signal this oscillator has an interrupt + retval = i<<1; + + chip->rege0 = retval | 0x80; + + // and clear its flag + chip->oscillators[i].irqpend = 0; + + /*if (chip->irq_callback) + { + chip->irq_callback(chip->device, 0); + }*/ + break; + } + } + + // if any oscillators still need to be serviced, assert IRQ again immediately + /*for (i = 0; i < chip->oscsenabled; i++) + { + if (chip->oscillators[i].irqpend) + { + if (chip->irq_callback) + { + chip->irq_callback(chip->device, 1); + } + break; + } + }*/ + + return retval; + + case 0xe1: // oscillator enable + return (chip->oscsenabled-1)<<1; + + case 0xe2: // A/D converter + /*if (chip->adc_read) + { + return chip->adc_read(chip->device, 0); + }*/ + break; + } + } + + return 0; +} + +//WRITE8_DEVICE_HANDLER( es5503_w ) +void es5503_w(void *_info, offs_t offset, UINT8 data) +{ + //ES5503Chip *chip = get_safe_token(device); + ES5503Chip *chip = (ES5503Chip *)_info; + + //stream_update(chip->stream); + + if (offset < 0xe0) + { + int osc = offset & 0x1f; + + switch(offset & 0xe0) + { + case 0: // freq lo + chip->oscillators[osc].freq &= 0xff00; + chip->oscillators[osc].freq |= data; + break; + + case 0x20: // freq hi + chip->oscillators[osc].freq &= 0x00ff; + chip->oscillators[osc].freq |= (data<<8); + break; + + case 0x40: // volume + chip->oscillators[osc].vol = data; + break; + + case 0x60: // data - ignore writes + break; + + case 0x80: // wavetable pointer + chip->oscillators[osc].wavetblpointer = (data<<8); + break; + + case 0xa0: // oscillator control + // if a fresh key-on, reset the ccumulator + if ((chip->oscillators[osc].control & 1) && (!(data&1))) + { + chip->oscillators[osc].accumulator = 0; + } + + chip->oscillators[osc].control = data; + break; + + case 0xc0: // bank select / wavetable size / resolution + if (data & 0x40) // bank select - not used on the Apple IIgs + { + chip->oscillators[osc].wavetblpointer |= 0x10000; + } + else + { + chip->oscillators[osc].wavetblpointer &= 0xffff; + } + + chip->oscillators[osc].wavetblsize = ((data>>3) & 7); + chip->oscillators[osc].wtsize = wavesizes[chip->oscillators[osc].wavetblsize]; + chip->oscillators[osc].resolution = (data & 7); + break; + } + } + else // global registers + { + switch (offset) + { + case 0xe0: // interrupt status + break; + + case 0xe1: // oscillator enable + chip->oscsenabled = 1 + ((data>>1) & 0x1f); + + chip->output_rate = (chip->clock/8)/(2+chip->oscsenabled); + //stream_set_sample_rate(chip->stream, chip->output_rate); + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->output_rate); + break; + + case 0xe2: // A/D converter + break; + } + } +} + +/*void es5503_set_base(running_device *device, UINT8 *wavemem) +{ + ES5503Chip *chip = get_safe_token(device); + + chip->docram = wavemem; +}*/ + +void es5503_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) +{ + ES5503Chip *chip = (ES5503Chip *)_info; + + if (DataStart >= chip->dramsize) + return; + if (DataStart + DataLength > chip->dramsize) + DataLength = chip->dramsize - DataStart; + + memcpy(chip->docram + DataStart, RAMData, DataLength); + + return; +} + +void es5503_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ES5503Chip *chip = (ES5503Chip *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 32; CurChn ++) + chip->oscillators[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + +void es5503_set_srchg_cb(void *_info, SRATE_CALLBACK CallbackFunc, void* DataPtr) +{ + ES5503Chip *chip = (ES5503Chip *)_info; + + // set Sample Rate Change Callback routine + chip->SmpRateFunc = CallbackFunc; + chip->SmpRateData = DataPtr; + + return; +} + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( es5503 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ES5503Chip); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( es5503 ); break; + case DEVINFO_FCT_STOP: // Nothing // break; + case DEVINFO_FCT_RESET: // Nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "ES5503"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Ensoniq ES550x"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright R. Belmont"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(ES5503, es5503);*/ diff --git a/Frameworks/GME/vgmplay/chips/es5503.h b/Frameworks/GME/vgmplay/chips/es5503.h new file mode 100644 index 000000000..5f6e9ad1b --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/es5503.h @@ -0,0 +1,34 @@ +#pragma once + +#ifndef __ES5503_H__ +#define __ES5503_H__ + +///#include "devlegcy.h" + +/*typedef struct _es5503_interface es5503_interface; +struct _es5503_interface +{ + void (*irq_callback)(running_device *device, int state); + read8_device_func adc_read; + UINT8 *wave_memory; +};*/ + +//READ8_DEVICE_HANDLER( es5503_r ); +//WRITE8_DEVICE_HANDLER( es5503_w ); +void es5503_w(void *chip, offs_t offset, UINT8 data); +UINT8 es5503_r(void *chip, offs_t offset); + +void es5503_pcm_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_es5503(void **chip, int clock, int channels); +void device_stop_es5503(void *chip); +void device_reset_es5503(void *chip); +//void es5503_set_base(running_device *device, UINT8 *wavemem); + +void es5503_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); + +void es5503_set_mute_mask(void *chip, UINT32 MuteMask); +void es5503_set_srchg_cb(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr); + +//DECLARE_LEGACY_SOUND_DEVICE(ES5503, es5503); + +#endif /* __ES5503_H__ */ diff --git a/Frameworks/GME/vgmplay/chips/es5506.c b/Frameworks/GME/vgmplay/chips/es5506.c new file mode 100644 index 000000000..c4d430af7 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/es5506.c @@ -0,0 +1,2429 @@ +/********************************************************************************************** + + Ensoniq ES5505/6 driver + by Aaron Giles + +Ensoniq OTIS - ES5505 Ensoniq OTTO - ES5506 + + OTIS is a VLSI device designed in a 2 micron double metal OTTO is a VLSI device designed in a 1.5 micron double metal + CMOS process. The device is the next generation of audio CMOS process. The device is the next generation of audio + technology from ENSONIQ. This new chip achieves a new technology from ENSONIQ. All calculations in the device are + level of audio fidelity performance. These improvements made with at least 18-bit accuracy. + are achieved through the use of frequency interpolation + and on board real time digital filters. All calculations The major features of OTTO are: + in the device are made with at least 16 bit accuracy. - 68 pin PLCC package + - On chip real time digital filters + The major features of OTIS are: - Frequency interpolation + - 48 Pin dual in line package - 32 independent voices + - On chip real time digital filters - Loop start and stop posistions for each voice + - Frequency interpolation - Bidirectional and reverse looping + - 32 independent voices (up from 25 in DOCII) - 68000 compatibility for asynchronous bus communication + - Loop start and stop positions for each voice - separate host and sound memory interface + - Bidirectional and reverse looping - 6 channel stereo serial communication port + - 68000 compatibility for asynchronous bus communication - Programmable clocks for defining serial protocol + - On board pulse width modulation D to A - Internal volume multiplication and stereo panning + - 4 channel stereo serial communication port - A to D input for pots and wheels + - Internal volume multiplication and stereo panning - Hardware support for envelopes + - A to D input for pots and wheels - Support for dual OTTO systems + - Up to 10MHz operation - Optional compressed data format for sample data + - Up to 16MHz operation + ______ ______ + _|o \__/ |_ + A17/D13 - |_|1 48|_| - VSS A A A A A A + _| |_ 2 1 1 1 1 1 A + A18/D14 - |_|2 47|_| - A16/D12 0 9 8 7 6 5 1 + _| |_ / / / / / / 4 + A19/D15 - |_|3 46|_| - A15/D11 H H H H H H H V V H D D D D D D / + _| |_ D D D D D D D S D D 1 1 1 1 1 1 D + BS - |_|4 45|_| - A14/D10 0 1 2 3 4 5 6 S D 7 5 4 3 2 1 0 9 + _| |_ ------------------------------------+ + PWZERO - |_|5 44|_| - A13/D9 / 9 8 7 6 5 4 3 2 1 6 6 6 6 6 6 6 6 | + _| |_ / 8 7 6 5 4 3 2 1 | + SER0 - |_|6 43|_| - A12/D8 | | + _| E |_ SER0|10 60|A13/D8 + SER1 - |_|7 N 42|_| - A11/D7 SER1|11 59|A12/D7 + _| S |_ SER2|12 58|A11/D6 + SER2 - |_|8 O 41|_| - A10/D6 SER3|13 ENSONIQ 57|A10/D5 + _| N |_ SER4|14 56|A9/D4 + SER3 - |_|9 I 40|_| - A9/D5 SER5|15 55|A8/D3 + _| Q |_ WCLK|16 54|A7/D2 + SERWCLK - |_|10 39|_| - A8/D4 LRCLK|17 ES5506 53|A6/D1 + _| |_ BCLK|18 52|A5/D0 + SERLR - |_|11 38|_| - A7/D3 RESB|19 51|A4 + _| |_ HA5|20 50|A3 + SERBCLK - |_|12 E 37|_| - A6/D2 HA4|21 OTTO 49|A2 + _| S |_ HA3|22 48|A1 + RLO - |_|13 5 36|_| - A5/D1 HA2|23 47|A0 + _| 5 |_ HA1|24 46|BS1 + RHI - |_|14 0 35|_| - A4/D0 HA0|25 45|BS0 + _| 5 |_ POT_IN|26 44|DTACKB + LLO - |_|15 34|_| - CLKIN | 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 | + _| |_ | 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 | + LHI - |_|16 33|_| - CAS +--------------------------------------+ + _| |_ B E E B E B B D S B B B E K B W W + POT - |_|17 O 32|_| - AMUX S B L N L S S D S S X S L Q / / + _| T |_ E E R E H M C V V A U A C R R R + DTACK - |_|18 I 31|_| - RAS R R D H R M C I M + _| S |_ _ D A + R/W - |_|19 30|_| - E T + _| |_ O + MS - |_|20 29|_| - IRQ P + _| |_ + CS - |_|21 28|_| - A3 + _| |_ + RES - |_|22 27|_| - A2 + _| |_ + VSS - |_|23 26|_| - A1 + _| |_ + VDD - |_|24 25|_| - A0 + |________________| + +***********************************************************************************************/ + +//#include "emu.h" +#include +#include // for memset +#include "mamedef.h" +#include "es5506.h" + + + +/********************************************************************************************** + + CONSTANTS + +***********************************************************************************************/ + +#define LOG_COMMANDS 0 +#define RAINE_CHECK 0 +#define MAKE_WAVS 0 + +#if MAKE_WAVS +#include "wavwrite.h" +#endif + +#define ACCESSING_BITS_0_7 ( byteOfs) +#define ACCESSING_BITS_8_15 (! byteOfs) + + +#define MAX_SAMPLE_CHUNK 10000 +#define ULAW_MAXBITS 8 + +#define CONTROL_BS1 0x8000 +#define CONTROL_BS0 0x4000 +#define CONTROL_CMPD 0x2000 +#define CONTROL_CA2 0x1000 +#define CONTROL_CA1 0x0800 +#define CONTROL_CA0 0x0400 +#define CONTROL_LP4 0x0200 +#define CONTROL_LP3 0x0100 +#define CONTROL_IRQ 0x0080 +#define CONTROL_DIR 0x0040 +#define CONTROL_IRQE 0x0020 +#define CONTROL_BLE 0x0010 +#define CONTROL_LPE 0x0008 +#define CONTROL_LEI 0x0004 +#define CONTROL_STOP1 0x0002 +#define CONTROL_STOP0 0x0001 + +#define CONTROL_BSMASK (CONTROL_BS1 | CONTROL_BS0) +#define CONTROL_CAMASK (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0) +#define CONTROL_LPMASK (CONTROL_LP4 | CONTROL_LP3) +#define CONTROL_LOOPMASK (CONTROL_BLE | CONTROL_LPE) +#define CONTROL_STOPMASK (CONTROL_STOP1 | CONTROL_STOP0) + + + +/********************************************************************************************** + + INTERNAL DATA STRUCTURES + +***********************************************************************************************/ + +/* struct describing a single playing voice */ +typedef struct _es5506_voice es5506_voice; +struct _es5506_voice +{ + /* external state */ + UINT32 control; /* control register */ + UINT32 freqcount; /* frequency count register */ + UINT32 start; /* start register */ + UINT32 lvol; /* left volume register */ + UINT32 end; /* end register */ + UINT32 lvramp; /* left volume ramp register */ + UINT32 accum; /* accumulator register */ + UINT32 rvol; /* right volume register */ + UINT32 rvramp; /* right volume ramp register */ + UINT32 ecount; /* envelope count register */ + UINT32 k2; /* k2 register */ + UINT32 k2ramp; /* k2 ramp register */ + UINT32 k1; /* k1 register */ + UINT32 k1ramp; /* k1 ramp register */ + INT32 o4n1; /* filter storage O4(n-1) */ + INT32 o3n1; /* filter storage O3(n-1) */ + INT32 o3n2; /* filter storage O3(n-2) */ + INT32 o2n1; /* filter storage O2(n-1) */ + INT32 o2n2; /* filter storage O2(n-2) */ + INT32 o1n1; /* filter storage O1(n-1) */ + UINT32 exbank; /* external address bank */ + + /* internal state */ + UINT8 index; /* index of this voice */ + UINT8 filtcount; /* filter count */ + UINT8 Muted; + UINT32 accum_mask; +}; + +typedef struct _es5506_state es5506_state; +struct _es5506_state +{ + //sound_stream *stream; /* which stream are we using */ + int sample_rate; /* current sample rate */ + UINT32 region_size[4]; + UINT16 * region_base[4]; /* pointer to the base of the region */ + UINT32 write_latch; /* currently accumulated data for write */ + UINT32 read_latch; /* currently accumulated data for read */ + UINT32 master_clock; /* master clock frequency */ + //void (*irq_callback)(device_t *, int); /* IRQ callback */ + //UINT16 (*port_read)(void); /* input port read */ + + UINT8 current_page; /* current register page */ + UINT8 active_voices; /* number of active voices */ + UINT8 mode; /* MODE register */ + UINT8 wst; /* W_ST register */ + UINT8 wend; /* W_END register */ + UINT8 lrend; /* LR_END register */ + UINT8 irqv; /* IRQV register */ + + es5506_voice voice[32]; /* the 32 voices */ + + INT32 * scratch; + + INT16 * ulaw_lookup; + UINT16 * volume_lookup; + //device_t *device; + +#if MAKE_WAVS + void * wavraw; /* raw waveform */ +#endif + int channels; /* number of output channels: 1 .. 6 */ + UINT8 sndtype; /* 0 - ES5505, 1 - ES5506 */ + + SRATE_CALLBACK SmpRateFunc; + void* SmpRateData; +}; + + +/*INLINE es5506_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == ES5505 || device->type() == ES5506); + return (es5506_state *)downcast(device)->token(); +}*/ + + + +/********************************************************************************************** + + GLOBAL VARIABLES + +***********************************************************************************************/ + +//static FILE *eslog; + + + +/********************************************************************************************** + + update_irq_state -- update the IRQ state + +***********************************************************************************************/ + +static void update_irq_state(es5506_state *chip) +{ + /* ES5505/6 irq line has been set high - inform the host */ + //if (chip->irq_callback) + // (*chip->irq_callback)(chip->device, 1); /* IRQB set high */ +} + +static void update_internal_irq_state(es5506_state *chip) +{ + /* Host (cpu) has just read the voice interrupt vector (voice IRQ ack). + + Reset the voice vector to show the IRQB line is low (top bit set). + If we have any stacked interrupts (other voices waiting to be + processed - with their IRQ bit set) then they will be moved into + the vector next time the voice is processed. In emulation + terms they get updated next time generate_samples() is called. + */ + + chip->irqv=0x80; + + //if (chip->irq_callback) + // (*chip->irq_callback)(chip->device, 0); /* IRQB set low */ +} + +/********************************************************************************************** + + compute_tables -- compute static tables + +***********************************************************************************************/ + +static void compute_tables(es5506_state *chip) +{ + int i; + + /* allocate ulaw lookup table */ + //chip->ulaw_lookup = auto_alloc_array(chip->device->machine(), INT16, 1 << ULAW_MAXBITS); + chip->ulaw_lookup = (INT16*)malloc((1 << ULAW_MAXBITS) * sizeof(INT16)); + + /* generate ulaw lookup table */ + for (i = 0; i < (1 << ULAW_MAXBITS); i++) + { + UINT16 rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS)); + UINT8 exponent = rawval >> 13; + UINT32 mantissa = (rawval << 3) & 0xffff; + + if (exponent == 0) + chip->ulaw_lookup[i] = (INT16)mantissa >> 7; + else + { + mantissa = (mantissa >> 1) | (~mantissa & 0x8000); + chip->ulaw_lookup[i] = (INT16)mantissa >> (7 - exponent); + } + } + + /* allocate volume lookup table */ + //chip->volume_lookup = auto_alloc_array(chip->device->machine(), UINT16, 4096); + chip->volume_lookup = (UINT16*)malloc(4096 * sizeof(UINT16)); + + /* generate volume lookup table */ + for (i = 0; i < 4096; i++) + { + UINT8 exponent = i >> 8; + UINT32 mantissa = (i & 0xff) | 0x100; + + chip->volume_lookup[i] = (mantissa << 11) >> (20 - exponent); + } +} + + + +/********************************************************************************************** + + interpolate -- interpolate between two samples + +***********************************************************************************************/ + +#define interpolate(sample1, sample2, accum) \ + (sample1 * (INT32)(0x800 - (accum & 0x7ff)) + \ + sample2 * (INT32)(accum & 0x7ff)) >> 11; + + + +/********************************************************************************************** + + apply_filters -- apply the 4-pole digital filter to the sample + +***********************************************************************************************/ + +#define apply_filters(voice, sample) \ +do \ +{ \ + /* pole 1 is always low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o1n1) / 16384) + voice->o1n1; \ + voice->o1n1 = sample; \ + \ + /* pole 2 is always low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o2n1) / 16384) + voice->o2n1; \ + voice->o2n2 = voice->o2n1; \ + voice->o2n1 = sample; \ + \ + /* remaining poles depend on the current filter setting */ \ + switch (voice->control & CONTROL_LPMASK) \ + { \ + case 0: \ + /* pole 3 is high-pass using K2 */ \ + sample = sample - voice->o2n2 + ((INT32)(voice->k2 >> 2) * voice->o3n1) / 32768 + voice->o3n1 / 2; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is high-pass using K2 */ \ + sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ + voice->o4n1 = sample; \ + break; \ + \ + case CONTROL_LP3: \ + /* pole 3 is low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is high-pass using K2 */ \ + sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ + voice->o4n1 = sample; \ + break; \ + \ + case CONTROL_LP4: \ + /* pole 3 is low-pass using K2 */ \ + sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is low-pass using K2 */ \ + sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ + voice->o4n1 = sample; \ + break; \ + \ + case CONTROL_LP4 | CONTROL_LP3: \ + /* pole 3 is low-pass using K1 */ \ + sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ + voice->o3n2 = voice->o3n1; \ + voice->o3n1 = sample; \ + \ + /* pole 4 is low-pass using K2 */ \ + sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ + voice->o4n1 = sample; \ + break; \ + } \ +} while (0) + + + +/********************************************************************************************** + + update_envelopes -- update the envelopes + +***********************************************************************************************/ + +#define update_envelopes(voice, samples) \ +do \ +{ \ + int count = (samples > 1 && samples > voice->ecount) ? voice->ecount : samples; \ + \ + /* decrement the envelope counter */ \ + voice->ecount -= count; \ + \ + /* ramp left volume */ \ + if (voice->lvramp) \ + { \ + voice->lvol += (INT8)voice->lvramp * count; \ + if ((INT32)voice->lvol < 0) voice->lvol = 0; \ + else if (voice->lvol > 0xffff) voice->lvol = 0xffff; \ + } \ + \ + /* ramp right volume */ \ + if (voice->rvramp) \ + { \ + voice->rvol += (INT8)voice->rvramp * count; \ + if ((INT32)voice->rvol < 0) voice->rvol = 0; \ + else if (voice->rvol > 0xffff) voice->rvol = 0xffff; \ + } \ + \ + /* ramp k1 filter constant */ \ + if (voice->k1ramp && ((INT32)voice->k1ramp >= 0 || !(voice->filtcount & 7))) \ + { \ + voice->k1 += (INT8)voice->k1ramp * count; \ + if ((INT32)voice->k1 < 0) voice->k1 = 0; \ + else if (voice->k1 > 0xffff) voice->k1 = 0xffff; \ + } \ + \ + /* ramp k2 filter constant */ \ + if (voice->k2ramp && ((INT32)voice->k2ramp >= 0 || !(voice->filtcount & 7))) \ + { \ + voice->k2 += (INT8)voice->k2ramp * count; \ + if ((INT32)voice->k2 < 0) voice->k2 = 0; \ + else if (voice->k2 > 0xffff) voice->k2 = 0xffff; \ + } \ + \ + /* update the filter constant counter */ \ + voice->filtcount += count; \ + \ +} while (0) + + + +/********************************************************************************************** + + check_for_end_forward + check_for_end_reverse -- check for loop end and loop appropriately + +***********************************************************************************************/ + +#define check_for_end_forward(voice, accum) \ +do \ +{ \ + /* are we past the end? */ \ + if (accum > voice->end && !(voice->control & CONTROL_LEI)) \ + { \ + /* generate interrupt if required */ \ + if (voice->control&CONTROL_IRQE) \ + voice->control |= CONTROL_IRQ; \ + \ + /* handle the different types of looping */ \ + switch (voice->control & CONTROL_LOOPMASK) \ + { \ + /* non-looping */ \ + case 0: \ + voice->control |= CONTROL_STOP0; \ + goto alldone; \ + \ + /* uni-directional looping */ \ + case CONTROL_LPE: \ + accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ + break; \ + \ + /* trans-wave looping */ \ + case CONTROL_BLE: \ + accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ + voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ + break; \ + \ + /* bi-directional looping */ \ + case CONTROL_LPE | CONTROL_BLE: \ + accum = (voice->end - (accum - voice->end)) & voice->accum_mask; \ + voice->control ^= CONTROL_DIR; \ + goto reverse; \ + } \ + } \ +} while (0) + + +#define check_for_end_reverse(voice, accum) \ +do \ +{ \ + /* are we past the end? */ \ + if (accum < voice->start && !(voice->control & CONTROL_LEI)) \ + { \ + /* generate interrupt if required */ \ + if (voice->control&CONTROL_IRQE) \ + voice->control |= CONTROL_IRQ; \ + \ + /* handle the different types of looping */ \ + switch (voice->control & CONTROL_LOOPMASK) \ + { \ + /* non-looping */ \ + case 0: \ + voice->control |= CONTROL_STOP0; \ + goto alldone; \ + \ + /* uni-directional looping */ \ + case CONTROL_LPE: \ + accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ + break; \ + \ + /* trans-wave looping */ \ + case CONTROL_BLE: \ + accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ + voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ + break; \ + \ + /* bi-directional looping */ \ + case CONTROL_LPE | CONTROL_BLE: \ + accum = (voice->start + (voice->start - accum)) & voice->accum_mask;\ + voice->control ^= CONTROL_DIR; \ + goto reverse; \ + } \ + } \ +} while (0) + + + +/********************************************************************************************** + + generate_dummy -- generate nothing, just apply envelopes + +***********************************************************************************************/ + +static void generate_dummy(es5506_state *chip, es5506_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) +{ + UINT32 freqcount = voice->freqcount; + UINT32 accum = voice->accum & voice->accum_mask; + + /* outer loop, in case we switch directions */ + while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + { +reverse: + /* two cases: first case is forward direction */ + if (!(voice->control & CONTROL_DIR)) + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + accum = (accum + freqcount) & voice->accum_mask; + + /* update filters/volumes */ + if (voice->ecount != 0) + update_envelopes(voice, 1); + + /* check for loop end */ + check_for_end_forward(voice, accum); + } + } + + /* two cases: second case is backward direction */ + else + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + accum = (accum - freqcount) & voice->accum_mask; + + /* update filters/volumes */ + if (voice->ecount != 0) + update_envelopes(voice, 1); + + /* check for loop end */ + check_for_end_reverse(voice, accum); + } + } + } + + /* if we stopped, process any additional envelope */ +alldone: + voice->accum = accum; + if (samples > 0) + update_envelopes(voice, samples); +} + + + +/********************************************************************************************** + + generate_ulaw -- general u-law decoding routine + +***********************************************************************************************/ + +static void generate_ulaw(es5506_state *chip, es5506_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) +{ + UINT32 freqcount = voice->freqcount; + UINT32 accum = voice->accum & voice->accum_mask; + INT32 lvol = chip->volume_lookup[voice->lvol >> 4]; + INT32 rvol = chip->volume_lookup[voice->rvol >> 4]; + + /* pre-add the bank offset */ + base += voice->exbank; + + /* outer loop, in case we switch directions */ + while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + { +reverse: + /* two cases: first case is forward direction */ + if (!(voice->control & CONTROL_DIR)) + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = base[accum >> 11]; + INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* decompress u-law */ + val1 = chip->ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; + val2 = chip->ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum + freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = chip->volume_lookup[voice->lvol >> 4]; + rvol = chip->volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_forward(voice, accum); + } + } + + /* two cases: second case is backward direction */ + else + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = base[accum >> 11]; + INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* decompress u-law */ + val1 = chip->ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; + val2 = chip->ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum - freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = chip->volume_lookup[voice->lvol >> 4]; + rvol = chip->volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_reverse(voice, accum); + } + } + } + + /* if we stopped, process any additional envelope */ +alldone: + voice->accum = accum; + if (samples > 0) + update_envelopes(voice, samples); +} + + + +/********************************************************************************************** + + generate_pcm -- general PCM decoding routine + +***********************************************************************************************/ + +static void generate_pcm(es5506_state *chip, es5506_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples) +{ + UINT32 freqcount = voice->freqcount; + UINT32 accum = voice->accum & voice->accum_mask; + INT32 lvol = chip->volume_lookup[voice->lvol >> 4]; + INT32 rvol = chip->volume_lookup[voice->rvol >> 4]; + + /* pre-add the bank offset */ + base += voice->exbank; + + /* outer loop, in case we switch directions */ + while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + { +reverse: + /* two cases: first case is forward direction */ + if (!(voice->control & CONTROL_DIR)) + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = (INT16)base[accum >> 11]; + INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum + freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = chip->volume_lookup[voice->lvol >> 4]; + rvol = chip->volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_forward(voice, accum); + } + } + + /* two cases: second case is backward direction */ + else + { + /* loop while we still have samples to generate */ + while (samples--) + { + /* fetch two samples */ + INT32 val1 = (INT16)base[accum >> 11]; + INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum - freqcount) & voice->accum_mask; + + /* apply filters */ + apply_filters(voice, val1); + + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice, 1); + lvol = chip->volume_lookup[voice->lvol >> 4]; + rvol = chip->volume_lookup[voice->rvol >> 4]; + } + + /* apply volumes and add */ + *lbuffer++ += (val1 * lvol) >> 11; + *rbuffer++ += (val1 * rvol) >> 11; + + /* check for loop end */ + check_for_end_reverse(voice, accum); + } + } + } + + /* if we stopped, process any additional envelope */ +alldone: + voice->accum = accum; + if (samples > 0) + update_envelopes(voice, samples); +} + + + +/********************************************************************************************** + + generate_samples -- tell each voice to generate samples + +***********************************************************************************************/ + +static void generate_samples(es5506_state *chip, INT32 **outputs, int offset, int samples) +{ + int v; + int i; + int voice_channel; + int channel; + int l; + int r; + INT32 *left; + INT32 *right; + + /* skip if nothing to do */ + if (!samples) + return; + + /* clear out the accumulator */ + for (i = 0; i < chip->channels << 1; i++) + { + memset(outputs[i] + offset, 0, sizeof(INT32) * samples); + } + + /* loop over voices */ + for (v = 0; v <= chip->active_voices; v++) + { + es5506_voice *voice = &chip->voice[v]; + UINT16 *base = chip->region_base[voice->control >> 14]; + + /* special case: if end == start, stop the voice */ + if (voice->start == voice->end) + voice->control |= CONTROL_STOP0; + + voice_channel = (voice->control & CONTROL_CAMASK) >> 10; + channel = voice_channel % chip->channels; + l = channel << 1; + r = l + 1; + left = outputs[l] + offset; + right = outputs[r] + offset; + + /* generate from the appropriate source */ + if (!base) + { + //logerror("es5506: NULL region base %d\n",voice->control >> 14); + generate_dummy(chip, voice, base, left, right, samples); + } + else if (voice->control & 0x2000) + generate_ulaw(chip, voice, base, left, right, samples); + else + generate_pcm(chip, voice, base, left, right, samples); + + /* does this voice have it's IRQ bit raised? */ + if (voice->control&CONTROL_IRQ) + { + logerror("es5506: IRQ raised on voice %d!!\n",v); + /* only update voice vector if existing IRQ is acked by host */ + if (chip->irqv&0x80) + { + /* latch voice number into vector, and set high bit low */ + chip->irqv=v&0x7f; + + /* take down IRQ bit on voice */ + voice->control&=~CONTROL_IRQ; + + /* inform host of irq */ + update_irq_state(chip); + } + } + } +} + + + +/********************************************************************************************** + + es5506_update -- update the sound chip so that it is in sync with CPU execution + +***********************************************************************************************/ + +//static STREAM_UPDATE( es5506_update ) +void es5506_update(void *param, stream_sample_t **outputs, int samples) +{ + es5506_state *chip = (es5506_state *)param; + int offset; + +#if MAKE_WAVS + /* start the logging once we have a sample rate */ + if (chip->sample_rate) + { + if (!chip->wavraw) + chip->wavraw = wav_open("raw.wav", chip->sample_rate, 2); + } +#endif + + /* loop until all samples are output */ + offset = 0; + while (samples) + { + int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; + + generate_samples(chip, outputs, offset, length); + + /* account for these samples */ + offset += length; + samples -= length; + } +} + + +/********************************************************************************************** + + DEVICE_START( es5506 ) -- start emulation of the ES5506 + +***********************************************************************************************/ + +//static void es5506_start_common(device_t *device, const void *config, device_type sndtype) +static void es5506_start_common(es5506_state *chip, int clock, UINT8 sndtype) +{ + //const es5506_interface *intf = (const es5506_interface *)config; + //es5506_state *chip = get_safe_token(device); +// int j; +// UINT32 accum_mask; + int max_chns; + + chip->sndtype = sndtype; + /* only override the number of channels if the value is in the valid range 1 .. 6 */ + max_chns = chip->sndtype ? 6 : 4; // 6 for ES5506, 4 for ES5505 + if (chip->channels < 1|| chip->channels > max_chns) + chip->channels = 1; /* 1 channel by default, for backward compatibility */ + + /* debugging */ + //if (LOG_COMMANDS && !eslog) + // eslog = fopen("es.log", "w"); + + /* create the stream */ +// chip->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock() / (16*32), chip, es5506_update); + + /* initialize the regions */ +// chip->region_base[0] = intf->region0 ? (UINT16 *)device->machine().region(intf->region0)->base() : NULL; +// chip->region_base[1] = intf->region1 ? (UINT16 *)device->machine().region(intf->region1)->base() : NULL; +// chip->region_base[2] = intf->region2 ? (UINT16 *)device->machine().region(intf->region2)->base() : NULL; +// chip->region_base[3] = intf->region3 ? (UINT16 *)device->machine().region(intf->region3)->base() : NULL; + + /* initialize the rest of the structure */ +// chip->device = device; +// chip->master_clock = device->clock(); +// chip->irq_callback = intf->irq_callback; + chip->master_clock = clock; + chip->irqv = 0x80; + + if (chip->sndtype) + { + /* KT-76 assumes all voices are active on an ES5506 without setting them! */ + chip->active_voices = 31; + chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); + //chip->stream->set_sample_rate(m_sample_rate); + } + else + { + // ES5505 + chip->sample_rate = chip->master_clock / (16*32); + } + + /* compute the tables */ + compute_tables(chip); + + /* init the voices */ + //accum_mask = (chip->sndtype == ES5506) ? 0xffffffff : 0x7fffffff; + // now done in device_reset + + /* allocate memory */ + //chip->scratch = auto_alloc_array(device->machine(), INT32, 2 * MAX_SAMPLE_CHUNK); + chip->scratch = (INT32*)malloc(2 * MAX_SAMPLE_CHUNK * sizeof(INT32)); + + /* register save */ + /*device->save_item(NAME(chip->sample_rate)); + device->save_item(NAME(chip->write_latch)); + device->save_item(NAME(chip->read_latch)); + + device->save_item(NAME(chip->current_page)); + device->save_item(NAME(chip->active_voices)); + device->save_item(NAME(chip->mode)); + device->save_item(NAME(chip->wst)); + device->save_item(NAME(chip->wend)); + device->save_item(NAME(chip->lrend)); + device->save_item(NAME(chip->irqv)); + + device->save_pointer(NAME(chip->scratch), 2 * MAX_SAMPLE_CHUNK); + + for (j = 0; j < 32; j++) + { + device->save_item(NAME(chip->voice[j].control), j); + device->save_item(NAME(chip->voice[j].freqcount), j); + device->save_item(NAME(chip->voice[j].start), j); + device->save_item(NAME(chip->voice[j].lvol), j); + device->save_item(NAME(chip->voice[j].end), j); + device->save_item(NAME(chip->voice[j].lvramp), j); + device->save_item(NAME(chip->voice[j].accum), j); + device->save_item(NAME(chip->voice[j].rvol), j); + device->save_item(NAME(chip->voice[j].rvramp), j); + device->save_item(NAME(chip->voice[j].ecount), j); + device->save_item(NAME(chip->voice[j].k2), j); + device->save_item(NAME(chip->voice[j].k2ramp), j); + device->save_item(NAME(chip->voice[j].k1), j); + device->save_item(NAME(chip->voice[j].k1ramp), j); + device->save_item(NAME(chip->voice[j].o4n1), j); + device->save_item(NAME(chip->voice[j].o3n1), j); + device->save_item(NAME(chip->voice[j].o3n2), j); + device->save_item(NAME(chip->voice[j].o2n1), j); + device->save_item(NAME(chip->voice[j].o2n2), j); + device->save_item(NAME(chip->voice[j].o1n1), j); + device->save_item(NAME(chip->voice[j].exbank), j); + device->save_item(NAME(chip->voice[j].filtcount), j); + }*/ + + /* success */ +} + + +//static DEVICE_START( es5506 ) +int device_start_es5506(void **_info, int clock, int channels) +{ + es5506_state* chip; + + chip = (es5506_state *) calloc(1, sizeof(es5506_state)); + *_info = (void *) chip; + + //es5506_start_common(device, device->static_config(), ES5506); + //chip->channels = channels; + chip->channels = 1; // fixed to L+R for now + es5506_start_common(chip, clock & 0x7FFFFFFF, clock >> 31); + return chip->master_clock / (16*32); +} + + + +/********************************************************************************************** + + DEVICE_STOP( es5506 ) -- stop emulation of the ES5506 + +***********************************************************************************************/ + +//static DEVICE_STOP( es5506 ) +void device_stop_es5506(void *_info) +{ + es5506_state *chip = (es5506_state *)_info; + + free(chip->ulaw_lookup); chip->ulaw_lookup = NULL; + free(chip->volume_lookup); chip->volume_lookup = NULL; + free(chip->scratch); chip->scratch = NULL; + + /* debugging */ + /*if (LOG_COMMANDS && eslog) + { + fclose(eslog); + eslog = NULL; + }*/ + +#if MAKE_WAVS +{ + int i; + + for (i = 0; i < MAX_ES5506; i++) + { + if (es5506[i].wavraw) + wav_close(es5506[i].wavraw); + } +} +#endif + + free(chip); +} + + +//static DEVICE_RESET( es5506 ) +void device_reset_es5506(void *_info) +{ + es5506_state *chip = (es5506_state *)_info; + int j; + UINT32 accum_mask; + + accum_mask = chip->sndtype ? 0xffffffff : 0x7fffffff; + for (j = 0; j < 32; j++) + { + chip->voice[j].index = j; + chip->voice[j].control = CONTROL_STOPMASK; + chip->voice[j].lvol = 0xffff; + chip->voice[j].rvol = 0xffff; + chip->voice[j].exbank = 0; + chip->voice[j].accum_mask = accum_mask; + } +} + + + +/********************************************************************************************** + + es5506_reg_write -- handle a write to the selected ES5506 register + +***********************************************************************************************/ + +INLINE void es5506_reg_write_low(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT32 data) +{ + switch (offset) + { + case 0x00/8: /* CR */ + voice->control = data & 0xffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, control=%04x\n", chip->current_page & 0x1f, voice->control); + break; + + case 0x08/8: /* FC */ + voice->freqcount = data & 0x1ffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, freq count=%08x\n", chip->current_page & 0x1f, voice->freqcount); + break; + + case 0x10/8: /* LVOL */ + voice->lvol = data & 0xffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, left vol=%04x\n", chip->current_page & 0x1f, voice->lvol); + break; + + case 0x18/8: /* LVRAMP */ + voice->lvramp = (data & 0xff00) >> 8; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, left vol ramp=%04x\n", chip->current_page & 0x1f, voice->lvramp); + break; + + case 0x20/8: /* RVOL */ + voice->rvol = data & 0xffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, right vol=%04x\n", chip->current_page & 0x1f, voice->rvol); + break; + + case 0x28/8: /* RVRAMP */ + voice->rvramp = (data & 0xff00) >> 8; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, right vol ramp=%04x\n", chip->current_page & 0x1f, voice->rvramp); + break; + + case 0x30/8: /* ECOUNT */ + voice->ecount = data & 0x1ff; + voice->filtcount = 0; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, envelope count=%04x\n", chip->current_page & 0x1f, voice->ecount); + break; + + case 0x38/8: /* K2 */ + voice->k2 = data & 0xffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, K2=%04x\n", chip->current_page & 0x1f, voice->k2); + break; + + case 0x40/8: /* K2RAMP */ + voice->k2ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, K2 ramp=%04x\n", chip->current_page & 0x1f, voice->k2ramp); + break; + + case 0x48/8: /* K1 */ + voice->k1 = data & 0xffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, K1=%04x\n", chip->current_page & 0x1f, voice->k1); + break; + + case 0x50/8: /* K1RAMP */ + voice->k1ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, K1 ramp=%04x\n", chip->current_page & 0x1f, voice->k1ramp); + break; + + case 0x58/8: /* ACTV */ + { + chip->active_voices = data & 0x1f; + chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); + //chip->stream->set_sample_rate(chip->sample_rate); + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); + break; + } + + case 0x60/8: /* MODE */ + chip->mode = data & 0x1f; + break; + + case 0x68/8: /* PAR - read only */ + case 0x70/8: /* IRQV - read only */ + break; + + case 0x78/8: /* PAGE */ + chip->current_page = data & 0x7f; + break; + } +} + + +INLINE void es5506_reg_write_high(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT32 data) +{ + switch (offset) + { + case 0x00/8: /* CR */ + voice->control = data & 0xffff; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, control=%04x\n", chip->current_page & 0x1f, voice->control); + break; + + case 0x08/8: /* START */ + voice->start = data & 0xfffff800; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, loop start=%08x\n", chip->current_page & 0x1f, voice->start); + break; + + case 0x10/8: /* END */ + voice->end = data & 0xffffff80; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, loop end=%08x\n", chip->current_page & 0x1f, voice->end); + break; + + case 0x18/8: /* ACCUM */ + voice->accum = data; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, accum=%08x\n", chip->current_page & 0x1f, voice->accum); + break; + + case 0x20/8: /* O4(n-1) */ + voice->o4n1 = (INT32)(data << 14) >> 14; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, O4(n-1)=%05x\n", chip->current_page & 0x1f, voice->o4n1 & 0x3ffff); + break; + + case 0x28/8: /* O3(n-1) */ + voice->o3n1 = (INT32)(data << 14) >> 14; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, O3(n-1)=%05x\n", chip->current_page & 0x1f, voice->o3n1 & 0x3ffff); + break; + + case 0x30/8: /* O3(n-2) */ + voice->o3n2 = (INT32)(data << 14) >> 14; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, O3(n-2)=%05x\n", chip->current_page & 0x1f, voice->o3n2 & 0x3ffff); + break; + + case 0x38/8: /* O2(n-1) */ + voice->o2n1 = (INT32)(data << 14) >> 14; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, O2(n-1)=%05x\n", chip->current_page & 0x1f, voice->o2n1 & 0x3ffff); + break; + + case 0x40/8: /* O2(n-2) */ + voice->o2n2 = (INT32)(data << 14) >> 14; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, O2(n-2)=%05x\n", chip->current_page & 0x1f, voice->o2n2 & 0x3ffff); + break; + + case 0x48/8: /* O1(n-1) */ + voice->o1n1 = (INT32)(data << 14) >> 14; + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "voice %d, O1(n-1)=%05x\n", chip->current_page & 0x1f, voice->o1n1 & 0x3ffff); + break; + + case 0x50/8: /* W_ST */ + chip->wst = data & 0x7f; + break; + + case 0x58/8: /* W_END */ + chip->wend = data & 0x7f; + break; + + case 0x60/8: /* LR_END */ + chip->lrend = data & 0x7f; + break; + + case 0x68/8: /* PAR - read only */ + case 0x70/8: /* IRQV - read only */ + break; + + case 0x78/8: /* PAGE */ + chip->current_page = data & 0x7f; + break; + } +} + +INLINE void es5506_reg_write_test(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT32 data) +{ + switch (offset) + { + case 0x00/8: /* CHANNEL 0 LEFT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 0 left test write %08x\n", data); + break; + + case 0x08/8: /* CHANNEL 0 RIGHT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 0 right test write %08x\n", data); + break; + + case 0x10/8: /* CHANNEL 1 LEFT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 1 left test write %08x\n", data); + break; + + case 0x18/8: /* CHANNEL 1 RIGHT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 1 right test write %08x\n", data); + break; + + case 0x20/8: /* CHANNEL 2 LEFT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 2 left test write %08x\n", data); + break; + + case 0x28/8: /* CHANNEL 2 RIGHT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 2 right test write %08x\n", data); + break; + + case 0x30/8: /* CHANNEL 3 LEFT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 3 left test write %08x\n", data); + break; + + case 0x38/8: /* CHANNEL 3 RIGHT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 3 right test write %08x\n", data); + break; + + case 0x40/8: /* CHANNEL 4 LEFT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 4 left test write %08x\n", data); + break; + + case 0x48/8: /* CHANNEL 4 RIGHT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 4 right test write %08x\n", data); + break; + + case 0x50/8: /* CHANNEL 5 LEFT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 5 left test write %08x\n", data); + break; + + case 0x58/8: /* CHANNEL 6 RIGHT */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Channel 5 right test write %08x\n", data); + break; + + case 0x60/8: /* EMPTY */ + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "Test write EMPTY %08x\n", data); + break; + + case 0x68/8: /* PAR - read only */ + case 0x70/8: /* IRQV - read only */ + break; + + case 0x78/8: /* PAGE */ + chip->current_page = data & 0x7f; + break; + } +} + +//WRITE8_DEVICE_HANDLER( es5506_w ) +static void es5506_w(es5506_state *chip, offs_t offset, UINT8 data) +{ + //es5506_state *chip = get_safe_token(device); + es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; + int shift = 8 * (offset & 3); + + /* accumulate the data */ + chip->write_latch = (chip->write_latch & ~(0xff000000 >> shift)) | (data << (24 - shift)); + + /* wait for a write to complete */ + if (shift != 24) + return; + + /* force an update */ + //chip->stream->update(); + + /* switch off the page and register */ + if (chip->current_page < 0x20) + es5506_reg_write_low(chip, voice, offset / 4, chip->write_latch); + else if (chip->current_page < 0x40) + es5506_reg_write_high(chip, voice, offset / 4, chip->write_latch); + else + es5506_reg_write_test(chip, voice, offset / 4, chip->write_latch); + + /* clear the write latch when done */ + chip->write_latch = 0; +} + + + +/********************************************************************************************** + + es5506_reg_read -- read from the specified ES5506 register + +***********************************************************************************************/ + +INLINE UINT32 es5506_reg_read_low(es5506_state *chip, es5506_voice *voice, offs_t offset) +{ + UINT32 result = 0; + + switch (offset) + { + case 0x00/8: /* CR */ + result = voice->control; + break; + + case 0x08/8: /* FC */ + result = voice->freqcount; + break; + + case 0x10/8: /* LVOL */ + result = voice->lvol; + break; + + case 0x18/8: /* LVRAMP */ + result = voice->lvramp << 8; + break; + + case 0x20/8: /* RVOL */ + result = voice->rvol; + break; + + case 0x28/8: /* RVRAMP */ + result = voice->rvramp << 8; + break; + + case 0x30/8: /* ECOUNT */ + result = voice->ecount; + break; + + case 0x38/8: /* K2 */ + result = voice->k2; + break; + + case 0x40/8: /* K2RAMP */ + result = (voice->k2ramp << 8) | (voice->k2ramp >> 31); + break; + + case 0x48/8: /* K1 */ + result = voice->k1; + break; + + case 0x50/8: /* K1RAMP */ + result = (voice->k1ramp << 8) | (voice->k1ramp >> 31); + break; + + case 0x58/8: /* ACTV */ + result = chip->active_voices; + break; + + case 0x60/8: /* MODE */ + result = chip->mode; + break; + + case 0x68/8: /* PAR */ + //if (chip->port_read) + // result = (*chip->port_read)(); + break; + + case 0x70/8: /* IRQV */ + result = chip->irqv; + update_internal_irq_state(chip); + break; + + case 0x78/8: /* PAGE */ + result = chip->current_page; + break; + } + return result; +} + + +INLINE UINT32 es5506_reg_read_high(es5506_state *chip, es5506_voice *voice, offs_t offset) +{ + UINT32 result = 0; + + switch (offset) + { + case 0x00/8: /* CR */ + result = voice->control; + break; + + case 0x08/8: /* START */ + result = voice->start; + break; + + case 0x10/8: /* END */ + result = voice->end; + break; + + case 0x18/8: /* ACCUM */ + result = voice->accum; + break; + + case 0x20/8: /* O4(n-1) */ + result = voice->o4n1 & 0x3ffff; + break; + + case 0x28/8: /* O3(n-1) */ + result = voice->o3n1 & 0x3ffff; + break; + + case 0x30/8: /* O3(n-2) */ + result = voice->o3n2 & 0x3ffff; + break; + + case 0x38/8: /* O2(n-1) */ + result = voice->o2n1 & 0x3ffff; + break; + + case 0x40/8: /* O2(n-2) */ + result = voice->o2n2 & 0x3ffff; + break; + + case 0x48/8: /* O1(n-1) */ + result = voice->o1n1 & 0x3ffff; + break; + + case 0x50/8: /* W_ST */ + result = chip->wst; + break; + + case 0x58/8: /* W_END */ + result = chip->wend; + break; + + case 0x60/8: /* LR_END */ + result = chip->lrend; + break; + + case 0x68/8: /* PAR */ + //if (chip->port_read) + // result = (*chip->port_read)(); + break; + + case 0x70/8: /* IRQV */ + result = chip->irqv; + update_internal_irq_state(chip); + break; + + case 0x78/8: /* PAGE */ + result = chip->current_page; + break; + } + return result; +} + +INLINE UINT32 es5506_reg_read_test(es5506_state *chip, es5506_voice *voice, offs_t offset) +{ + UINT32 result = 0; + + switch (offset) + { + case 0x68/8: /* PAR */ + //if (chip->port_read) + // result = (*chip->port_read)(); + break; + + case 0x70/8: /* IRQV */ + result = chip->irqv; + break; + + case 0x78/8: /* PAGE */ + result = chip->current_page; + break; + } + return result; +} + +//READ8_DEVICE_HANDLER( es5506_r ) +static UINT8 es5506_r(es5506_state *chip, offs_t offset) +{ + //es5506_state *chip = get_safe_token(device); + es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; + int shift = 8 * (offset & 3); + + /* only read on offset 0 */ + if (shift != 0) + return chip->read_latch >> (24 - shift); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "read from %02x/%02x -> ", chip->current_page, offset / 4 * 8); + + /* force an update */ + //chip->stream->update(); + + /* switch off the page and register */ + if (chip->current_page < 0x20) + chip->read_latch = es5506_reg_read_low(chip, voice, offset / 4); + else if (chip->current_page < 0x40) + chip->read_latch = es5506_reg_read_high(chip, voice, offset / 4); + else + chip->read_latch = es5506_reg_read_test(chip, voice, offset / 4); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%08x\n", chip->read_latch); + + /* return the high byte */ + return chip->read_latch >> 24; +} + + + +//void es5506_voice_bank_w(device_t *device, int voice, int bank) +void es5506_voice_bank_w(void *_info, int voice, int bank) +{ + //es5506_state *chip = get_safe_token(device); + es5506_state *chip = (es5506_state *)_info; + chip->voice[voice].exbank=bank; +} + + +/********************************************************************************************** + + DEVICE_START( es5505 ) -- start emulation of the ES5505 + +***********************************************************************************************/ + +/*static DEVICE_START( es5505 ) +{ + const es5505_interface *intf = (const es5505_interface *)device->static_config(); + es5506_interface es5506intf; + + memset(&es5506intf, 0, sizeof(es5506intf)); + + es5506intf.region0 = intf->region0; + es5506intf.region1 = intf->region1; + es5506intf.irq_callback = intf->irq_callback; + es5506intf.read_port = intf->read_port; + + es5506_start_common(device, &es5506intf, ES5505); +}*/ + + + +/********************************************************************************************** + + DEVICE_STOP( es5505 ) -- stop emulation of the ES5505 + +***********************************************************************************************/ + +/*static DEVICE_STOP( es5505 ) +{ + DEVICE_STOP_CALL( es5506 ); +} + + +static DEVICE_RESET( es5505 ) +{ + DEVICE_RESET_CALL( es5506 ); +}*/ + + + +/********************************************************************************************** + + es5505_reg_write -- handle a write to the selected ES5505 register + +***********************************************************************************************/ + +INLINE void es5505_reg_write_low(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT16 data) +{ + //running_machine &machine = chip->device->machine(); + UINT8 byteOfs; + + byteOfs = offset & 0x01; + if (! byteOfs) + data <<= 8; + switch (offset >> 1) + { + case 0x00: /* CR */ + if (ACCESSING_BITS_0_7) + { +#if RAINE_CHECK + voice->control &= ~(CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_DIR); +#else + voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); +#endif + voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((data << 12) & CONTROL_BS0); + } + if (ACCESSING_BITS_8_15) + { + voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); + voice->control |= ((data >> 2) & CONTROL_LPMASK) | + ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); + } + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine.describe_context(), chip->current_page & 0x1f, voice->control, data, mem_mask ^ 0xffff); + break; + + case 0x01: /* FC */ + if (ACCESSING_BITS_0_7) + voice->freqcount = (voice->freqcount & ~0x001fe) | ((data & 0x00ff) << 1); + if (ACCESSING_BITS_8_15) + voice->freqcount = (voice->freqcount & ~0x1fe00) | ((data & 0xff00) << 1); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, freq count=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->freqcount); + break; + + case 0x02: /* STRT (hi) */ + if (ACCESSING_BITS_0_7) + voice->start = (voice->start & ~0x03fc0000) | ((data & 0x00ff) << 18); + if (ACCESSING_BITS_8_15) + voice->start = (voice->start & ~0x7c000000) | ((data & 0x1f00) << 18); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, loop start=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->start); + break; + + case 0x03: /* STRT (lo) */ + if (ACCESSING_BITS_0_7) + voice->start = (voice->start & ~0x00000380) | ((data & 0x00e0) << 2); + if (ACCESSING_BITS_8_15) + voice->start = (voice->start & ~0x0003fc00) | ((data & 0xff00) << 2); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, loop start=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->start); + break; + + case 0x04: /* END (hi) */ + if (ACCESSING_BITS_0_7) + voice->end = (voice->end & ~0x03fc0000) | ((data & 0x00ff) << 18); + if (ACCESSING_BITS_8_15) + voice->end = (voice->end & ~0x7c000000) | ((data & 0x1f00) << 18); +#if RAINE_CHECK + voice->control |= CONTROL_STOP0; +#endif + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, loop end=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->end); + break; + + case 0x05: /* END (lo) */ + if (ACCESSING_BITS_0_7) + voice->end = (voice->end & ~0x00000380) | ((data & 0x00e0) << 2); + if (ACCESSING_BITS_8_15) + voice->end = (voice->end & ~0x0003fc00) | ((data & 0xff00) << 2); +#if RAINE_CHECK + voice->control |= CONTROL_STOP0; +#endif + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, loop end=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->end); + break; + + case 0x06: /* K2 */ + if (ACCESSING_BITS_0_7) + voice->k2 = (voice->k2 & ~0x00f0) | (data & 0x00f0); + if (ACCESSING_BITS_8_15) + voice->k2 = (voice->k2 & ~0xff00) | (data & 0xff00); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, K2=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->k2); + break; + + case 0x07: /* K1 */ + if (ACCESSING_BITS_0_7) + voice->k1 = (voice->k1 & ~0x00f0) | (data & 0x00f0); + if (ACCESSING_BITS_8_15) + voice->k1 = (voice->k1 & ~0xff00) | (data & 0xff00); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, K1=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->k1); + break; + + case 0x08: /* LVOL */ + if (ACCESSING_BITS_8_15) + voice->lvol = (voice->lvol & ~0xff00) | (data & 0xff00); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, left vol=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->lvol); + break; + + case 0x09: /* RVOL */ + if (ACCESSING_BITS_8_15) + voice->rvol = (voice->rvol & ~0xff00) | (data & 0xff00); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, right vol=%04x\n", machine.describe_context(), chip->current_page & 0x1f, voice->rvol); + break; + + case 0x0a: /* ACC (hi) */ + if (ACCESSING_BITS_0_7) + voice->accum = (voice->accum & ~0x03fc0000) | ((data & 0x00ff) << 18); + if (ACCESSING_BITS_8_15) + voice->accum = (voice->accum & ~0x7c000000) | ((data & 0x1f00) << 18); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, accum=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->accum); + break; + + case 0x0b: /* ACC (lo) */ + if (ACCESSING_BITS_0_7) + voice->accum = (voice->accum & ~0x000003fc) | ((data & 0x00ff) << 2); + if (ACCESSING_BITS_8_15) + voice->accum = (voice->accum & ~0x0003fc00) | ((data & 0xff00) << 2); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, accum=%08x\n", machine.describe_context(), chip->current_page & 0x1f, voice->accum); + break; + + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + if (ACCESSING_BITS_0_7) + { + chip->active_voices = data & 0x1f; + chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); + //chip->stream->set_sample_rate(chip->sample_rate); + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); + } + break; + + case 0x0e: /* IRQV - read only */ + break; + + case 0x0f: /* PAGE */ + if (ACCESSING_BITS_0_7) + chip->current_page = data & 0x7f; + break; + } +} + + +INLINE void es5505_reg_write_high(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT16 data) +{ + //running_machine &machine = chip->device->machine(); + UINT8 byteOfs; + + byteOfs = offset & 0x01; + if (! byteOfs) + data <<= 8; + switch (offset >> 1) + { + case 0x00: /* CR */ + if (ACCESSING_BITS_0_7) + { + voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); + voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((data << 12) & CONTROL_BS0); + } + if (ACCESSING_BITS_8_15) + { + voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); + voice->control |= ((data >> 2) & CONTROL_LPMASK) | + ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); + } + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine.describe_context(), chip->current_page & 0x1f, voice->control, data, mem_mask); + break; + + case 0x01: /* O4(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o4n1 = (voice->o4n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o4n1 = (INT16)((voice->o4n1 & ~0xff00) | (data & 0xff00)); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, O4(n-1)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o4n1 & 0x3ffff); + break; + + case 0x02: /* O3(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o3n1 = (voice->o3n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o3n1 = (INT16)((voice->o3n1 & ~0xff00) | (data & 0xff00)); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, O3(n-1)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o3n1 & 0x3ffff); + break; + + case 0x03: /* O3(n-2) */ + if (ACCESSING_BITS_0_7) + voice->o3n2 = (voice->o3n2 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o3n2 = (INT16)((voice->o3n2 & ~0xff00) | (data & 0xff00)); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, O3(n-2)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o3n2 & 0x3ffff); + break; + + case 0x04: /* O2(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o2n1 = (voice->o2n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o2n1 = (INT16)((voice->o2n1 & ~0xff00) | (data & 0xff00)); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, O2(n-1)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o2n1 & 0x3ffff); + break; + + case 0x05: /* O2(n-2) */ + if (ACCESSING_BITS_0_7) + voice->o2n2 = (voice->o2n2 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o2n2 = (INT16)((voice->o2n2 & ~0xff00) | (data & 0xff00)); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, O2(n-2)=%05x\n", machine.describe_context(), chip->current_page & 0x1f, voice->o2n2 & 0x3ffff); + break; + + case 0x06: /* O1(n-1) */ + if (ACCESSING_BITS_0_7) + voice->o1n1 = (voice->o1n1 & ~0x00ff) | (data & 0x00ff); + if (ACCESSING_BITS_8_15) + voice->o1n1 = (INT16)((voice->o1n1 & ~0xff00) | (data & 0xff00)); + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%s:voice %d, O1(n-1)=%05x (accum=%08x)\n", machine.describe_context(), chip->current_page & 0x1f, voice->o2n1 & 0x3ffff, voice->accum); + break; + + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + if (ACCESSING_BITS_0_7) + { + chip->active_voices = data & 0x1f; + chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); + //chip->stream->set_sample_rate(chip->sample_rate); + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); + } + break; + + case 0x0e: /* IRQV - read only */ + break; + + case 0x0f: /* PAGE */ + if (ACCESSING_BITS_0_7) + chip->current_page = data & 0x7f; + break; + } +} + + +INLINE void es5505_reg_write_test(es5506_state *chip, es5506_voice *voice, offs_t offset, UINT16 data) +{ + UINT8 byteOfs; + + byteOfs = offset & 0x01; + if (! byteOfs) + data <<= 8; + switch (offset >> 1) + { + case 0x00: /* CH0L */ + case 0x01: /* CH0R */ + case 0x02: /* CH1L */ + case 0x03: /* CH1R */ + case 0x04: /* CH2L */ + case 0x05: /* CH2R */ + case 0x06: /* CH3L */ + case 0x07: /* CH3R */ + break; + + case 0x08: /* SERMODE */ + if (ACCESSING_BITS_0_7) + chip->mode = data & 0x07; + break; + + case 0x09: /* PAR */ + break; + + case 0x0d: /* ACT */ + if (ACCESSING_BITS_0_7) + { + chip->active_voices = data & 0x1f; + chip->sample_rate = chip->master_clock / (16 * (chip->active_voices + 1)); + //chip->stream->set_sample_rate(chip->sample_rate); + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "active voices=%d, sample_rate=%d\n", chip->active_voices, chip->sample_rate); + } + break; + + case 0x0e: /* IRQV - read only */ + break; + + case 0x0f: /* PAGE */ + if (ACCESSING_BITS_0_7) + chip->current_page = data & 0x7f; + break; + } +} + + +//WRITE16_DEVICE_HANDLER( es5505_w ) +static void es5505_w(es5506_state *chip, offs_t offset, UINT8 data) +{ + //es5506_state *chip = get_safe_token(device); + es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; + +// logerror("%s:ES5505 write %02x/%02x = %04x & %04x\n", machine.describe_context(), chip->current_page, offset, data, mem_mask); + + /* force an update */ + //chip->stream->update(); + + /* switch off the page and register */ + if (chip->current_page < 0x20) + es5505_reg_write_low(chip, voice, offset, data); + else if (chip->current_page < 0x40) + es5505_reg_write_high(chip, voice, offset, data); + else + es5505_reg_write_test(chip, voice, offset, data); +} + + + +/********************************************************************************************** + + es5505_reg_read -- read from the specified ES5505 register + +***********************************************************************************************/ + +INLINE UINT16 es5505_reg_read_low(es5506_state *chip, es5506_voice *voice, offs_t offset) +{ + UINT16 result = 0; + + switch (offset) + { + case 0x00: /* CR */ + result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((voice->control & CONTROL_BS0) >> 12) | + ((voice->control & CONTROL_LPMASK) << 2) | + ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | + 0xf000; + break; + + case 0x01: /* FC */ + result = voice->freqcount >> 1; + break; + + case 0x02: /* STRT (hi) */ + result = voice->start >> 18; + break; + + case 0x03: /* STRT (lo) */ + result = voice->start >> 2; + break; + + case 0x04: /* END (hi) */ + result = voice->end >> 18; + break; + + case 0x05: /* END (lo) */ + result = voice->end >> 2; + break; + + case 0x06: /* K2 */ + result = voice->k2; + break; + + case 0x07: /* K1 */ + result = voice->k1; + break; + + case 0x08: /* LVOL */ + result = voice->lvol; + break; + + case 0x09: /* RVOL */ + result = voice->rvol; + break; + + case 0x0a: /* ACC (hi) */ + result = voice->accum >> 18; + break; + + case 0x0b: /* ACC (lo) */ + result = voice->accum >> 2; + break; + + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + result = chip->active_voices; + break; + + case 0x0e: /* IRQV */ + result = chip->irqv; + update_internal_irq_state(chip); + break; + + case 0x0f: /* PAGE */ + result = chip->current_page; + break; + } + return result; +} + + +INLINE UINT16 es5505_reg_read_high(es5506_state *chip, es5506_voice *voice, offs_t offset) +{ + UINT16 result = 0; + + switch (offset) + { + case 0x00: /* CR */ + result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | + ((voice->control & CONTROL_BS0) >> 12) | + ((voice->control & CONTROL_LPMASK) << 2) | + ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | + 0xf000; + break; + + case 0x01: /* O4(n-1) */ + result = voice->o4n1; + break; + + case 0x02: /* O3(n-1) */ + result = voice->o3n1; + break; + + case 0x03: /* O3(n-2) */ + result = voice->o3n2; + break; + + case 0x04: /* O2(n-1) */ + result = voice->o2n1; + break; + + case 0x05: /* O2(n-2) */ + result = voice->o2n2; + break; + + case 0x06: /* O1(n-1) */ + /* special case for the Taito F3 games: they set the accumulator on a stopped */ + /* voice and assume the filters continue to process the data. They then read */ + /* the O1(n-1) in order to extract raw data from the sound ROMs. Since we don't */ + /* want to waste time filtering stopped channels, we just look for a read from */ + /* this register on a stopped voice, and return the raw sample data at the */ + /* accumulator */ + if ((voice->control & CONTROL_STOPMASK) && chip->region_base[voice->control >> 14]) + { + voice->o1n1 = chip->region_base[voice->control >> 14][voice->exbank + (voice->accum >> 11)]; + logerror("%02x %08x ==> %08x\n",voice->o1n1,voice->control >> 14,voice->exbank + (voice->accum >> 11)); + } + result = voice->o1n1; + break; + + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: /* unused */ + break; + + case 0x0d: /* ACT */ + result = chip->active_voices; + break; + + case 0x0e: /* IRQV */ + result = chip->irqv; + update_internal_irq_state(chip); + break; + + case 0x0f: /* PAGE */ + result = chip->current_page; + break; + } + return result; +} + + +INLINE UINT16 es5505_reg_read_test(es5506_state *chip, es5506_voice *voice, offs_t offset) +{ + UINT16 result = 0; + + switch (offset) + { + case 0x00: /* CH0L */ + case 0x01: /* CH0R */ + case 0x02: /* CH1L */ + case 0x03: /* CH1R */ + case 0x04: /* CH2L */ + case 0x05: /* CH2R */ + case 0x06: /* CH3L */ + case 0x07: /* CH3R */ + break; + + case 0x08: /* SERMODE */ + result = chip->mode; + break; + + case 0x09: /* PAR */ + //if (chip->port_read) + // result = (*chip->port_read)(); + break; + + case 0x0f: /* PAGE */ + result = chip->current_page; + break; + } + return result; +} + + +//READ16_DEVICE_HANDLER( es5505_r ) +static UINT8 es5505_r(es5506_state *chip, offs_t offset) +{ + //es5506_state *chip = get_safe_token(device); + es5506_voice *voice = &chip->voice[chip->current_page & 0x1f]; + UINT16 result = 0; + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "read from %02x/%02x -> ", chip->current_page, offset); + + /* force an update */ + //chip->stream->update(); + + /* switch off the page and register */ + if (chip->current_page < 0x20) + result = es5505_reg_read_low(chip, voice, offset); + else if (chip->current_page < 0x40) + result = es5505_reg_read_high(chip, voice, offset); + else + result = es5505_reg_read_test(chip, voice, offset); + + //if (LOG_COMMANDS && eslog) + // fprintf(eslog, "%04x (accum=%08x)\n", result, voice->accum); + + /* return the high byte */ + if (offset & 0x01) + return (result & 0x00FF) >> 0; + else + return (result & 0xFF00) >> 8; +} + +UINT8 es550x_r(void *_info, offs_t offset) +{ + es5506_state *chip = (es5506_state *)_info; + + if (! chip->sndtype) + return es5505_r(chip, offset); + else + return es5506_r(chip, offset); +} + +void es550x_w(void *_info, offs_t offset, UINT8 data) +{ + es5506_state *chip = (es5506_state *)_info; + if (offset < 0x40) + { + if (! chip->sndtype) + es5505_w(chip, offset, data); + else + es5506_w(chip, offset, data); + } + else + { + es5506_voice_bank_w(chip, offset & 0x1F, data << 20); + } + return; +} + +void es550x_w16(void *_info, offs_t offset, UINT16 data) +{ + es5506_state *chip = (es5506_state *)_info; + + if (offset < 0x40) + { + if (! chip->sndtype) + { + es5505_w(chip, offset | 0, (data & 0xFF00) >> 8); + es5505_w(chip, offset | 1, (data & 0x00FF) >> 0); + } + else + { + es5506_w(chip, offset | 0, (data & 0xFF00) >> 8); + es5506_w(chip, offset | 1, (data & 0x00FF) >> 0); + } + } + else + { + es5506_voice_bank_w(chip, offset & 0x1F, data << 20); + } + return; +} + + + +/*void es5505_voice_bank_w(device_t *device, int voice, int bank) +{ + //es5506_state *chip = get_safe_token(device); + es5506_state *chip = &ES5506Data[ChipID]; +#if RAINE_CHECK + chip->voice[voice].control = CONTROL_STOPMASK; +#endif + chip->voice[voice].exbank=bank; +}*/ + +/*void es5505_set_channel_volume(device_t *device, int channel, int volume) +{ + //es5506_state *chip = get_safe_token(device); + es5506_state *chip = &ES5506Data[ChipID]; + + chip->stream->set_output_gain(channel,volume / 100.0); +}*/ + +void es5506_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + es5506_state *info = (es5506_state *)_info; + UINT8 curRgn; + UINT8 is8bROM; + UINT32 curPos; + + curRgn = (DataStart >> 28) & 0x03; + is8bROM = (DataStart >> 31) & 0x01; + DataStart &= 0x0FFFFFFF; + + if (is8bROM) + { + // multiply all values with 2 for boundary checks + ROMSize *= 2; + DataStart *= 2; + DataLength *= 2; + } + if (info->region_size[curRgn] != ROMSize) + { + info->region_base[curRgn] = (UINT16*)realloc(info->region_base[curRgn], ROMSize); + info->region_size[curRgn] = ROMSize; + memset(info->region_base[curRgn], 0x00, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + if (! is8bROM) + { + memcpy((UINT8*)info->region_base[curRgn] + DataStart, ROMData, DataLength); + } + else + { + DataLength /= 2; + for (curPos = 0x00; curPos < DataLength; curPos ++) + info->region_base[curRgn][DataStart + curPos] = ROMData[curPos] << 8; + } + + return; +} + +void es5506_set_mute_mask(void *_info, UINT32 MuteMask) +{ + es5506_state *chip = (es5506_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 32; CurChn ++) + chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + +void es5506_set_srchg_cb(void *_info, SRATE_CALLBACK CallbackFunc, void* DataPtr) +{ + es5506_state *chip = (es5506_state *)_info; + + // set Sample Rate Change Callback routine + chip->SmpRateFunc = CallbackFunc; + chip->SmpRateData = DataPtr; + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( es5505 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(es5506_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( es5505 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( es5505 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( es5505 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "ES5505"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Ensoniq Wavetable"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( es5506 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(es5506_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( es5506 ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( es5506 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( es5506 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "ES5506"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Ensoniq Wavetable"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(ES5505, es5505); +DEFINE_LEGACY_SOUND_DEVICE(ES5506, es5506);*/ diff --git a/Frameworks/GME/vgmplay/chips/es5506.h b/Frameworks/GME/vgmplay/chips/es5506.h new file mode 100644 index 000000000..099e28b67 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/es5506.h @@ -0,0 +1,65 @@ +/********************************************************************************************** + * + * Ensoniq ES5505/6 driver + * by Aaron Giles + * + **********************************************************************************************/ + +#pragma once + +#ifndef __ES5506_H__ +#define __ES5506_H__ + +//#include "devlegcy.h" + +/*typedef struct _es5505_interface es5505_interface; +struct _es5505_interface +{ + const char * region0; // memory region where the sample ROM lives + const char * region1; // memory region where the sample ROM lives + void (*irq_callback)(device_t *device, int state); // irq callback + UINT16 (*read_port)(device_t *device); // input port read +};* + +READ16_DEVICE_HANDLER( es5505_r ); +WRITE16_DEVICE_HANDLER( es5505_w ); +void es5505_voice_bank_w(device_t *device, int voice, int bank); +void es5505_set_channel_volume(device_t *device, int channel, int volume); + +//DECLARE_LEGACY_SOUND_DEVICE(ES5505, es5505); + + +typedef struct _es5506_interface es5506_interface; +struct _es5506_interface +{ + const char * region0; // memory region where the sample ROM lives + const char * region1; // memory region where the sample ROM lives + const char * region2; // memory region where the sample ROM lives + const char * region3; // memory region where the sample ROM lives + void (*irq_callback)(device_t *device, int state); // irq callback + UINT16 (*read_port)(device_t *device); // input port read +};*/ + +//READ8_DEVICE_HANDLER( es5506_r ); +//WRITE8_DEVICE_HANDLER( es5506_w ); +UINT8 es550x_r(void *chip, offs_t offset); +void es550x_w(void *chip, offs_t offset, UINT8 data); +void es550x_w16(void *chip, offs_t offset, UINT16 data); + +void es5506_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_es5506(void **chip, int clock, int channels); +void device_stop_es5506(void *chip); +void device_reset_es5506(void *chip); +//void es5506_set_base(running_device *device, UINT8 *wavemem); + +void es5506_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void es5506_set_mute_mask(void *chip, UINT32 MuteMask); +void es5506_set_srchg_cb(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr); + +//void es5506_voice_bank_w(device_t *device, int voice, int bank); + +//DECLARE_LEGACY_SOUND_DEVICE(ES5506, es5506); + +#endif /* __ES5506_H__ */ diff --git a/Frameworks/GME/gme/fm.c b/Frameworks/GME/vgmplay/chips/fm.c similarity index 95% rename from Frameworks/GME/gme/fm.c rename to Frameworks/GME/vgmplay/chips/fm.c index 380b09cd7..dc680909d 100644 --- a/Frameworks/GME/gme/fm.c +++ b/Frameworks/GME/vgmplay/chips/fm.c @@ -115,9 +115,8 @@ #include #include #include +#include #include -#include "mathdefs.h" - #include "mamedef.h" //#ifndef __RAINE__ @@ -379,7 +378,7 @@ static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 - (1.4 dB is loosing precision as you can see) + (1.4 dB is losing precision as you can see) It's implemented as generator from 0..126 with step 2 then a shift right N times, where N is: @@ -625,6 +624,8 @@ typedef struct /* local time tables */ INT32 dt_tab[8][32]; /* DeTune table */ /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; const ssg_callbacks *SSG; } FM_ST; @@ -712,6 +713,7 @@ INLINE void FM_STATUS_SET(FM_ST *ST,int flag) { ST->irq = 1; /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); } } @@ -724,6 +726,7 @@ INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) { ST->irq = 0; /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); } } @@ -762,6 +765,7 @@ INLINE void set_timers( FM_ST *ST, void *n, int v ) { ST->TBC = ( 256-ST->TB)<<4; /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock); } } else @@ -769,6 +773,7 @@ INLINE void set_timers( FM_ST *ST, void *n, int v ) if( ST->TBC != 0 ) { ST->TBC = 0; + if (ST->timer_handler) (ST->timer_handler)(n,1,0,ST->clock); } } /* load a */ @@ -778,6 +783,7 @@ INLINE void set_timers( FM_ST *ST, void *n, int v ) { ST->TAC = (1024-ST->TA); /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock); } } else @@ -785,6 +791,7 @@ INLINE void set_timers( FM_ST *ST, void *n, int v ) if( ST->TAC != 0 ) { ST->TAC = 0; + if (ST->timer_handler) (ST->timer_handler)(n,0,0,ST->clock); } } } @@ -797,6 +804,7 @@ INLINE void TimerAOver(FM_ST *ST) if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); /* clear or reload the counter */ ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock); } /* Timer B Overflow */ INLINE void TimerBOver(FM_ST *ST) @@ -805,6 +813,7 @@ INLINE void TimerBOver(FM_ST *ST) if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); /* clear or reload the counter */ ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock); } @@ -814,7 +823,7 @@ INLINE void TimerBOver(FM_ST *ST) /* ---------- calculate timer A ---------- */ #define INTERNAL_TIMER_A(ST,CSM_CH) \ { \ - if( (ST)->TAC && (1) ) \ + if( (ST)->TAC && ((ST)->timer_handler==0) ) \ if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ { \ TimerAOver( ST ); \ @@ -826,7 +835,7 @@ INLINE void TimerBOver(FM_ST *ST) /* ---------- calculate timer B ---------- */ #define INTERNAL_TIMER_B(ST,step) \ { \ - if( (ST)->TBC && (1) ) \ + if( (ST)->TBC && ((ST)->timer_handler==0) ) \ if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ TimerBOver( ST ); \ } @@ -1083,7 +1092,7 @@ INLINE void advance_lfo(FM_OPN *OPN) /* update AM when LFO output changes */ - /* actually I can't optimize is this way without rewritting chan_calc() + /* actually I can't optimize is this way without rewriting chan_calc() to use chip->lfo_am instead of global lfo_am */ { @@ -2226,6 +2235,38 @@ void ym2203_reset_chip(void *chip) } #ifdef __STATE_H__ +void ym2203_postload(void *chip) +{ + if (chip) + { + YM2203 *F2203 = (YM2203 *)chip; + int r; + + /* prescaler */ + OPNPrescaler_w(&F2203->OPN,1,1); + + /* SSG registers */ + for(r=0;r<16;r++) + { + (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,0,r); + (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,1,F2203->REGS[r]); + } + + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); + + /* channels */ + /*FM_channel_postload(F2203->CH,3);*/ + } +} + static void YM2203_save_state(YM2203 *F2203, const device_config *device) { state_save_register_device_item_array(device, 0, F2203->REGS); @@ -2245,7 +2286,8 @@ static void YM2203_save_state(YM2203 *F2203, const device_config *device) */ //void * ym2203_init(void *param, const device_config *device, int clock, int rate, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) -void * ym2203_init(void *param, int clock, int rate, const ssg_callbacks *ssg) +void * ym2203_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2203 *F2203; @@ -2268,6 +2310,8 @@ void * ym2203_init(void *param, int clock, int rate, const ssg_callbacks *ssg) F2203->OPN.ST.clock = clock; F2203->OPN.ST.rate = rate; + F2203->OPN.ST.timer_handler = timer_handler; + F2203->OPN.ST.IRQ_Handler = IRQHandler; F2203->OPN.ST.SSG = ssg; #ifdef __STATE_H__ @@ -2313,10 +2357,12 @@ int ym2203_write(void *chip,int a,UINT8 v) (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x20: /* 0x20-0x2f : Mode section */ + ym2203_update_req(OPN->ST.param); /* write register */ OPNWriteMode(OPN,addr,v); break; default: /* 0x30-0xff : OPN section */ + ym2203_update_req(OPN->ST.param); /* write register */ OPNWriteReg(OPN,addr,v); } @@ -2352,6 +2398,7 @@ int ym2203_timer_over(void *chip,int c) } else { /* Timer A */ + ym2203_update_req(F2203->OPN.ST.param); /* timer update */ TimerAOver( &(F2203->OPN.ST) ); /* CSM mode key,TL control */ @@ -2552,7 +2599,8 @@ static void FM_ADPCMAWrite(YM2610 *F2610,int r,int v) if( (v>>c)&1 ) { /**** start adpcm ****/ - adpcm[c].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); + // The .step variable is already set and for the YM2608 it is different on channels 4 and 5. + //adpcm[c].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); adpcm[c].now_addr = adpcm[c].start<<1; adpcm[c].now_step = 0; adpcm[c].adpcm_acc = 0; @@ -3432,6 +3480,70 @@ void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length) } +#ifdef __STATE_H__ +void ym2608_postload(void *chip) +{ + if (chip) + { + YM2608 *F2608 = (YM2608 *)chip; + int r; + + /* prescaler */ + OPNPrescaler_w(&F2608->OPN,1,2); + F2608->deltaT.freqbase = F2608->OPN.ST.freqbase; + /* IRQ mask / mode */ + YM2608IRQMaskWrite(&F2608->OPN, F2608, F2608->REGS[0x29]); + /* SSG registers */ + for(r=0;r<16;r++) + { + (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,0,r); + (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,1,F2608->REGS[r]); + } + + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); + OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); + OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); + } + /* FM channels */ + /*FM_channel_postload(F2608->CH,6);*/ + /* rhythm(ADPCMA) */ + FM_ADPCMAWrite(F2608,1,F2608->REGS[0x111]); + for( r=0x08 ; r<0x0c ; r++) + FM_ADPCMAWrite(F2608,r,F2608->REGS[r+0x110]); + /* Delta-T ADPCM unit */ + YM_DELTAT_postload(&F2608->deltaT , &F2608->REGS[0x100] ); + } +} + +static void YM2608_save_state(YM2608 *F2608, const device_config *device) +{ + state_save_register_device_item_array(device, 0, F2608->REGS); + FMsave_state_st(device,&F2608->OPN.ST); + FMsave_state_channel(device,F2608->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2608->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2608->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2608->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2608->addr_A1); + /* rythm(ADPCMA) */ + FMsave_state_adpcma(device,F2608->adpcm); + /* Delta-T ADPCM unit */ + YM_DELTAT_savestate(device,&F2608->deltaT); +} +#endif /* _STATE_H */ + static void YM2608_deltat_status_set(void *chip, UINT8 changebits) { YM2608 *F2608 = (YM2608 *)chip; @@ -3446,7 +3558,8 @@ static void YM2608_deltat_status_reset(void *chip, UINT8 changebits) //void * ym2608_init(void *param, const device_config *device, int clock, int rate, // void *pcmrom,int pcmsize, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) -void * ym2608_init(void *param, int clock, int rate, const ssg_callbacks *ssg) +void * ym2608_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2608 *F2608; @@ -3470,6 +3583,8 @@ void * ym2608_init(void *param, int clock, int rate, const ssg_callbacks *ssg) F2608->OPN.ST.rate = rate; /* External handlers */ + F2608->OPN.ST.timer_handler = timer_handler; + F2608->OPN.ST.IRQ_Handler = IRQHandler; F2608->OPN.ST.SSG = ssg; /* DELTA-T */ @@ -3477,6 +3592,7 @@ void * ym2608_init(void *param, int clock, int rate, const ssg_callbacks *ssg) //F2608->deltaT.memory_size = pcmsize; F2608->deltaT.memory = NULL; F2608->deltaT.memory_size = 0x00; + F2608->deltaT.memory_mask = 0x00; /*F2608->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */ /*F2608->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */ @@ -3615,6 +3731,7 @@ int ym2608_write(void *chip, int a,UINT8 v) if( v >= 0x2d && v <= 0x2f ) { OPNPrescaler_w(OPN , v , 2); + //TODO: set ADPCM[c].step F2608->deltaT.freqbase = OPN->ST.freqbase; } break; @@ -3632,6 +3749,7 @@ int ym2608_write(void *chip, int a,UINT8 v) (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x10: /* 0x10-0x1f : Rhythm section */ + ym2608_update_req(OPN->ST.param); FM_ADPCMAWrite(F2608,addr-0x10,v); break; case 0x20: /* Mode Register */ @@ -3641,10 +3759,12 @@ int ym2608_write(void *chip, int a,UINT8 v) YM2608IRQMaskWrite(OPN, F2608, v); break; default: + ym2608_update_req(OPN->ST.param); OPNWriteMode(OPN,addr,v); } break; default: /* OPN section */ + ym2608_update_req(OPN->ST.param); OPNWriteReg(OPN,addr,v); } break; @@ -3660,6 +3780,7 @@ int ym2608_write(void *chip, int a,UINT8 v) addr = OPN->ST.address; F2608->REGS[addr | 0x100] = v; + ym2608_update_req(OPN->ST.param); switch( addr & 0xf0 ) { case 0x00: /* DELTAT PORT */ @@ -3751,6 +3872,7 @@ int ym2608_timer_over(void *chip,int c) break; case 0: { /* Timer A */ + ym2608_update_req(F2608->OPN.ST.param); /* timer update */ TimerAOver( &(F2608->OPN.ST) ); /* CSM mode key,TL controll */ @@ -3783,6 +3905,7 @@ void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataSt F2608->deltaT.memory = (UINT8*)realloc(F2608->deltaT.memory, ROMSize); F2608->deltaT.memory_size = ROMSize; memset(F2608->deltaT.memory, 0xFF, ROMSize); + YM_DELTAT_calc_mem_mask(&F2608->deltaT); } if (DataStart > ROMSize) return; @@ -3842,12 +3965,12 @@ void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length) /* Check YM2610B warning message */ if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) ) { - /*LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0));*/ - FM_KEY_IS(&F2610->CH[3].SLOT[3]) = 0; + LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0)); + FM_KEY_IS(&F2610->CH[0].SLOT[3]) = 0; } if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) ) { - /*LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3));*/ + LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3)); FM_KEY_IS(&F2610->CH[3].SLOT[3]) = 0; } #endif @@ -4132,6 +4255,74 @@ void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length) #endif /* BUILD_YM2610B */ +#ifdef __STATE_H__ +void ym2610_postload(void *chip) +{ + if (chip) + { + YM2610 *F2610 = (YM2610 *)chip; + int r; + + /* SSG registers */ + for(r=0;r<16;r++) + { + (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,0,r); + (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,1,F2610->REGS[r]); + } + + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); + OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); + OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); + } + /* FM channels */ + /*FM_channel_postload(F2610->CH,6);*/ + + /* rhythm(ADPCMA) */ + FM_ADPCMAWrite(F2610,1,F2610->REGS[0x101]); + for( r=0 ; r<6 ; r++) + { + FM_ADPCMAWrite(F2610,r+0x08,F2610->REGS[r+0x108]); + FM_ADPCMAWrite(F2610,r+0x10,F2610->REGS[r+0x110]); + FM_ADPCMAWrite(F2610,r+0x18,F2610->REGS[r+0x118]); + FM_ADPCMAWrite(F2610,r+0x20,F2610->REGS[r+0x120]); + FM_ADPCMAWrite(F2610,r+0x28,F2610->REGS[r+0x128]); + } + /* Delta-T ADPCM unit */ + YM_DELTAT_postload(&F2610->deltaT , &F2610->REGS[0x010] ); + } +} + +static void YM2610_save_state(YM2610 *F2610, const device_config *device) +{ + state_save_register_device_item_array(device, 0, F2610->REGS); + FMsave_state_st(device,&F2610->OPN.ST); + FMsave_state_channel(device,F2610->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2610->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2610->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2610->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2610->addr_A1); + + state_save_register_device_item(device, 0, F2610->adpcm_arrivedEndAddress); + /* rythm(ADPCMA) */ + FMsave_state_adpcma(device,F2610->adpcm); + /* Delta-T ADPCM unit */ + YM_DELTAT_savestate(device,&F2610->deltaT); +} +#endif /* _STATE_H */ + static void YM2610_deltat_status_set(void *chip, UINT8 changebits) { YM2610 *F2610 = (YM2610 *)chip; @@ -4146,7 +4337,8 @@ static void YM2610_deltat_status_reset(void *chip, UINT8 changebits) //void *ym2610_init(void *param, const device_config *device, int clock, int rate, // void *pcmroma,int pcmsizea,void *pcmromb,int pcmsizeb, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) -void *ym2610_init(void *param, int clock, int rate, const ssg_callbacks *ssg) +void *ym2610_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2610 *F2610; @@ -4170,6 +4362,8 @@ void *ym2610_init(void *param, int clock, int rate, const ssg_callbacks *ssg) F2610->OPN.ST.clock = clock; F2610->OPN.ST.rate = rate; /* Extend handler */ + F2610->OPN.ST.timer_handler = timer_handler; + F2610->OPN.ST.IRQ_Handler = IRQHandler; F2610->OPN.ST.SSG = ssg; /* ADPCM */ //F2610->pcmbuf = (const UINT8 *)pcmroma; @@ -4181,6 +4375,7 @@ void *ym2610_init(void *param, int clock, int rate, const ssg_callbacks *ssg) //F2610->deltaT.memory_size = pcmsizeb; F2610->deltaT.memory = NULL; F2610->deltaT.memory_size = 0x00; + F2610->deltaT.memory_mask = 0x00; F2610->deltaT.status_set_handler = YM2610_deltat_status_set; F2610->deltaT.status_reset_handler = YM2610_deltat_status_reset; @@ -4234,6 +4429,7 @@ void ym2610_reset_chip(void *chip) F2610->pcm_size = 0x00; F2610->deltaT.memory = NULL; F2610->deltaT.memory_size = 0x00; + F2610->deltaT.memory_mask = 0x00; /* Reset Prescaler */ OPNSetPres( OPN, 6*24, 6*24, 4*2); /* OPN 1/6 , SSG 1/4 */ @@ -4327,6 +4523,8 @@ int ym2610_write(void *chip, int a, UINT8 v) (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x10: /* DeltaT ADPCM */ + ym2610_update_req(OPN->ST.param); + switch(addr) { case 0x10: /* control 1 */ @@ -4367,9 +4565,11 @@ int ym2610_write(void *chip, int a, UINT8 v) break; case 0x20: /* Mode Register */ + ym2610_update_req(OPN->ST.param); OPNWriteMode(OPN,addr,v); break; default: /* OPN section */ + ym2610_update_req(OPN->ST.param); /* write register */ OPNWriteReg(OPN,addr,v); } @@ -4384,6 +4584,7 @@ int ym2610_write(void *chip, int a, UINT8 v) if (F2610->addr_A1 != 1) break; /* verified on real YM2608 */ + ym2610_update_req(OPN->ST.param); addr = OPN->ST.address; F2610->REGS[addr | 0x100] = v; if( addr < 0x30 ) @@ -4434,6 +4635,7 @@ int ym2610_timer_over(void *chip,int c) } else { /* Timer A */ + ym2610_update_req(F2610->OPN.ST.param); /* timer update */ TimerAOver( &(F2610->OPN.ST) ); /* CSM mode key,TL controll */ @@ -4472,6 +4674,7 @@ void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataSt F2610->deltaT.memory = (UINT8*)realloc(F2610->deltaT.memory, ROMSize); F2610->deltaT.memory_size = ROMSize; memset(F2610->deltaT.memory, 0xFF, ROMSize); + YM_DELTAT_calc_mem_mask(&F2610->deltaT); } if (DataStart > ROMSize) return; diff --git a/Frameworks/GME/gme/fm.h b/Frameworks/GME/vgmplay/chips/fm.h similarity index 60% rename from Frameworks/GME/gme/fm.h rename to Frameworks/GME/vgmplay/chips/fm.h index bee9f9095..a63540ee8 100644 --- a/Frameworks/GME/gme/fm.h +++ b/Frameworks/GME/vgmplay/chips/fm.h @@ -5,12 +5,15 @@ #pragma once -#include "mamedef.h" - -#ifdef __cplusplus -extern "C" { -#endif - +/* --- select emulation chips --- */ +/* +#define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator +#define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator +#define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator +#define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator +#define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator +#define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator +*/ #define BUILD_YM2203 1 #define BUILD_YM2608 1 #define BUILD_YM2610 1 @@ -28,6 +31,65 @@ extern "C" { /* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ //#define FM_BUSY_FLAG_SUPPORT 1 +/* --- external SSG(YM2149/AY-3-8910)emulator interface port */ +/* used by YM2203,YM2608,and YM2610 */ +typedef struct _ssg_callbacks ssg_callbacks; +struct _ssg_callbacks +{ + void (*set_clock)(void *param, int clock); + void (*write)(void *param, int address, int data); + int (*read)(void *param); + void (*reset)(void *param); +}; + +/* --- external callback funstions for realtime update --- */ + +#if FM_BUSY_FLAG_SUPPORT +#define TIME_TYPE attotime +#define UNDEFINED_TIME attotime_zero +#define FM_GET_TIME_NOW(machine) timer_get_time(machine) +#define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) +#define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) +#define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) +#endif + +#if BUILD_YM2203 + /* in 2203intf.c */ + void ym2203_update_request(void *param); + #define ym2203_update_req(chip) ym2203_update_request(chip) +#endif /* BUILD_YM2203 */ + +#if BUILD_YM2608 + /* in 2608intf.c */ + void ym2608_update_request(void *param); + #define ym2608_update_req(chip) ym2608_update_request(chip); +#endif /* BUILD_YM2608 */ + +#if (BUILD_YM2610||BUILD_YM2610B) + /* in 2610intf.c */ + void ym2610_update_request(void *param); + #define ym2610_update_req(chip) ym2610_update_request(chip); +#endif /* (BUILD_YM2610||BUILD_YM2610B) */ + +#if (BUILD_YM2612||BUILD_YM3438) + /* in 2612intf.c */ + void ym2612_update_request(void *param); + #define ym2612_update_req(chip) ym2612_update_request(chip); +#endif /* (BUILD_YM2612||BUILD_YM3438) */ + +/* compiler dependence */ +#if 0 +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif /* OSD_CPU_H */ +#endif + typedef stream_sample_t FMSAMPLE; @@ -40,6 +102,8 @@ typedef unsigned char FMSAMPLE; #endif */ +typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); +typedef void (*FM_IRQHANDLER)(void *param,int irq); /* FM_TIMERHANDLER : Stop or Start timer */ /* int n = chip number */ /* int c = Channel 0=TimerA,1=TimerB */ @@ -50,15 +114,6 @@ typedef unsigned char FMSAMPLE; /* int n = chip number */ /* int irq = IRQ level 0=OFF,1=ON */ -typedef struct _ssg_callbacks ssg_callbacks; -struct _ssg_callbacks -{ - void (*set_clock)(void *param, int clock); - void (*write)(void *param, int address, int data); - int (*read)(void *param); - void (*reset)(void *param); -}; - #if BUILD_YM2203 /* -------------------- YM2203(OPN) Interface -------------------- */ @@ -74,7 +129,8 @@ struct _ssg_callbacks */ //void * ym2203_init(void *param, const device_config *device, int baseclock, int rate, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); -void * ym2203_init(void *param, int baseclock, int rate, const ssg_callbacks *ssg); +void * ym2203_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); /* ** shutdown the YM2203 emulators @@ -121,15 +177,18 @@ void ym2203_set_mutemask(void *chip, UINT32 MuteMask); //void * ym2608_init(void *param, const device_config *device, int baseclock, int rate, // void *pcmroma,int pcmsizea, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); -void * ym2608_init(void *param, int baseclock, int rate, const ssg_callbacks *ssg); +void * ym2608_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void ym2608_shutdown(void *chip); void ym2608_reset_chip(void *chip); void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length); int ym2608_write(void *chip, int a,unsigned char v); unsigned char ym2608_read(void *chip,int a); +int ym2608_timer_over(void *chip, int c ); +void ym2608_postload(void *chip); void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, - offs_t DataLength, const UINT8* ROMData); + offs_t DataLength, const UINT8* ROMData); void ym2608_set_mutemask(void *chip, UINT32 MuteMask); #endif /* BUILD_YM2608 */ @@ -139,7 +198,8 @@ void ym2608_set_mutemask(void *chip, UINT32 MuteMask); //void * ym2610_init(void *param, const device_config *device, int baseclock, int rate, // void *pcmroma,int pcmasize,void *pcmromb,int pcmbsize, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); -void * ym2610_init(void *param, int baseclock, int rate, const ssg_callbacks *ssg); +void * ym2610_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void ym2610_shutdown(void *chip); void ym2610_reset_chip(void *chip); void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length); @@ -153,7 +213,7 @@ unsigned char ym2610_read(void *chip,int a); int ym2610_timer_over(void *chip, int c ); void ym2610_postload(void *chip); void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, - offs_t DataLength, const UINT8* ROMData); + offs_t DataLength, const UINT8* ROMData); void ym2610_set_mutemask(void *chip, UINT32 MuteMask); #endif /* (BUILD_YM2610||BUILD_YM2610B) */ @@ -161,18 +221,18 @@ void ym2610_set_mutemask(void *chip, UINT32 MuteMask); #if (BUILD_YM2612||BUILD_YM3438) //void * ym2612_init(void *param, const device_config *device, int baseclock, int rate, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); -void * ym2612_init(int baseclock, int rate); +void * ym2612_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, + UINT8 *IsVGMInit, int Options); void ym2612_shutdown(void *chip); void ym2612_reset_chip(void *chip); void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length); int ym2612_write(void *chip, int a,unsigned char v); unsigned char ym2612_read(void *chip,int a); +int ym2612_timer_over(void *chip, int c ); +void ym2612_postload(void *chip); void ym2612_set_mutemask(void *chip, UINT32 MuteMask); -void ym2612_setoptions(void *chip, UINT8 Flags); #endif /* (BUILD_YM2612||BUILD_YM3438) */ -#ifdef __cplusplus -} -#endif diff --git a/Frameworks/GME/gme/fm2612.c b/Frameworks/GME/vgmplay/chips/fm2612.c similarity index 93% rename from Frameworks/GME/gme/fm2612.c rename to Frameworks/GME/vgmplay/chips/fm2612.c index 72621f94c..44ff0fa70 100644 --- a/Frameworks/GME/gme/fm2612.c +++ b/Frameworks/GME/vgmplay/chips/fm2612.c @@ -13,7 +13,7 @@ /* ** History: ** -** 2006~2009 Eke-Eke (Genesis Plus GX): +** 2006~2012 Eke-Eke (Genesis Plus GX): ** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware ** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 ** @@ -26,7 +26,7 @@ ** ** - fixed LFO implementation: ** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) -** .modified LFO behavior when switched off (AM/PM current level is held) and on (LFO step is reseted): fixes intro in Spider-Man & Venom : Separation Anxiety +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) ** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. ** - improved internal timers emulation ** - adjusted lowest EG rates increment values @@ -131,10 +131,12 @@ //#include "emu.h" #include #include -#include "mathdefs.h" +#include #include "mamedef.h" #include "fm.h" +#define NULL ((void *)0) + /* shared function building option */ #define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438) #define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) @@ -385,7 +387,7 @@ static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 - (1.4 dB is loosing precision as you can see) + (1.4 dB is losing precision as you can see) It's implemented as generator from 0..126 with step 2 then a shift right N times, where N is: @@ -599,12 +601,14 @@ typedef struct UINT8 kcode; /* key code: */ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT8 Muted; + UINT8 *IsVGMInit; } FM_CH; typedef struct { //running_device *device; + void * param; /* this chip parameter */ double freqbase; /* frequency base */ int timer_prescaler; /* timer prescaler */ UINT8 irq; /* interrupt level */ @@ -626,6 +630,9 @@ typedef struct /* local time tables */ INT32 dt_tab[8][32]; /* DeTune table */ /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; + const ssg_callbacks *SSG; } FM_ST; @@ -676,8 +683,7 @@ typedef struct INT32 mem; /* one sample delay memory */ INT32 out_fm[6]; /* outputs of working channels */ - UINT8 LFOResetZero; - + UINT8 *IsVGMInit; } FM_OPN; /* here's the virtual YM2612 */ @@ -698,6 +704,8 @@ typedef struct UINT8 WaveOutMode; INT32 WaveL; INT32 WaveR; + + UINT8 PseudoSt; } YM2612; /* log output level */ @@ -729,6 +737,7 @@ INLINE void FM_STATUS_SET(FM_ST *ST,int flag) { ST->irq = 1; /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); } } @@ -741,6 +750,7 @@ INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) { ST->irq = 0; /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); } } @@ -757,7 +767,10 @@ INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; - if( !SLOT->key && !OPN->SL3.key_csm) + // Note by Valley Bell: + // I assume that the CSM mode shouldn't affect channels + // other than FM3, so I added a check for it here. + if( !SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) { /* restart Phase Generator */ SLOT->phase = 0; @@ -772,7 +785,7 @@ INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) else { /* force attenuation level to 0 */ - SLOT->volume = MIN_ATT_INDEX; + SLOT->volume = MIN_ATT_INDEX; /* directly switch to Decay (or Sustain) */ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; @@ -792,9 +805,15 @@ INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; - if (SLOT->key && !OPN->SL3.key_csm) + if (SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) { - if (SLOT->state>EG_REL) + if (*OPN->IsVGMInit) // workaround for VGMs trimmed with VGMTool + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else if (SLOT->state>EG_REL) { SLOT->state = EG_REL; /* phase -> Release */ @@ -859,7 +878,13 @@ INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) FM_SLOT *SLOT = &CH->SLOT[s]; if (!SLOT->key) { - if (SLOT->state>EG_REL) + if (*CH->IsVGMInit) + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else if (SLOT->state>EG_REL) { SLOT->state = EG_REL; /* phase -> Release */ @@ -885,7 +910,7 @@ INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) } /* OPN Mode Register Write */ -INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, int v ) +INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, void *n, int v ) { /* b7 = CSM MODE */ /* b6 = 3 slot mode */ @@ -913,44 +938,28 @@ INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, int v ) } } - /* reset Timer b flag */ + // reset Timer b flag if( v & 0x20 ) FM_STATUS_RESET(ST,0x02); - /* reset Timer a flag */ + // reset Timer a flag if( v & 0x10 ) FM_STATUS_RESET(ST,0x01); - /* load b */ - if( v & 0x02 ) + // load b + if ((v&2) && !(ST->mode&2)) { - if( ST->TBC == 0 ) - { - ST->TBC = ( 256-ST->TB)<<4; - /* External timer handler */ - } + ST->TBC = ( 256-ST->TB)<<4; + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock); } - else - { /* stop timer b */ - if( ST->TBC != 0 ) - { - ST->TBC = 0; - } - } - /* load a */ - if( v & 0x01 ) + // load a + if ((v&1) && !(ST->mode&1)) { - if( ST->TAC == 0 ) - { - ST->TAC = (1024-ST->TA); - /* External timer handler */ - } - } - else - { /* stop timer a */ - if( ST->TAC != 0 ) - { - ST->TAC = 0; - } + ST->TAC = (1024-ST->TA); + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock); + ST->TAC *= 4096; } + ST->mode = v; } @@ -962,6 +971,8 @@ INLINE void TimerAOver(FM_ST *ST) if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); /* clear or reload the counter */ ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock); + ST->TAC *= 4096; } /* Timer B Overflow */ INLINE void TimerBOver(FM_ST *ST) @@ -970,6 +981,7 @@ INLINE void TimerBOver(FM_ST *ST) if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); /* clear or reload the counter */ ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock); } @@ -1215,12 +1227,14 @@ INLINE void advance_lfo(FM_OPN *OPN) /* There are 128 LFO steps */ OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127; - /* triangle */ - /* AM: 0 to 126 step +2, 126 to 0 step -2 */ + // Valley Bell: Replaced old code (non-inverted triangle) with + // the one from Genesis Plus GX 1.71. + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ if (OPN->lfo_cnt<64) - OPN->LFO_AM = OPN->lfo_cnt * 2; + OPN->LFO_AM = (OPN->lfo_cnt ^ 63) << 1; else - OPN->LFO_AM = 126 - ((OPN->lfo_cnt&63) * 2); + OPN->LFO_AM = (OPN->lfo_cnt & 63) << 1; /* PM works with 4 times slower clock */ OPN->LFO_PM = OPN->lfo_cnt >> 2; @@ -1760,25 +1774,29 @@ static void OPNWriteMode(FM_OPN *OPN, int r, int v) case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ if (v&8) /* LFO enabled ? */ { - if (!OPN->lfo_timer_overflow) + /*if (!OPN->lfo_timer_overflow) { - /* restart LFO */ + // restart LFO OPN->lfo_cnt = 0; OPN->lfo_timer = 0; OPN->LFO_AM = 0; OPN->LFO_PM = 0; - } + }*/ OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH; } else { + // Valley Bell: Ported from Genesis Plus GX 1.71 + // hold LFO waveform in reset state OPN->lfo_timer_overflow = 0; - if (OPN->LFOResetZero) - { - OPN->LFO_AM = 0; - OPN->LFO_PM = 0; - } + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + + + OPN->LFO_PM = 0; + OPN->LFO_AM = 126; + //OPN->lfo_timer_overflow = 0; } break; case 0x24: /* timer A High 8*/ @@ -1791,7 +1809,7 @@ static void OPNWriteMode(FM_OPN *OPN, int r, int v) OPN->ST.TB = v; break; case 0x27: /* mode, timer control */ - set_timers( OPN, &(OPN->ST),v ); + set_timers( OPN, &(OPN->ST),OPN->ST.param,v ); break; case 0x28: /* key on / off */ c = v & 0x03; @@ -2055,8 +2073,6 @@ static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) { /* frequency base */ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; - if ( fabs( OPN->ST.freqbase - 1.0 ) < 0.0001 ) - OPN->ST.freqbase = 1.0; /* EG is updated every 3 samples */ OPN->eg_timer_add = (UINT32)((1<ST.freqbase); @@ -2068,6 +2084,9 @@ static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) /* Timer base time */ OPN->ST.timer_prescaler = timer_prescaler; + /* SSG part prescaler set */ + if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); + /* make time tables */ init_timetables(OPN, OPN->ST.freqbase); } @@ -2384,6 +2403,19 @@ void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length) /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ OPN->SL3.key_csm <<= 1; + /* timer A control */ + //INTERNAL_TIMER_A( &OPN->ST , cch[2] ) + { + if( OPN->ST.TAC && (OPN->ST.timer_handler==0) ) + if( (OPN->ST.TAC -= (int)(OPN->ST.freqbase*4096)) <= 0 ) + { + TimerAOver( &OPN->ST ); + // CSM mode total level latch and auto key on + if( OPN->ST.mode & 0x80 ) + CSMKeyControll( OPN, cch[2] ); + } + } + /* CSM Mode Key ON still disabled */ if (OPN->SL3.key_csm & 2) { @@ -2395,13 +2427,65 @@ void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length) OPN->SL3.key_csm = 0; } } + + /* timer B control */ +// INTERNAL_TIMER_B(&OPN->ST,length) } +#ifdef __STATE_H__ +void ym2612_postload(void *chip) +{ + if (chip) + { + YM2612 *F2612 = (YM2612 *)chip; + int r; + + /* DAC data & port */ + F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */ + F2612->dacen = F2612->REGS[0x2d] & 0x80; + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* channels */ + /*FM_channel_postload(F2612->CH,6);*/ + } +} + +static void YM2612_save_state(YM2612 *F2612, running_device *device) +{ + state_save_register_device_item_array(device, 0, F2612->REGS); + FMsave_state_st(device,&F2612->OPN.ST); + FMsave_state_channel(device,F2612->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2612->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2612->addr_A1); +} +#endif /* _STATE_H */ + /* initialize YM2612 emulator(s) */ //void * ym2612_init(void *param, running_device *device, int clock, int rate, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) -void * ym2612_init(int clock, int rate) +void * ym2612_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, + UINT8 *IsVGMInit, int Flags) { + int i; + YM2612 *F2612; /* allocate extend state space */ @@ -2413,6 +2497,7 @@ void * ym2612_init(int clock, int rate) /* allocate total level table (128kb space) */ init_tables(); + F2612->OPN.ST.param = param; F2612->OPN.type = TYPE_YM2612; F2612->OPN.P_CH = F2612->CH; //F2612->OPN.ST.device = device; @@ -2421,8 +2506,12 @@ void * ym2612_init(int clock, int rate) /* F2612->OPN.ST.irq = 0; */ /* F2612->OPN.ST.status = 0; */ /* Extend handler */ + F2612->OPN.ST.timer_handler = timer_handler; + F2612->OPN.ST.IRQ_Handler = IRQHandler; + + F2612->PseudoSt = (Flags >> 2) & 0x01; - if (rate > clock / 128) + if (F2612->PseudoSt) F2612->WaveOutMode = 0x01; else F2612->WaveOutMode = 0x03; @@ -2431,6 +2520,13 @@ void * ym2612_init(int clock, int rate) fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); FileSample = 0;*/ +#ifdef __STATE_H__ + YM2612_save_state(F2612, device); +#endif + F2612->OPN.IsVGMInit = IsVGMInit; + for (i = 0; i < 6; i++) + F2612->CH[i].IsVGMInit = IsVGMInit; + return F2612; } @@ -2463,7 +2559,7 @@ void ym2612_reset_chip(void *chip) OPN->lfo_timer = 0; OPN->lfo_cnt = 0; - OPN->LFO_AM = 0; + OPN->LFO_AM = 126; OPN->LFO_PM = 0; OPN->ST.TAC = 0; @@ -2535,6 +2631,7 @@ int ym2612_write(void *chip, int a, UINT8 v) switch( addr ) { case 0x2a: /* DAC data (YM2612) */ + ym2612_update_req(F2612->OPN.ST.param); F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */ break; case 0x2b: /* DAC Sel (YM2612) */ @@ -2546,11 +2643,13 @@ int ym2612_write(void *chip, int a, UINT8 v) F2612->dac_test = v & 0x20; break; default: /* OPN section */ + ym2612_update_req(F2612->OPN.ST.param); /* write register */ OPNWriteMode(&(F2612->OPN),addr,v); } break; default: /* 0x30-0xff OPN section */ + ym2612_update_req(F2612->OPN.ST.param); /* write register */ OPNWriteReg(&(F2612->OPN),addr,v); } @@ -2567,6 +2666,7 @@ int ym2612_write(void *chip, int a, UINT8 v) addr = F2612->OPN.ST.address; F2612->REGS[addr | 0x100] = v; + ym2612_update_req(F2612->OPN.ST.param); OPNWriteReg(&(F2612->OPN),addr | 0x100,v); break; } @@ -2600,6 +2700,7 @@ int ym2612_timer_over(void *chip,int c) } else { /* Timer A */ + ym2612_update_req(F2612->OPN.ST.param); /* timer update */ TimerAOver( &(F2612->OPN.ST) ); /* CSM mode key,TL controll */ @@ -2624,11 +2725,4 @@ void ym2612_set_mutemask(void *chip, UINT32 MuteMask) return; } -void ym2612_setoptions(void *chip, UINT8 Flags) -{ - YM2612 *F2612 = (YM2612 *)chip; - F2612->OPN.LFOResetZero = (Flags >> 2) & 0x01; - - return; -} #endif /* (BUILD_YM2612||BUILD_YM3238) */ diff --git a/Frameworks/GME/gme/fmopl.cpp b/Frameworks/GME/vgmplay/chips/fmopl.c similarity index 85% rename from Frameworks/GME/gme/fmopl.cpp rename to Frameworks/GME/vgmplay/chips/fmopl.c index 3caf43733..4a5e143c7 100644 --- a/Frameworks/GME/gme/fmopl.cpp +++ b/Frameworks/GME/vgmplay/chips/fmopl.c @@ -1,2621 +1,2742 @@ -/* -** -** File: fmopl.c - software implementation of FM sound generator -** types OPL and OPL2 -** -** Copyright Jarek Burczynski (bujar at mame dot net) -** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development -** -** Version 0.72 -** - -Revision History: - -04-08-2003 Jarek Burczynski: - - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip - handles memory read/write or during the adpcm synthesis when the chip - requests another byte of ADPCM data. - -24-07-2003 Jarek Burczynski: - - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after - some (unknown) delay). Right now it's always set. - -14-06-2003 Jarek Burczynski: - - implemented all of the status register flags in Y8950 emulation - - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since - they can be either RAM or ROM - -08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) - - corrected ym3526_read() to always set bit 2 and bit 1 - to HIGH state - identical to ym3812_read (verified on real YM3526) - -04-28-2002 Jarek Burczynski: - - binary exact Envelope Generator (verified on real YM3812); - compared to YM2151: the EG clock is equal to internal_clock, - rates are 2 times slower and volume resolution is one bit less - - modified interface functions (they no longer return pointer - - that's internal to the emulator now): - - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() - - corrected 'off by one' error in feedback calculations (when feedback is off) - - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) - - speeded up noise generator calculations (Nicola Salmoria) - -03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) - Complete rewrite (all verified on real YM3812): - - corrected sin_tab and tl_tab data - - corrected operator output calculations - - corrected waveform_select_enable register; - simply: ignore all writes to waveform_select register when - waveform_select_enable == 0 and do not change the waveform previously selected. - - corrected KSR handling - - corrected Envelope Generator: attack shape, Sustain mode and - Percussive/Non-percussive modes handling - - Envelope Generator rates are two times slower now - - LFO amplitude (tremolo) and phase modulation (vibrato) - - rhythm sounds phase generation - - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) - - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) - - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) - -12-28-2001 Acho A. Tang - - reflected Delta-T EOS status on Y8950 status port. - - fixed subscription range of attack/decay tables - - - To do: - add delay before key off in CSM mode (see CSMKeyControll) - verify volume of the FM part on the Y8950 -*/ - -#include -#include -#define _USE_MATH_DEFINES -#include -#include "fmopl.h" -#include "ymdeltat.h" - -#ifndef INLINE -#define INLINE __inline -#endif -#ifndef NULL - #define NULL ((void *)0) -#endif -#ifndef logerror -#define logerror (void) -#endif - -#ifndef M_PI - #define M_PI 3.14159265358979323846 -#endif - -/* output final shift */ -#if (OPL_SAMPLE_BITS==16) - #define FINAL_SH (0) - #define MAXOUT (+32767) - #define MINOUT (-32768) -#else - #define FINAL_SH (8) - #define MAXOUT (+127) - #define MINOUT (-128) -#endif - - -#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define EG_SH 16 /* 16.16 fixed point (EG timing) */ -#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ -#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ - -#define FREQ_MASK ((1<=0) - { - if (value < 0x0200) - return (value & ~0); - if (value < 0x0400) - return (value & ~1); - if (value < 0x0800) - return (value & ~3); - if (value < 0x1000) - return (value & ~7); - if (value < 0x2000) - return (value & ~15); - if (value < 0x4000) - return (value & ~31); - return (value & ~63); - } - /*else value < 0*/ - if (value > -0x0200) - return (~abs(value) & ~0); - if (value > -0x0400) - return (~abs(value) & ~1); - if (value > -0x0800) - return (~abs(value) & ~3); - if (value > -0x1000) - return (~abs(value) & ~7); - if (value > -0x2000) - return (~abs(value) & ~15); - if (value > -0x4000) - return (~abs(value) & ~31); - return (~abs(value) & ~63); -} - - -static FILE *sample[1]; - #if 1 /*save to MONO file */ - #define SAVE_ALL_CHANNELS \ - { signed int pom = acc_calc(lt); \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - } - #else /*save to STEREO file */ - #define SAVE_ALL_CHANNELS \ - { signed int pom = lt; \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - pom = rt; \ - fputc((unsigned short)pom&0xff,sample[0]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ - } - #endif -#endif - -#define LOG_CYM_FILE 0 -//static FILE * cymfile = NULL; - - - -#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ -#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ -#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ -#define OPL_TYPE_IO 0x08 /* I/O port */ - -/* ---------- Generic interface section ---------- */ -#define OPL_TYPE_YM3526 (0) -#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) -#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) - - - -typedef struct{ - UINT32 ar; /* attack rate: AR<<2 */ - UINT32 dr; /* decay rate: DR<<2 */ - UINT32 rr; /* release rate:RR<<2 */ - UINT8 KSR; /* key scale rate */ - UINT8 ksl; /* keyscale level */ - UINT8 ksr; /* key scale rate: kcode>>KSR */ - UINT8 mul; /* multiple: mul_tab[ML] */ - - /* Phase Generator */ - UINT32 Cnt; /* frequency counter */ - UINT32 Incr; /* frequency counter step */ - UINT8 FB; /* feedback shift value */ - INT32 *connect1; /* slot1 output pointer */ - INT32 op1_out[2]; /* slot1 output for feedback */ - UINT8 CON; /* connection (algorithm) type */ - - /* Envelope Generator */ - UINT8 eg_type; /* percussive/non-percussive mode */ - UINT8 state; /* phase type */ - UINT32 TL; /* total level: TL << 2 */ - INT32 TLL; /* adjusted now TL */ - INT32 volume; /* envelope counter */ - UINT32 sl; /* sustain level: sl_tab[SL] */ - UINT8 eg_sh_ar; /* (attack state) */ - UINT8 eg_sel_ar; /* (attack state) */ - UINT8 eg_sh_dr; /* (decay state) */ - UINT8 eg_sel_dr; /* (decay state) */ - UINT8 eg_sh_rr; /* (release state) */ - UINT8 eg_sel_rr; /* (release state) */ - UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ - - /* LFO */ - UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ - UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ - - /* waveform select */ - UINT16 wavetable; -} OPL_SLOT; - -typedef struct{ - OPL_SLOT SLOT[2]; - /* phase generator state */ - UINT32 block_fnum; /* block+fnum */ - UINT32 fc; /* Freq. Increment base */ - UINT32 ksl_base; /* KeyScaleLevel Base step */ - UINT8 kcode; /* key code (for key scaling) */ -} OPL_CH; - -/* OPL state */ -typedef struct fm_opl_f { - /* FM channel slots */ - OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ - - UINT32 eg_cnt; /* global envelope generator counter */ - UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ - UINT32 eg_timer_add; /* step of eg_timer */ - UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ - - UINT8 rhythm; /* Rhythm mode */ - - UINT32 fn_tab[1024]; /* fnumber->increment counter */ - - /* LFO */ - UINT8 lfo_am_depth; - UINT8 lfo_pm_depth_range; - UINT32 lfo_am_cnt; - UINT32 lfo_am_inc; - UINT32 lfo_pm_cnt; - UINT32 lfo_pm_inc; - - UINT32 noise_rng; /* 23 bit noise shift register */ - UINT32 noise_p; /* current noise 'phase' */ - UINT32 noise_f; /* current noise period */ - - UINT8 wavesel; /* waveform select enable flag */ - - UINT32 T[2]; /* timer counters */ - UINT8 st[2]; /* timer enable */ - -#if BUILD_Y8950 - /* Delta-T ADPCM unit (Y8950) */ - - YM_DELTAT *deltat; - - /* Keyboard and I/O ports interface */ - UINT8 portDirection; - UINT8 portLatch; - OPL_PORTHANDLER_R porthandler_r; - OPL_PORTHANDLER_W porthandler_w; - void * port_param; - OPL_PORTHANDLER_R keyboardhandler_r; - OPL_PORTHANDLER_W keyboardhandler_w; - void * keyboard_param; -#endif - - /* external event callback handlers */ - //OPL_TIMERHANDLER timer_handler; /* TIMER handler */ - void *TimerParam; /* TIMER parameter */ - OPL_IRQHANDLER IRQHandler; /* IRQ handler */ - void *IRQParam; /* IRQ parameter */ - OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ - void *UpdateParam; /* stream update parameter */ - - UINT8 type; /* chip type */ - UINT8 address; /* address register */ - UINT8 status; /* status flag */ - UINT8 statusmask; /* status mask */ - UINT8 mode; /* Reg.08 : CSM,notesel,etc. */ - - UINT32 clock; /* master clock (Hz) */ - UINT32 rate; /* sampling rate (Hz) */ - double freqbase; /* frequency base */ - //attotime TimerBase; /* Timer base time (==sampling time)*/ - - OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2; - - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - signed int output[1]; - -#if BUILD_Y8950 - INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ -#endif - - UINT32 LFO_AM; - INT32 LFO_PM; -} FM_OPL; - - - -/* mapping of register number (offset) to slot number used by the emulator */ -static const int slot_array[32]= -{ - 0, 2, 4, 1, 3, 5,-1,-1, - 6, 8,10, 7, 9,11,-1,-1, - 12,14,16,13,15,17,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1 -}; - -/* key scale level */ -/* table is 3dB/octave , DV converts this into 6dB/octave */ -/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ -#define DV (0.1875/2.0) -static const UINT32 ksl_tab[8*16]= -{ - /* OCT 0 */ - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - /* OCT 1 */ - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - UINT32( 0.000/DV), UINT32( 0.750/DV), UINT32( 1.125/DV), UINT32( 1.500/DV), - UINT32( 1.875/DV), UINT32( 2.250/DV), UINT32( 2.625/DV), UINT32( 3.000/DV), - /* OCT 2 */ - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), - UINT32( 0.000/DV), UINT32( 1.125/DV), UINT32( 1.875/DV), UINT32( 2.625/DV), - UINT32( 3.000/DV), UINT32( 3.750/DV), UINT32( 4.125/DV), UINT32( 4.500/DV), - UINT32( 4.875/DV), UINT32( 5.250/DV), UINT32( 5.625/DV), UINT32( 6.000/DV), - /* OCT 3 */ - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 1.875/DV), - UINT32( 3.000/DV), UINT32( 4.125/DV), UINT32( 4.875/DV), UINT32( 5.625/DV), - UINT32( 6.000/DV), UINT32( 6.750/DV), UINT32( 7.125/DV), UINT32( 7.500/DV), - UINT32( 7.875/DV), UINT32( 8.250/DV), UINT32( 8.625/DV), UINT32( 9.000/DV), - /* OCT 4 */ - UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 3.000/DV), UINT32( 4.875/DV), - UINT32( 6.000/DV), UINT32( 7.125/DV), UINT32( 7.875/DV), UINT32( 8.625/DV), - UINT32( 9.000/DV), UINT32( 9.750/DV), UINT32(10.125/DV), UINT32(10.500/DV), - UINT32(10.875/DV), UINT32(11.250/DV), UINT32(11.625/DV), UINT32(12.000/DV), - /* OCT 5 */ - UINT32( 0.000/DV), UINT32( 3.000/DV), UINT32( 6.000/DV), UINT32( 7.875/DV), - UINT32( 9.000/DV), UINT32(10.125/DV), UINT32(10.875/DV), UINT32(11.625/DV), - UINT32(12.000/DV), UINT32(12.750/DV), UINT32(13.125/DV), UINT32(13.500/DV), - UINT32(13.875/DV), UINT32(14.250/DV), UINT32(14.625/DV), UINT32(15.000/DV), - /* OCT 6 */ - UINT32( 0.000/DV), UINT32( 6.000/DV), UINT32( 9.000/DV), UINT32(10.875/DV), - UINT32(12.000/DV), UINT32(13.125/DV), UINT32(13.875/DV), UINT32(14.625/DV), - UINT32(15.000/DV), UINT32(15.750/DV), UINT32(16.125/DV), UINT32(16.500/DV), - UINT32(16.875/DV), UINT32(17.250/DV), UINT32(17.625/DV), UINT32(18.000/DV), - /* OCT 7 */ - UINT32( 0.000/DV), UINT32( 9.000/DV), UINT32(12.000/DV), UINT32(13.875/DV), - UINT32(15.000/DV), UINT32(16.125/DV), UINT32(16.875/DV), UINT32(17.625/DV), - UINT32(18.000/DV), UINT32(18.750/DV), UINT32(19.125/DV), UINT32(19.500/DV), - UINT32(19.875/DV), UINT32(20.250/DV), UINT32(20.625/DV), UINT32(21.000/DV) -}; -#undef DV - -/* sustain level table (3dB per step) */ -/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ -#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) ) -static const UINT32 sl_tab[16]={ - SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), - SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) -}; -#undef SC - - -#define RATE_STEPS (8) -static const unsigned char eg_inc[15*RATE_STEPS]={ - -/*cycle:0 1 2 3 4 5 6 7*/ - -/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ -/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ -/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ -/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ - -/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ -/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ -/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ -/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ - -/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ -/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ -/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ -/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ - -/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ -/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ -/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ -}; - - -#define O(a) (a*RATE_STEPS) - -/*note that there is no O(13) in this table - it's directly in the code */ -static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), - -/* rates 00-12 */ -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), - -/* rate 13 */ -O( 4),O( 5),O( 6),O( 7), - -/* rate 14 */ -O( 8),O( 9),O(10),O(11), - -/* rate 15 */ -O(12),O(12),O(12),O(12), - -/* 16 dummy rates (same as 15 3) */ -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), - -}; -#undef O - -/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ -/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ -/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ - -#define O(a) (a*1) -static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), - -/* rates 00-12 */ -O(12),O(12),O(12),O(12), -O(11),O(11),O(11),O(11), -O(10),O(10),O(10),O(10), -O( 9),O( 9),O( 9),O( 9), -O( 8),O( 8),O( 8),O( 8), -O( 7),O( 7),O( 7),O( 7), -O( 6),O( 6),O( 6),O( 6), -O( 5),O( 5),O( 5),O( 5), -O( 4),O( 4),O( 4),O( 4), -O( 3),O( 3),O( 3),O( 3), -O( 2),O( 2),O( 2),O( 2), -O( 1),O( 1),O( 1),O( 1), -O( 0),O( 0),O( 0),O( 0), - -/* rate 13 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 14 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 15 */ -O( 0),O( 0),O( 0),O( 0), - -/* 16 dummy rates (same as 15 3) */ -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), - -}; -#undef O - - -/* multiple table */ -#define ML 2 -static const UINT8 mul_tab[16]= { -/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ - UINT8( 0.50*ML),UINT8( 1.00*ML),UINT8( 2.00*ML),UINT8( 3.00*ML),UINT8( 4.00*ML),UINT8( 5.00*ML),UINT8( 6.00*ML),UINT8( 7.00*ML), - UINT8( 8.00*ML),UINT8( 9.00*ML),UINT8(10.00*ML),UINT8(10.00*ML),UINT8(12.00*ML),UINT8(12.00*ML),UINT8(15.00*ML),UINT8(15.00*ML) -}; -#undef ML - -/* TL_TAB_LEN is calculated as: -* 12 - sinus amplitude bits (Y axis) -* 2 - sinus sign bit (Y axis) -* TL_RES_LEN - sinus resolution (X axis) -*/ -#define TL_TAB_LEN (12*2*TL_RES_LEN) -static signed int tl_tab[TL_TAB_LEN]; - -#define ENV_QUIET (TL_TAB_LEN>>4) - -/* sin waveform table in 'decibel' scale */ -/* four waveforms on OPL2 type chips */ -static unsigned int sin_tab[SIN_LEN * 4]; - - -/* LFO Amplitude Modulation table (verified on real YM3812) - 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples - - Length: 210 elements. - - Each of the elements has to be repeated - exactly 64 times (on 64 consecutive samples). - The whole table takes: 64 * 210 = 13440 samples. - - When AM = 1 data is used directly - When AM = 0 data is divided by 4 before being used (loosing precision is important) -*/ - -#define LFO_AM_TAB_ELEMENTS 210 - -static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { -0,0,0,0,0,0,0, -1,1,1,1, -2,2,2,2, -3,3,3,3, -4,4,4,4, -5,5,5,5, -6,6,6,6, -7,7,7,7, -8,8,8,8, -9,9,9,9, -10,10,10,10, -11,11,11,11, -12,12,12,12, -13,13,13,13, -14,14,14,14, -15,15,15,15, -16,16,16,16, -17,17,17,17, -18,18,18,18, -19,19,19,19, -20,20,20,20, -21,21,21,21, -22,22,22,22, -23,23,23,23, -24,24,24,24, -25,25,25,25, -26,26,26, -25,25,25,25, -24,24,24,24, -23,23,23,23, -22,22,22,22, -21,21,21,21, -20,20,20,20, -19,19,19,19, -18,18,18,18, -17,17,17,17, -16,16,16,16, -15,15,15,15, -14,14,14,14, -13,13,13,13, -12,12,12,12, -11,11,11,11, -10,10,10,10, -9,9,9,9, -8,8,8,8, -7,7,7,7, -6,6,6,6, -5,5,5,5, -4,4,4,4, -3,3,3,3, -2,2,2,2, -1,1,1,1 -}; - -/* LFO Phase Modulation table (verified on real YM3812) */ -static const INT8 lfo_pm_table[8*8*2] = { - -/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ -0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ -1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ -4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ -2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ -5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ -6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ - -/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ -3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ -7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ -}; - - -/* lock level of common table */ -//static int num_lock = 0; - - -//static void *cur_chip = NULL; /* current chip pointer */ - -INLINE int limit( int val, int max, int min ) { - if ( val > max ) - val = max; - else if ( val < min ) - val = min; - - return val; -} - - -/* status set and IRQ handling */ -INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) -{ - /* set status flag */ - OPL->status |= flag; - if(!(OPL->status & 0x80)) - { - if(OPL->status & OPL->statusmask) - { /* IRQ on */ - OPL->status |= 0x80; - /* callback user interrupt handler (IRQ is OFF to ON) */ - if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); - } - } -} - -/* status reset and IRQ handling */ -INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) -{ - /* reset status flag */ - OPL->status &=~flag; - if((OPL->status & 0x80)) - { - if (!(OPL->status & OPL->statusmask) ) - { - OPL->status &= 0x7f; - /* callback user interrupt handler (IRQ is ON to OFF) */ - if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); - } - } -} - -/* IRQ mask set */ -INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) -{ - OPL->statusmask = flag; - /* IRQ handling check */ - OPL_STATUS_SET(OPL,0); - OPL_STATUS_RESET(OPL,0); -} - - -/* advance LFO to next sample */ -INLINE void advance_lfo(FM_OPL *OPL) -{ - UINT8 tmp; - - /* LFO */ - OPL->lfo_am_cnt += OPL->lfo_am_inc; - if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; - - if (OPL->lfo_am_depth) - OPL->LFO_AM = tmp; - else - OPL->LFO_AM = tmp>>2; - - OPL->lfo_pm_cnt += OPL->lfo_pm_inc; - OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; -} - -/* advance to next sample */ -INLINE void advance(FM_OPL *OPL) -{ - OPL_CH *CH; - OPL_SLOT *op; - int i; - - OPL->eg_timer += OPL->eg_timer_add; - - while (OPL->eg_timer >= OPL->eg_timer_overflow) - { - OPL->eg_timer -= OPL->eg_timer_overflow; - - OPL->eg_cnt++; - - for (i=0; i<9*2; i++) - { - CH = &OPL->P_CH[i/2]; - op = &CH->SLOT[i&1]; - - /* Envelope Generator */ - switch(op->state) - { - case EG_ATT: /* attack phase */ - if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) - { - op->volume += (~op->volume * - (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) - ) >>3; - - if (op->volume <= MIN_ATT_INDEX) - { - op->volume = MIN_ATT_INDEX; - op->state = EG_DEC; - } - - } - break; - - case EG_DEC: /* decay phase */ - if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; - - if ( op->volume >= op->sl ) - op->state = EG_SUS; - - } - break; - - case EG_SUS: /* sustain phase */ - - /* this is important behaviour: - one can change percusive/non-percussive modes on the fly and - the chip will remain in sustain phase - verified on real YM3812 */ - - if(op->eg_type) /* non-percussive mode */ - { - /* do nothing */ - } - else /* percussive mode */ - { - /* during sustain phase chip adds Release Rate (in percussive mode) */ - if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - op->volume = MAX_ATT_INDEX; - } - /* else do nothing in sustain phase */ - } - break; - - case EG_REL: /* release phase */ - if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - - } - break; - - default: - break; - } - } - } - - for (i=0; i<9*2; i++) - { - CH = &OPL->P_CH[i/2]; - op = &CH->SLOT[i&1]; - - /* Phase Generator */ - if(op->vib) - { - UINT8 block; - unsigned int block_fnum = CH->block_fnum; - - unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; - - signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ]; - - if (lfo_fn_table_index_offset) /* LFO phase modulation active */ - { - block_fnum += lfo_fn_table_index_offset; - block = (block_fnum&0x1c00) >> 10; - op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; - } - else /* LFO phase modulation = zero */ - { - op->Cnt += op->Incr; - } - } - else /* LFO phase modulation disabled for this operator */ - { - op->Cnt += op->Incr; - } - } - - /* The Noise Generator of the YM3812 is 23-bit shift register. - * Period is equal to 2^23-2 samples. - * Register works at sampling frequency of the chip, so output - * can change on every sample. - * - * Output of the register and input to the bit 22 is: - * bit0 XOR bit14 XOR bit15 XOR bit22 - * - * Simply use bit 22 as the noise output. - */ - - OPL->noise_p += OPL->noise_f; - i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ - OPL->noise_p &= FREQ_MASK; - while (i) - { - /* - UINT32 j; - j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; - OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); - */ - - /* - Instead of doing all the logic operations above, we - use a trick here (and use bit 0 as the noise output). - The difference is only that the noise bit changes one - step ahead. This doesn't matter since we don't know - what is real state of the noise_rng after the reset. - */ - - if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; - OPL->noise_rng >>= 1; - - i--; - } -} - - -INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) -{ - UINT32 p; - - p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; -} - -INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) -{ - UINT32 p; - - p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; -} - - -#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask)) - -/* calculate output */ -INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH ) -{ - OPL_SLOT *SLOT; - unsigned int env; - signed int out; - - OPL->phase_modulation = 0; - - /* SLOT 1 */ - SLOT = &CH->SLOT[SLOT1]; - env = volume_calc(SLOT); - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - *SLOT->connect1 += SLOT->op1_out[0]; - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->FB) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable); -} - -/* - operators used in the rhythm sounds generation process: - - Envelope Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal - 6 / 0 12 50 70 90 f0 + - 6 / 1 15 53 73 93 f3 + - 7 / 0 13 51 71 91 f1 + - 7 / 1 16 54 74 94 f4 + - 8 / 0 14 52 72 92 f2 + - 8 / 1 17 55 75 95 f5 + - - Phase Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number MULTIPLE Drum Hat Drum Tom Cymbal - 6 / 0 12 30 + - 6 / 1 15 33 + - 7 / 0 13 31 + + + - 7 / 1 16 34 ----- n o t u s e d ----- - 8 / 0 14 32 + - 8 / 1 17 35 + + - -channel operator register number Bass High Snare Tom Top -number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal - 6 12,15 B6 A6 + - - 7 13,16 B7 A7 + + + - - 8 14,17 B8 A8 + + + - -*/ - -/* calculate rhythm */ - -INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise ) -{ - OPL_SLOT *SLOT; - signed int out; - unsigned int env; - - - /* Bass Drum (verified on real YM3812): - - depends on the channel 6 'connect' register: - when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) - when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - - output sample always is multiplied by 2 - */ - - OPL->phase_modulation = 0; - /* SLOT 1 */ - SLOT = &CH[6].SLOT[SLOT1]; - env = volume_calc(SLOT); - - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - - if (!SLOT->CON) - OPL->phase_modulation = SLOT->op1_out[0]; - /* else ignore output of operator 1 */ - - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->FB) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2; - - - /* Phase generation is based on: */ - /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ - /* SD (16) channel 7->slot 1 */ - /* TOM (14) channel 8->slot 1 */ - /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ - - /* Envelope generation based on: */ - /* HH channel 7->slot1 */ - /* SD channel 7->slot2 */ - /* TOM channel 8->slot1 */ - /* TOP channel 8->slot2 */ - - - /* The following formulas can be well optimized. - I leave them in direct form for now (in case I've missed something). - */ - - /* High Hat (verified on real YM3812) */ - env = volume_calc(OPL->SLOT7_1); - if( env < ENV_QUIET ) - { - - /* high hat phase generation: - phase = d0 or 234 (based on frequency only) - phase = 34 or 2d0 (based on noise) - */ - - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>2)&1; - - unsigned char res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0xd0; */ - /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ - UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>3)&1; - - unsigned char res2 = (bit3e ^ bit5e); - - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ - if (res2) - phase = (0x200|(0xd0>>2)); - - - /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ - /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ - if (phase&0x200) - { - if (noise) - phase = 0x200|0xd0; - } - else - /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ - /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ - { - if (noise) - phase = 0xd0>>2; - } - - OPL->output[0] += op_calc(phase<SLOT7_1->wavetable) * 2; - } - - /* Snare Drum (verified on real YM3812) */ - env = volume_calc(OPL->SLOT7_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit8 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>8)&1; - - /* when bit8 = 0 phase = 0x100; */ - /* when bit8 = 1 phase = 0x200; */ - UINT32 phase = bit8 ? 0x200 : 0x100; - - /* Noise bit XOR'es phase by 0x100 */ - /* when noisebit = 0 pass the phase from calculation above */ - /* when noisebit = 1 phase ^= 0x100; */ - /* in other words: phase ^= (noisebit<<8); */ - if (noise) - phase ^= 0x100; - - OPL->output[0] += op_calc(phase<SLOT7_2->wavetable) * 2; - } - - /* Tom Tom (verified on real YM3812) */ - env = volume_calc(OPL->SLOT8_1); - if( env < ENV_QUIET ) - OPL->output[0] += op_calc(OPL->SLOT8_1->Cnt, env, 0, OPL->SLOT8_1->wavetable) * 2; - - /* Top Cymbal (verified on real YM3812) */ - env = volume_calc(OPL->SLOT8_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>2)&1; - - unsigned char res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0x100; */ - /* when res1 = 1 phase = 0x200 | 0x100; */ - UINT32 phase = res1 ? 0x300 : 0x100; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>3)&1; - - unsigned char res2 = (bit3e ^ bit5e); - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | 0x100; */ - if (res2) - phase = 0x300; - - OPL->output[0] += op_calc(phase<SLOT8_2->wavetable) * 2; - } - -} - - -/* generic table initialize */ -static int init_tables(void) -{ - signed int i,x; - signed int n; - double o,m; - - - for (x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - n <<= 1; /* 12 bits here (as in real chip) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; - - for (i=1; i<12; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; - } - #if 0 - logerror("tl %04i", x*2); - for (i=0; i<12; i++) - logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); - logerror("\n"); - #endif - } - /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ - - - for (i=0; i0.0) - o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ - else - o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ - - o = o / (ENV_STEP/4); - - n = (int)(2.0*o); - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); - - /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ - } - - for (i=0; i>1) ]; - - /* waveform 3: _ _ _ _ */ - /* / |_/ |_/ |_/ |_*/ - /* abs(output only first quarter of the sinus waveform) */ - - if (i & (1<<(SIN_BITS-2)) ) - sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; - - /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); - logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); - logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ - } - /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ - - -#ifdef SAVE_SAMPLE - sample[0]=fopen("sampsum.pcm","wb"); -#endif - - return 1; -} - -static void OPLCloseTable( void ) -{ -#ifdef SAVE_SAMPLE - fclose(sample[0]); -#endif -} - - - -static void OPL_initalize(FM_OPL *OPL) -{ - int i; - - /* frequency base */ - OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0; -#if 0 - OPL->rate = (double)OPL->clock / 72.0; - OPL->freqbase = 1.0; -#endif - - /*logerror("freqbase=%f\n", OPL->freqbase);*/ - - /* Timer base time */ - //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72); - - /* make fnumber -> increment counter table */ - for( i=0 ; i < 1024 ; i++ ) - { - /* opn phase increment counter = 20bit */ - OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ -#if 0 - logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", - i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 ); -#endif - } - -#if 0 - for( i=0 ; i < 16 ; i++ ) - { - logerror("FMOPL.C: sl_tab[%i] = %08x\n", - i, sl_tab[i] ); - } - for( i=0 ; i < 8 ; i++ ) - { - int j; - logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); - for (j=0; j<16; j++) - { - logerror("%08x ", ksl_tab[i*16+j] ); - } - logerror("\n"); - } -#endif - - - /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ - /* One entry from LFO_AM_TABLE lasts for 64 samples */ - OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; - - /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ - OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; - - /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/ - - /* Noise generator: a step takes 1 sample */ - OPL->noise_f = (1.0 / 1.0) * (1<freqbase; - - OPL->eg_timer_add = (1<freqbase; - OPL->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, OPL->eg_timer_overflow);*/ - -} - -INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set) -{ - if( !SLOT->key ) - { - /* restart Phase Generator */ - SLOT->Cnt = 0; - /* phase -> Attack */ - SLOT->state = EG_ATT; - } - SLOT->key |= key_set; -} - -INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr) -{ - if( SLOT->key ) - { - SLOT->key &= key_clr; - - if( !SLOT->key ) - { - /* phase -> Release */ - if (SLOT->state>EG_REL) - SLOT->state = EG_REL; - } - } -} - -/* update phase increment counter of operator (also update the EG rates if necessary) */ -INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) -{ - int ksr; - - /* (frequency) phase increment counter */ - SLOT->Incr = CH->fc * SLOT->mul; - ksr = CH->kcode >> SLOT->KSR; - - if( SLOT->ksr != ksr ) - { - SLOT->ksr = ksr; - - /* calculate envelope generator rates */ - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; - } -} - -/* set multi,am,vib,EG-TYP,KSR,mul */ -INLINE void set_mul(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->mul = mul_tab[v&0x0f]; - SLOT->KSR = (v&0x10) ? 0 : 2; - SLOT->eg_type = (v&0x20); - SLOT->vib = (v&0x40); - SLOT->AMmask = (v&0x80) ? ~0 : 0; - CALC_FCSLOT(CH,SLOT); -} - -/* set ksl & tl */ -INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - int ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */ - - SLOT->ksl = ksl ? 3-ksl : 31; - SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ - - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); -} - -/* set attack rate & decay rate */ -INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; - - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - - SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; -} - -/* set sustain level & release rate */ -INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->sl = sl_tab[ v>>4 ]; - - SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; -} - - -/* write a value v to register r on OPL chip */ -static void OPLWriteReg(FM_OPL *OPL, int r, int v) -{ - OPL_CH *CH; - int slot; - UINT32 block_fnum; - - - /* adjust bus to 8 bits */ - r &= 0xff; - v &= 0xff; - - /*if (LOG_CYM_FILE && (cymfile) && (r!=0) ) - { - fputc( (unsigned char)r, cymfile ); - fputc( (unsigned char)v, cymfile ); - }*/ - - - switch(r&0xe0) - { - case 0x00: /* 00-1f:control */ - switch(r&0x1f) - { - case 0x01: /* waveform select enable */ - if(OPL->type&OPL_TYPE_WAVESEL) - { - OPL->wavesel = v&0x20; - /* do not change the waveform previously selected */ - } - break; - case 0x02: /* Timer 1 */ - OPL->T[0] = (256-v)*4; - break; - case 0x03: /* Timer 2 */ - OPL->T[1] = (256-v)*16; - break; - case 0x04: /* IRQ clear / mask and Timer enable */ - if(v&0x80) - { /* IRQ flag clear */ - OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */ - } - else - { /* set IRQ mask ,timer enable*/ - /*UINT8 st1 = v&1; - UINT8 st2 = (v>>1)&1;*/ - - /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ - OPL_STATUS_RESET(OPL, v & (0x78-0x08) ); - OPL_STATUSMASK_SET(OPL, (~v) & 0x78 ); - - /* timer 2 */ - /*if(OPL->st[1] != st2) - { - attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero; - OPL->st[1] = st2; - if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period); - }*/ - /* timer 1 */ - /*if(OPL->st[0] != st1) - { - attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero; - OPL->st[0] = st1; - if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period); - }*/ - } - break; -#if BUILD_Y8950 - case 0x06: /* Key Board OUT */ - if(OPL->type&OPL_TYPE_KEYBOARD) - { - if(OPL->keyboardhandler_w) - OPL->keyboardhandler_w(OPL->keyboard_param,v); - /*else - logerror("Y8950: write unmapped KEYBOARD port\n");*/ - } - break; - case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ - if(OPL->type&OPL_TYPE_ADPCM) - YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); - break; -#endif - case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ - OPL->mode = v; -#if BUILD_Y8950 - if(OPL->type&OPL_TYPE_ADPCM) - YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ -#endif - break; - -#if BUILD_Y8950 - case 0x09: /* START ADD */ - case 0x0a: - case 0x0b: /* STOP ADD */ - case 0x0c: - case 0x0d: /* PRESCALE */ - case 0x0e: - case 0x0f: /* ADPCM data write */ - case 0x10: /* DELTA-N */ - case 0x11: /* DELTA-N */ - case 0x12: /* ADPCM volume */ - if(OPL->type&OPL_TYPE_ADPCM) - YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); - break; - - case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ - case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ - case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ - /*logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);*/ - break; - - case 0x18: /* I/O CTRL (Direction) */ - if(OPL->type&OPL_TYPE_IO) - OPL->portDirection = v&0x0f; - break; - case 0x19: /* I/O DATA */ - if(OPL->type&OPL_TYPE_IO) - { - OPL->portLatch = v; - if(OPL->porthandler_w) - OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); - } - break; -#endif - default: - /*logerror("FMOPL.C: write to unknown register: %02x\n",r);*/ - break; - } - break; - case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_mul(OPL,slot,v); - break; - case 0x40: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_ksl_tl(OPL,slot,v); - break; - case 0x60: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_ar_dr(OPL,slot,v); - break; - case 0x80: - slot = slot_array[r&0x1f]; - if(slot < 0) return; - set_sl_rr(OPL,slot,v); - break; - case 0xa0: - if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ - { - OPL->lfo_am_depth = v & 0x80; - OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0; - - OPL->rhythm = v&0x3f; - - if(OPL->rhythm&0x20) - { - /* BD key on/off */ - if(v&0x10) - { - FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2); - FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2); - } - else - { - FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); - FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); - } - /* HH key on/off */ - if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2); - else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); - /* SD key on/off */ - if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2); - else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); - /* TOM key on/off */ - if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2); - else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY key on/off */ - if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2); - else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); - } - else - { - /* BD key off */ - FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); - FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); - /* HH key off */ - FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); - /* SD key off */ - FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); - /* TOM key off */ - FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY off */ - FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); - } - return; - } - /* keyon,block,fnum */ - if( (r&0x0f) > 8) return; - CH = &OPL->P_CH[r&0x0f]; - if(!(r&0x10)) - { /* a0-a8 */ - block_fnum = (CH->block_fnum&0x1f00) | v; - } - else - { /* b0-b8 */ - block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); - - if(v&0x20) - { - FM_KEYON (&CH->SLOT[SLOT1], 1); - FM_KEYON (&CH->SLOT[SLOT2], 1); - } - else - { - FM_KEYOFF(&CH->SLOT[SLOT1],~1); - FM_KEYOFF(&CH->SLOT[SLOT2],~1); - } - } - /* update */ - if(CH->block_fnum != block_fnum) - { - UINT8 block = block_fnum >> 10; - - CH->block_fnum = block_fnum; - - CH->ksl_base = ksl_tab[block_fnum>>6]; - CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block); - - /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ - CH->kcode = (CH->block_fnum&0x1c00)>>9; - - /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ - /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ - /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ - if (OPL->mode&0x40) - CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ - else - CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ - - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - } - break; - case 0xc0: - /* FB,C */ - if( (r&0x0f) > 8) return; - CH = &OPL->P_CH[r&0x0f]; - CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; - CH->SLOT[SLOT1].CON = v&1; - CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation; - break; - case 0xe0: /* waveform select */ - /* simply ignore write to the waveform select register if selecting not enabled in test register */ - if(OPL->wavesel) - { - slot = slot_array[r&0x1f]; - if(slot < 0) return; - CH = &OPL->P_CH[slot/2]; - - CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; - } - break; - } -} - -/*static TIMER_CALLBACK( cymfile_callback ) -{ - if (cymfile) - { - fputc( (unsigned char)0, cymfile ); - } -}*/ - -/* lock/unlock for common table */ -#if 0 -static int OPL_LockTable(/*const device_config *device*/) -{ - num_lock++; - if(num_lock>1) return 0; - - /* first time */ - - cur_chip = NULL; - /* allocate total level table (128kb space) */ - if( !init_tables() ) - { - num_lock--; - return -1; - } - -#if 0 - if (LOG_CYM_FILE) - { - cymfile = fopen("3812_.cym","wb"); - if (cymfile) - timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); /*110 Hz pulse timer*/ - else - logerror("Could not create file 3812_.cym\n"); - } -#endif - - return 0; -} - -static void OPL_UnLockTable(void) -{ - if(num_lock) num_lock--; - if(num_lock) return; - - /* last time */ - - cur_chip = NULL; - OPLCloseTable(); - - /*if (cymfile) - fclose (cymfile); - cymfile = NULL;*/ -} -#endif - -static void OPLResetChip(FM_OPL *OPL) -{ - int c,s; - int i; - - OPL->eg_timer = 0; - OPL->eg_cnt = 0; - - OPL->noise_rng = 1; /* noise shift register */ - OPL->mode = 0; /* normal mode */ - OPL_STATUS_RESET(OPL,0x7f); - - /* reset with register write */ - OPLWriteReg(OPL,0x01,0); /* wavesel disable */ - OPLWriteReg(OPL,0x02,0); /* Timer1 */ - OPLWriteReg(OPL,0x03,0); /* Timer2 */ - OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ - for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); - - /* reset operator parameters */ - for( c = 0 ; c < 9 ; c++ ) - { - OPL_CH *CH = &OPL->P_CH[c]; - for(s = 0 ; s < 2 ; s++ ) - { - /* wave table */ - CH->SLOT[s].wavetable = 0; - CH->SLOT[s].state = EG_OFF; - CH->SLOT[s].volume = MAX_ATT_INDEX; - } - } -#if BUILD_Y8950 - if(OPL->type&OPL_TYPE_ADPCM) - { - YM_DELTAT *DELTAT = OPL->deltat; - - DELTAT->freqbase = OPL->freqbase; - DELTAT->output_pointer = &OPL->output_deltat[0]; - DELTAT->portshift = 5; - DELTAT->output_range = 1<<23; - YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL); - } -#endif -} - - -#if 0 -static STATE_POSTLOAD( OPL_postload ) -{ - FM_OPL *OPL = (FM_OPL *)param; - int slot, ch; - - for( ch=0 ; ch < 9 ; ch++ ) - { - OPL_CH *CH = &OPL->P_CH[ch]; - - /* Look up key scale level */ - UINT32 block_fnum = CH->block_fnum; - CH->ksl_base = ksl_tab[block_fnum >> 6]; - CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); - - for( slot=0 ; slot < 2 ; slot++ ) - { - OPL_SLOT *SLOT = &CH->SLOT[slot]; - - /* Calculate key scale rate */ - SLOT->ksr = CH->kcode >> SLOT->KSR; - - /* Calculate attack, decay and release rates */ - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; - - /* Calculate phase increment */ - SLOT->Incr = CH->fc * SLOT->mul; - - /* Total level */ - SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl); - - /* Connect output */ - SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation; - } - } -#if BUILD_Y8950 - if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) - { - // We really should call the postlod function for the YM_DELTAT, but it's hard without registers - // (see the way the YM2610 does it) - //YM_DELTAT_postload(OPL->deltat, REGS); - } -#endif -} - - -static void OPLsave_state_channel(const device_config *device, OPL_CH *CH) -{ - int slot, ch; - - for( ch=0 ; ch < 9 ; ch++, CH++ ) - { - /* channel */ - state_save_register_device_item(device, ch, CH->block_fnum); - state_save_register_device_item(device, ch, CH->kcode); - /* slots */ - for( slot=0 ; slot < 2 ; slot++ ) - { - OPL_SLOT *SLOT = &CH->SLOT[slot]; - - state_save_register_device_item(device, ch * 2 + slot, SLOT->ar); - state_save_register_device_item(device, ch * 2 + slot, SLOT->dr); - state_save_register_device_item(device, ch * 2 + slot, SLOT->rr); - state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR); - state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl); - state_save_register_device_item(device, ch * 2 + slot, SLOT->mul); - - state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt); - state_save_register_device_item(device, ch * 2 + slot, SLOT->FB); - state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out); - state_save_register_device_item(device, ch * 2 + slot, SLOT->CON); - - state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type); - state_save_register_device_item(device, ch * 2 + slot, SLOT->state); - state_save_register_device_item(device, ch * 2 + slot, SLOT->TL); - state_save_register_device_item(device, ch * 2 + slot, SLOT->volume); - state_save_register_device_item(device, ch * 2 + slot, SLOT->sl); - state_save_register_device_item(device, ch * 2 + slot, SLOT->key); - - state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask); - state_save_register_device_item(device, ch * 2 + slot, SLOT->vib); - - state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable); - } - } -} - - -/* Register savestate for a virtual YM3812/YM3526Y8950 */ - -static void OPL_save_state(FM_OPL *OPL, const device_config *device) -{ - OPLsave_state_channel(device, OPL->P_CH); - - state_save_register_device_item(device, 0, OPL->eg_cnt); - state_save_register_device_item(device, 0, OPL->eg_timer); - - state_save_register_device_item(device, 0, OPL->rhythm); - - state_save_register_device_item(device, 0, OPL->lfo_am_depth); - state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range); - state_save_register_device_item(device, 0, OPL->lfo_am_cnt); - state_save_register_device_item(device, 0, OPL->lfo_pm_cnt); - - state_save_register_device_item(device, 0, OPL->noise_rng); - state_save_register_device_item(device, 0, OPL->noise_p); - - if( OPL->type & OPL_TYPE_WAVESEL ) - { - state_save_register_device_item(device, 0, OPL->wavesel); - } - - state_save_register_device_item_array(device, 0, OPL->T); - state_save_register_device_item_array(device, 0, OPL->st); - -#if BUILD_Y8950 - if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) - { - YM_DELTAT_savestate(device, OPL->deltat); - } - - if ( OPL->type & OPL_TYPE_IO ) - { - state_save_register_device_item(device, 0, OPL->portDirection); - state_save_register_device_item(device, 0, OPL->portLatch); - } -#endif - - state_save_register_device_item(device, 0, OPL->address); - state_save_register_device_item(device, 0, OPL->status); - state_save_register_device_item(device, 0, OPL->statusmask); - state_save_register_device_item(device, 0, OPL->mode); - - state_save_register_postload(device->machine, OPL_postload, OPL); -} -#endif - - -/* Create one of virtual YM3812/YM3526/Y8950 */ -/* 'clock' is chip clock in Hz */ -/* 'rate' is sampling rate */ -static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type) -{ - char *ptr; - FM_OPL *OPL; - int state_size; - - //if (OPL_LockTable(device) == -1) return NULL; - init_tables(); - - /* calculate OPL state size */ - state_size = sizeof(FM_OPL); - -#if BUILD_Y8950 - if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); -#endif - - /* allocate memory block */ - ptr = (char *)malloc(state_size); - - if (ptr==NULL) - return 0; - - /* clear */ - memset(ptr,0,state_size); - - OPL = (FM_OPL *)ptr; - - ptr += sizeof(FM_OPL); - -#if BUILD_Y8950 - if (type&OPL_TYPE_ADPCM) - { - OPL->deltat = (YM_DELTAT *)ptr; - } - ptr += sizeof(YM_DELTAT); -#endif - - OPL->type = type; - OPL->clock = clock; - OPL->rate = rate; - - /* init global tables */ - OPL_initalize(OPL); - - return OPL; -} - -/* Destroy one of virtual YM3812 */ -static void OPLDestroy(FM_OPL *OPL) -{ - //OPL_UnLockTable(); - free(OPL); -} - -/* Optional handlers */ - -/*static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param) -{ - OPL->timer_handler = timer_handler; - OPL->TimerParam = param; -}*/ -static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param) -{ - OPL->IRQHandler = IRQHandler; - OPL->IRQParam = param; -} -static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param) -{ - OPL->UpdateHandler = UpdateHandler; - OPL->UpdateParam = param; -} - -static int OPLWrite(FM_OPL *OPL,int a,int v) -{ - if( !(a&1) ) - { /* address port */ - OPL->address = v & 0xff; - } - else - { /* data port */ - if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); - OPLWriteReg(OPL,OPL->address,v); - } - return OPL->status>>7; -} - -static unsigned char OPLRead(FM_OPL *OPL,int a) -{ - if( !(a&1) ) - { - /* status port */ - - #if BUILD_Y8950 - - if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */ - { - return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1); - } - - #endif - - /* OPL and OPL2 */ - return OPL->status & (OPL->statusmask|0x80); - } - -#if BUILD_Y8950 - /* data port */ - switch(OPL->address) - { - case 0x05: /* KeyBoard IN */ - if(OPL->type&OPL_TYPE_KEYBOARD) - { - if(OPL->keyboardhandler_r) - return OPL->keyboardhandler_r(OPL->keyboard_param); - /*else - logerror("Y8950: read unmapped KEYBOARD port\n");*/ - } - return 0; - - case 0x0f: /* ADPCM-DATA */ - if(OPL->type&OPL_TYPE_ADPCM) - { - UINT8 val; - - val = YM_DELTAT_ADPCM_Read(OPL->deltat); - /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ - return val; - } - return 0; - - case 0x19: /* I/O DATA */ - if(OPL->type&OPL_TYPE_IO) - { - if(OPL->porthandler_r) - return OPL->porthandler_r(OPL->port_param); - /*else - logerror("Y8950:read unmapped I/O port\n");*/ - } - return 0; - case 0x1a: /* PCM-DATA */ - if(OPL->type&OPL_TYPE_ADPCM) - { - /*logerror("Y8950 A/D convertion is accessed but not implemented !\n");*/ - return 0x80; /* 2's complement PCM data - result from A/D convertion */ - } - return 0; - } -#endif - - return 0xff; -} - -/* CSM Key Controll */ -INLINE void CSMKeyControll(OPL_CH *CH) -{ - FM_KEYON (&CH->SLOT[SLOT1], 4); - FM_KEYON (&CH->SLOT[SLOT2], 4); - - /* The key off should happen exactly one sample later - not implemented correctly yet */ - - FM_KEYOFF(&CH->SLOT[SLOT1], ~4); - FM_KEYOFF(&CH->SLOT[SLOT2], ~4); -} - - -static int OPLTimerOver(FM_OPL *OPL,int c) -{ - if( c ) - { /* Timer B */ - OPL_STATUS_SET(OPL,0x20); - } - else - { /* Timer A */ - OPL_STATUS_SET(OPL,0x40); - /* CSM mode key,TL controll */ - if( OPL->mode & 0x80 ) - { /* CSM mode total level latch and auto key on */ - int ch; - if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); - for(ch=0; ch<9; ch++) - CSMKeyControll( &OPL->P_CH[ch] ); - } - } - /* reload timer */ - //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c])); - return OPL->status>>7; -} - - -#define MAX_OPL_CHIPS 2 - - -#if (BUILD_YM3812) - -void * ym3812_init(UINT32 clock, UINT32 rate) -{ - /* emulator create */ - FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812); - if (YM3812) - { - //OPL_save_state(YM3812); - ym3812_reset_chip(YM3812); - } - return YM3812; -} - -void ym3812_shutdown(void *chip) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - - /* emulator shutdown */ - OPLDestroy(YM3812); -} -void ym3812_reset_chip(void *chip) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - OPLResetChip(YM3812); -} - -int ym3812_write(void *chip, int a, int v) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - return OPLWrite(YM3812, a, v); -} - -unsigned char ym3812_read(void *chip, int a) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - /* YM3812 always returns bit2 and bit1 in HIGH state */ - return OPLRead(YM3812, a) | 0x06 ; -} -int ym3812_timer_over(void *chip, int c) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - return OPLTimerOver(YM3812, c); -} - -/*void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - OPLSetTimerHandler(YM3812, timer_handler, param); -}*/ -void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - OPLSetIRQHandler(YM3812, IRQHandler, param); -} -void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) -{ - FM_OPL *YM3812 = (FM_OPL *)chip; - OPLSetUpdateHandler(YM3812, UpdateHandler, param); -} - - -/* -** Generate samples for one of the YM3812's -** -** 'which' is the virtual YM3812 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length) -{ - FM_OPL *OPL = (FM_OPL *)chip; - UINT8 rhythm = OPL->rhythm&0x20; - OPLSAMPLE *buf = buffer; - int i; - - /* rhythm slots */ - OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; - OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; - OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; - OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; - for( i=0; i < length ; i++ ) - { - int lt; - - OPL->output[0] = 0; - - advance_lfo(OPL); - - /* FM part */ - OPL_CALC_CH(OPL, &OPL->P_CH[0]); - OPL_CALC_CH(OPL, &OPL->P_CH[1]); - OPL_CALC_CH(OPL, &OPL->P_CH[2]); - OPL_CALC_CH(OPL, &OPL->P_CH[3]); - OPL_CALC_CH(OPL, &OPL->P_CH[4]); - OPL_CALC_CH(OPL, &OPL->P_CH[5]); - - if(!rhythm) - { - OPL_CALC_CH(OPL, &OPL->P_CH[6]); - OPL_CALC_CH(OPL, &OPL->P_CH[7]); - OPL_CALC_CH(OPL, &OPL->P_CH[8]); - } - else /* Rhythm part */ - { - OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); - } - - lt = OPL->output[0]; - - lt >>= FINAL_SH; - - /* limit check */ - lt = limit( lt , MAXOUT, MINOUT ); - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - buf[i] = lt; - - advance(OPL); - } - -} -#endif /* BUILD_YM3812 */ - - - -#if (BUILD_YM3526) - -void *ym3526_init(UINT32 clock, UINT32 rate) -{ - /* emulator create */ - FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526); - if (YM3526) - { - /*OPL_save_state(YM3526);*/ - ym3526_reset_chip(YM3526); - } - return YM3526; -} - -void ym3526_shutdown(void *chip) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - /* emulator shutdown */ - OPLDestroy(YM3526); -} -void ym3526_reset_chip(void *chip) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - OPLResetChip(YM3526); -} - -int ym3526_write(void *chip, int a, int v) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - return OPLWrite(YM3526, a, v); -} - -unsigned char ym3526_read(void *chip, int a) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - /* YM3526 always returns bit2 and bit1 in HIGH state */ - return OPLRead(YM3526, a) | 0x06 ; -} -int ym3526_timer_over(void *chip, int c) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - return OPLTimerOver(YM3526, c); -} - -/*void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - OPLSetTimerHandler(YM3526, timer_handler, param); -}*/ -void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - OPLSetIRQHandler(YM3526, IRQHandler, param); -} -void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) -{ - FM_OPL *YM3526 = (FM_OPL *)chip; - OPLSetUpdateHandler(YM3526, UpdateHandler, param); -} - - -/* -** Generate samples for one of the YM3526's -** -** 'which' is the virtual YM3526 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length) -{ - FM_OPL *OPL = (FM_OPL *)chip; - UINT8 rhythm = OPL->rhythm&0x20; - OPLSAMPLE *buf = buffer; - int i; - - /* rhythm slots */ - OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; - OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; - OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; - OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; - for( i=0; i < length ; i++ ) - { - int lt; - - OPL->output[0] = 0; - - advance_lfo(OPL); - - /* FM part */ - OPL_CALC_CH(OPL, &OPL->P_CH[0]); - OPL_CALC_CH(OPL, &OPL->P_CH[1]); - OPL_CALC_CH(OPL, &OPL->P_CH[2]); - OPL_CALC_CH(OPL, &OPL->P_CH[3]); - OPL_CALC_CH(OPL, &OPL->P_CH[4]); - OPL_CALC_CH(OPL, &OPL->P_CH[5]); - - if(!rhythm) - { - OPL_CALC_CH(OPL, &OPL->P_CH[6]); - OPL_CALC_CH(OPL, &OPL->P_CH[7]); - OPL_CALC_CH(OPL, &OPL->P_CH[8]); - } - else /* Rhythm part */ - { - OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); - } - - lt = OPL->output[0]; - - lt >>= FINAL_SH; - - /* limit check */ - lt = limit( lt , MAXOUT, MINOUT ); - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - buf[i] = lt; - - advance(OPL); - } - -} -#endif /* BUILD_YM3526 */ - - - - -#if BUILD_Y8950 - -static void Y8950_deltat_status_set(void *chip, UINT8 changebits) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - OPL_STATUS_SET(Y8950, changebits); -} -static void Y8950_deltat_status_reset(void *chip, UINT8 changebits) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - OPL_STATUS_RESET(Y8950, changebits); -} - -void *y8950_init(UINT32 clock, UINT32 rate) -{ - /* emulator create */ - FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950); - if (Y8950) - { - Y8950->deltat->status_set_handler = Y8950_deltat_status_set; - Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset; - Y8950->deltat->status_change_which_chip = Y8950; - Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ - Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ - - /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ - /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ - /* reset */ - /*OPL_save_state(Y8950);*/ - y8950_reset_chip(Y8950); - } - - return Y8950; -} - -void y8950_shutdown(void *chip) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - /* emulator shutdown */ - OPLDestroy(Y8950); -} -void y8950_reset_chip(void *chip) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - OPLResetChip(Y8950); -} - -int y8950_write(void *chip, int a, int v) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - return OPLWrite(Y8950, a, v); -} - -unsigned char y8950_read(void *chip, int a) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - return OPLRead(Y8950, a); -} -int y8950_timer_over(void *chip, int c) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - return OPLTimerOver(Y8950, c); -} - -/*void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - OPLSetTimerHandler(Y8950, timer_handler, param); -}*/ -void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - OPLSetIRQHandler(Y8950, IRQHandler, param); -} -void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) -{ - FM_OPL *Y8950 = (FM_OPL *)chip; - OPLSetUpdateHandler(Y8950, UpdateHandler, param); -} - -void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ) -{ - FM_OPL *OPL = (FM_OPL *)chip; - OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr); - OPL->deltat->memory_size = deltat_mem_size; -} - -/* -** Generate samples for one of the Y8950's -** -** 'which' is the virtual Y8950 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length) -{ - int i; - FM_OPL *OPL = (FM_OPL *)chip; - UINT8 rhythm = OPL->rhythm&0x20; - YM_DELTAT *DELTAT = OPL->deltat; - OPLSAMPLE *buf = buffer; - - /* rhythm slots */ - OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; - OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; - OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; - OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; - - for( i=0; i < length ; i++ ) - { - int lt; - - OPL->output[0] = 0; - OPL->output_deltat[0] = 0; - - advance_lfo(OPL); - - /* deltaT ADPCM */ - if( DELTAT->portstate&0x80 ) - YM_DELTAT_ADPCM_CALC(DELTAT); - - /* FM part */ - OPL_CALC_CH(OPL, &OPL->P_CH[0]); - OPL_CALC_CH(OPL, &OPL->P_CH[1]); - OPL_CALC_CH(OPL, &OPL->P_CH[2]); - OPL_CALC_CH(OPL, &OPL->P_CH[3]); - OPL_CALC_CH(OPL, &OPL->P_CH[4]); - OPL_CALC_CH(OPL, &OPL->P_CH[5]); - - if(!rhythm) - { - OPL_CALC_CH(OPL, &OPL->P_CH[6]); - OPL_CALC_CH(OPL, &OPL->P_CH[7]); - OPL_CALC_CH(OPL, &OPL->P_CH[8]); - } - else /* Rhythm part */ - { - OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); - } - - lt = OPL->output[0] + (OPL->output_deltat[0]>>11); - - lt >>= FINAL_SH; - - /* limit check */ - lt = limit( lt , MAXOUT, MINOUT ); - - #ifdef SAVE_SAMPLE - if (which==0) - { - SAVE_ALL_CHANNELS - } - #endif - - /* store to sound buffer */ - buf[i] = lt; - - advance(OPL); - } - -} - -void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param) -{ - FM_OPL *OPL = (FM_OPL *)chip; - OPL->porthandler_w = PortHandler_w; - OPL->porthandler_r = PortHandler_r; - OPL->port_param = param; -} - -void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param) -{ - FM_OPL *OPL = (FM_OPL *)chip; - OPL->keyboardhandler_w = KeyboardHandler_w; - OPL->keyboardhandler_r = KeyboardHandler_r; - OPL->keyboard_param = param; -} - -#endif - +/* +** +** File: fmopl.c - software implementation of FM sound generator +** types OPL and OPL2 +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 0.72 +** + +Revision History: + +04-08-2003 Jarek Burczynski: + - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip + handles memory read/write or during the adpcm synthesis when the chip + requests another byte of ADPCM data. + +24-07-2003 Jarek Burczynski: + - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after + some (unknown) delay). Right now it's always set. + +14-06-2003 Jarek Burczynski: + - implemented all of the status register flags in Y8950 emulation + - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since + they can be either RAM or ROM + +08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip) + - corrected ym3526_read() to always set bit 2 and bit 1 + to HIGH state - identical to ym3812_read (verified on real YM3526) + +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + +03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) + Complete rewrite (all verified on real YM3812): + - corrected sin_tab and tl_tab data + - corrected operator output calculations + - corrected waveform_select_enable register; + simply: ignore all writes to waveform_select register when + waveform_select_enable == 0 and do not change the waveform previously selected. + - corrected KSR handling + - corrected Envelope Generator: attack shape, Sustain mode and + Percussive/Non-percussive modes handling + - Envelope Generator rates are two times slower now + - LFO amplitude (tremolo) and phase modulation (vibrato) + - rhythm sounds phase generation + - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm) + - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM) + - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1) + +12-28-2001 Acho A. Tang + - reflected Delta-T EOS status on Y8950 status port. + - fixed subscription range of attack/decay tables + + + To do: + add delay before key off in CSM mode (see CSMKeyControll) + verify volume of the FM part on the Y8950 +*/ + +#include +#include "mamedef.h" +#ifdef _DEBUG +#include +#endif +#include +#include +//#include "sndintrf.h" +#include "fmopl.h" +#if BUILD_Y8950 +#include "ymdeltat.h" +#endif + + +#define NULL ((void *)0) + + +/* output final shift */ +#if (OPL_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<=0) + { + if (value < 0x0200) + return (value & ~0); + if (value < 0x0400) + return (value & ~1); + if (value < 0x0800) + return (value & ~3); + if (value < 0x1000) + return (value & ~7); + if (value < 0x2000) + return (value & ~15); + if (value < 0x4000) + return (value & ~31); + return (value & ~63); + } + /*else value < 0*/ + if (value > -0x0200) + return (~abs(value) & ~0); + if (value > -0x0400) + return (~abs(value) & ~1); + if (value > -0x0800) + return (~abs(value) & ~3); + if (value > -0x1000) + return (~abs(value) & ~7); + if (value > -0x2000) + return (~abs(value) & ~15); + if (value > -0x4000) + return (~abs(value) & ~31); + return (~abs(value) & ~63); +} + + +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = acc_calc(lt); \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + +//#define LOG_CYM_FILE 0 +//static FILE * cymfile = NULL; + + + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ +#define OPL_TYPE_IO 0x08 /* I/O port */ + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) + + + +typedef struct +{ + UINT32 ar; /* attack rate: AR<<2 */ + UINT32 dr; /* decay rate: DR<<2 */ + UINT32 rr; /* release rate:RR<<2 */ + UINT8 KSR; /* key scale rate */ + UINT8 ksl; /* keyscale level */ + UINT8 ksr; /* key scale rate: kcode>>KSR */ + UINT8 mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + UINT32 Cnt; /* frequency counter */ + UINT32 Incr; /* frequency counter step */ + UINT8 FB; /* feedback shift value */ + INT32 *connect1; /* slot1 output pointer */ + INT32 op1_out[2]; /* slot1 output for feedback */ + UINT8 CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + UINT8 eg_type; /* percussive/non-percussive mode */ + UINT8 state; /* phase type */ + UINT32 TL; /* total level: TL << 2 */ + INT32 TLL; /* adjusted now TL */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level: sl_tab[SL] */ + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_dr; /* (decay state) */ + UINT8 eg_sel_dr; /* (decay state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ + UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + UINT16 wavetable; +} OPL_SLOT; + +typedef struct +{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + UINT32 block_fnum; /* block+fnum */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 kcode; /* key code (for key scaling) */ + UINT8 Muted; +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f +{ + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + UINT8 MuteSpc[6]; /* Mute Special: 5 Rhythm + 1 DELTA-T Channel */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ + + UINT8 rhythm; /* Rhythm mode */ + + UINT32 fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + UINT32 LFO_AM; + INT32 LFO_PM; + + UINT8 lfo_am_depth; + UINT8 lfo_pm_depth_range; + UINT32 lfo_am_cnt; + UINT32 lfo_am_inc; + UINT32 lfo_pm_cnt; + UINT32 lfo_pm_inc; + + UINT32 noise_rng; /* 23 bit noise shift register */ + UINT32 noise_p; /* current noise 'phase' */ + UINT32 noise_f; /* current noise period */ + + UINT8 wavesel; /* waveform select enable flag */ + + UINT32 T[2]; /* timer counters */ + UINT8 st[2]; /* timer enable */ + +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + + YM_DELTAT *deltat; + + /* Keyboard and I/O ports interface */ + UINT8 portDirection; + UINT8 portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + void * port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + void * keyboard_param; +#endif + + /* external event callback handlers */ + OPL_TIMERHANDLER timer_handler; /* TIMER handler */ + void *TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + void *IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ + void *UpdateParam; /* stream update parameter */ + + UINT8 type; /* chip type */ + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT8 statusmask; /* status mask */ + UINT8 mode; /* Reg.08 : CSM,notesel,etc. */ + + UINT32 clock; /* master clock (Hz) */ + UINT32 rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + //attotime TimerBase; /* Timer base time (==sampling time)*/ + + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + signed int output[1]; +#if BUILD_Y8950 + INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */ +#endif +} FM_OPL; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) +static const UINT32 ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; +#undef DV + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) ) +static const UINT32 sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const UINT8 mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, + 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML +}; +#undef ML + +/* TL_TAB_LEN is calculated as: +* 12 - sinus amplitude bits (Y axis) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (12*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* four waveforms on OPL2 type chips */ +static unsigned int sin_tab[SIN_LEN * 4]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const INT8 lfo_pm_table[8*8*2] = { + +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +static int num_lock = 0; + + +#define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1]) +#define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2]) +#define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1]) +#define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2]) + + + + +/*INLINE int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +}*/ + + +/* status set and IRQ handling */ +INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) +{ + /* set status flag */ + OPL->status |= flag; + if(!(OPL->status & 0x80)) + { + if(OPL->status & OPL->statusmask) + { /* IRQ on */ + OPL->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) +{ + /* reset status flag */ + OPL->status &=~flag; + if((OPL->status & 0x80)) + { + if (!(OPL->status & OPL->statusmask) ) + { + OPL->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); + } + } +} + +/* IRQ mask set */ +INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) +{ + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + + +/* advance LFO to next sample */ +INLINE void advance_lfo(FM_OPL *OPL) +{ + UINT8 tmp; + + /* LFO */ + OPL->lfo_am_cnt += OPL->lfo_am_inc; + if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (OPL->lfo_am_depth) + OPL->LFO_AM = tmp; + else + OPL->LFO_AM = tmp>>2; + + OPL->lfo_pm_cnt += OPL->lfo_pm_inc; + OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; +} + +INLINE void refresh_eg(FM_OPL* OPL) +{ + OPL_CH *CH; + OPL_SLOT *op; + int i; + int new_vol; + + for (i=0; i<9*2; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + // Envelope Generator + switch(op->state) + { + case EG_ATT: // attack phase + if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + new_vol = op->volume + ((~op->volume * + (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) + ) >> 3); + if (new_vol <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + } + break; + /*case EG_DEC: // decay phase + if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + new_vol = op->volume + eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( new_vol >= op->sl ) + op->state = EG_SUS; + } + break; + case EG_SUS: // sustain phase + if ( !op->eg_type) percussive mode + { + new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + if ( new_vol >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + } + break; + case EG_REL: // release phase + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + if ( new_vol >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + default: + break;*/ + } + } + + return; +} + +/* advance to next sample */ +INLINE void advance(FM_OPL *OPL) +{ + OPL_CH *CH; + OPL_SLOT *op; + int i; + + OPL->eg_timer += OPL->eg_timer_add; + + while (OPL->eg_timer >= OPL->eg_timer_overflow) + { + OPL->eg_timer -= OPL->eg_timer_overflow; + + OPL->eg_cnt++; + + for (i=0; i<9*2; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } + } + } + + for (i=0; i<9*2; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Phase Generator */ + if(op->vib) + { + UINT8 block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + OPL->noise_p += OPL->noise_f; + i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + OPL->noise_p &= FREQ_MASK; + while (i) + { + /* + UINT32 j; + j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; + OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; + OPL->noise_rng >>= 1; + + i--; + } +} + + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask)) + +/* calculate output */ +INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH ) +{ + OPL_SLOT *SLOT; + unsigned int env; + signed int out; + + if (CH->Muted) + return; + + OPL->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable); +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise ) +{ + OPL_SLOT *SLOT; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + OPL->phase_modulation = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + OPL->phase_modulation = SLOT->op1_out[0]; + /* else ignore output of operator 1 */ + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET && ! OPL->MuteSpc[0] ) + OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */ + /* SD (16) channel 7->slot 1 */ + /* TOM (14) channel 8->slot 1 */ + /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ + + /* Envelope generation based on: */ + /* HH channel 7->slot1 */ + /* SD channel 7->slot2 */ + /* TOM channel 8->slot1 */ + /* TOP channel 8->slot2 */ + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET && ! OPL->MuteSpc[4] ) + { + + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + OPL->output[0] += op_calc(phase<wavetable) * 2; + } + + /* Snare Drum (verified on real YM3812) */ + env = volume_calc(SLOT7_2); + if( env < ENV_QUIET && ! OPL->MuteSpc[1] ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + UINT32 phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + OPL->output[0] += op_calc(phase<wavetable) * 2; + } + + /* Tom Tom (verified on real YM3812) */ + env = volume_calc(SLOT8_1); + if( env < ENV_QUIET && ! OPL->MuteSpc[2] ) + OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(SLOT8_2); + if( env < ENV_QUIET && ! OPL->MuteSpc[3] ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + UINT32 phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + OPL->output[0] += op_calc(phase<wavetable) * 2; + } +} + + +/* generic table initialize */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<12; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<12; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); + logerror("\n"); + #endif + } + /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/ + } + /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + +static void OPLCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif +} + + + +static void OPL_initalize(FM_OPL *OPL) +{ + int i; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0; +#if 0 + OPL->rate = (double)OPL->clock / 72.0; + OPL->freqbase = 1.0; +#endif + + /*logerror("freqbase=%f\n", OPL->freqbase);*/ + + /* Timer base time */ + //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72); + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("FMOPL.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("FMOPL.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", ksl_tab[i*16+j] ); + } + logerror("\n"); + } +#endif + + for(i = 0; i < 9; i ++) + OPL->P_CH[i].Muted = 0x00; + for(i = 0; i < 6; i ++) + OPL->MuteSpc[i] = 0x00; + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; + + /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ + OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; + + /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/ + + /* Noise generator: a step takes 1 sample */ + OPL->noise_f = (1.0 / 1.0) * (1<freqbase; + + OPL->eg_timer_add = (1<freqbase; + OPL->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, OPL->eg_timer_overflow);*/ + +} + +INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +/* write a value v to register r on OPL chip */ +static void OPLWriteReg(FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + /*if (LOG_CYM_FILE && (cymfile) && (r!=0) ) + { + fputc( (unsigned char)r, cymfile ); + fputc( (unsigned char)v, cymfile ); + }*/ + + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* waveform select enable */ + if(OPL->type&OPL_TYPE_WAVESEL) + { + OPL->wavesel = v&0x20; + /* do not change the waveform previously selected */ + } + break; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v)*16; + break; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */ + } + else + { /* set IRQ mask ,timer enable*/ + UINT8 st1 = v&1; + UINT8 st2 = (v>>1)&1; + + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL, v & (0x78-0x08) ); + OPL_STATUSMASK_SET(OPL, (~v) & 0x78 ); + + /* timer 2 */ + if(OPL->st[1] != st2) + { + //attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero; + OPL->st[1] = st2; + //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period); + } + /* timer 1 */ + if(OPL->st[0] != st1) + { + //attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero; + OPL->st[0] = st1; + //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period); + } + } + break; +#if BUILD_Y8950 + case 0x06: /* Key Board OUT */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_w) + OPL->keyboardhandler_w(OPL->keyboard_param,v); +#ifdef _DEBUG + else + logerror("Y8950: write unmapped KEYBOARD port\n"); +#endif + } + break; + case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + break; +#endif + case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + OPL->mode = v; +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */ +#endif + break; + +#if BUILD_Y8950 + case 0x09: /* START ADD */ + case 0x0a: + case 0x0b: /* STOP ADD */ + case 0x0c: + case 0x0d: /* PRESCALE */ + case 0x0e: + case 0x0f: /* ADPCM data write */ + case 0x10: /* DELTA-N */ + case 0x11: /* DELTA-N */ + case 0x12: /* ADPCM volume */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + break; + + case 0x15: /* DAC data high 8 bits (F7,F6...F2) */ + case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */ + case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */ +#ifdef _DEBUG + logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v); +#endif + break; + + case 0x18: /* I/O CTRL (Direction) */ + if(OPL->type&OPL_TYPE_IO) + OPL->portDirection = v&0x0f; + break; + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + OPL->portLatch = v; + if(OPL->porthandler_w) + OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); + } + break; +#endif + default: +#ifdef _DEBUG + logerror("FMOPL.C: write to unknown register: %02x\n",r); +#endif + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(OPL,slot,v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(OPL,slot,v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(OPL,slot,v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(OPL,slot,v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + OPL->lfo_am_depth = v & 0x80; + OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + OPL->rhythm = v&0x3f; + + if(OPL->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + UINT8 block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = ksl_tab[block_fnum>>6]; + CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (OPL->mode&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation; + break; + case 0xe0: /* waveform select */ + /* simply ignore write to the waveform select register if selecting not enabled in test register */ + if(OPL->wavesel) + { + slot = slot_array[r&0x1f]; + if(slot < 0) return; + CH = &OPL->P_CH[slot/2]; + + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; + } + break; + } +} + +/*static TIMER_CALLBACK( cymfile_callback ) +{ + if (cymfile) + { + fputc( (unsigned char)0, cymfile ); + } +}*/ + +/* lock/unlock for common table */ +static int OPL_LockTable(void) +{ + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + /* allocate total level table (128kb space) */ + if( !init_tables() ) + { + num_lock--; + return -1; + } + + /*if (LOG_CYM_FILE) + { + cymfile = fopen("3812_.cym","wb"); + if (cymfile) + timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer + else + logerror("Could not create file 3812_.cym\n"); + }*/ + + return 0; +} + +static void OPL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + + OPLCloseTable(); + + /*if (cymfile) + fclose (cymfile); + cymfile = NULL;*/ +} + +static void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; + + OPL->eg_timer = 0; + OPL->eg_cnt = 0; + + OPL->noise_rng = 1; /* noise shift register */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wavesel disable */ + OPLWriteReg(OPL,0x02,0); /* Timer1 */ + OPLWriteReg(OPL,0x03,0); /* Timer2 */ + OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + + /* reset operator parameters */ + for( c = 0 ; c < 9 ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = 0; + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = OPL->deltat; + + DELTAT->freqbase = OPL->freqbase; + DELTAT->output_pointer = &OPL->output_deltat[0]; + DELTAT->portshift = 5; + DELTAT->output_range = 1<<23; + YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL); + } +#endif +} + + +#if 0 +//static STATE_POSTLOAD( OPL_postload ) +static void OPL_postload(void* param) +{ + FM_OPL *OPL = (FM_OPL *)param; + int slot, ch; + + for( ch=0 ; ch < 9 ; ch++ ) + { + OPL_CH *CH = &OPL->P_CH[ch]; + + /* Look up key scale level */ + UINT32 block_fnum = CH->block_fnum; + CH->ksl_base = ksl_tab[block_fnum >> 6]; + CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10)); + + for( slot=0 ; slot < 2 ; slot++ ) + { + OPL_SLOT *SLOT = &CH->SLOT[slot]; + + /* Calculate key scale rate */ + SLOT->ksr = CH->kcode >> SLOT->KSR; + + /* Calculate attack, decay and release rates */ + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + + /* Calculate phase increment */ + SLOT->Incr = CH->fc * SLOT->mul; + + /* Total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl); + + /* Connect output */ + SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation; + } + } +#if BUILD_Y8950 + if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) + { + // We really should call the postlod function for the YM_DELTAT, but it's hard without registers + // (see the way the YM2610 does it) + //YM_DELTAT_postload(OPL->deltat, REGS); + } +#endif +} + + +static void OPLsave_state_channel(OPL_CH *CH) +{ + int slot, ch; + + for( ch=0 ; ch < 9 ; ch++, CH++ ) + { + // channel + state_save_register_device_item(device, ch, CH->block_fnum); + state_save_register_device_item(device, ch, CH->kcode); + // slots + for( slot=0 ; slot < 2 ; slot++ ) + { + OPL_SLOT *SLOT = &CH->SLOT[slot]; + + state_save_register_device_item(device, ch * 2 + slot, SLOT->ar); + state_save_register_device_item(device, ch * 2 + slot, SLOT->dr); + state_save_register_device_item(device, ch * 2 + slot, SLOT->rr); + state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR); + state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl); + state_save_register_device_item(device, ch * 2 + slot, SLOT->mul); + + state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt); + state_save_register_device_item(device, ch * 2 + slot, SLOT->FB); + state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out); + state_save_register_device_item(device, ch * 2 + slot, SLOT->CON); + + state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type); + state_save_register_device_item(device, ch * 2 + slot, SLOT->state); + state_save_register_device_item(device, ch * 2 + slot, SLOT->TL); + state_save_register_device_item(device, ch * 2 + slot, SLOT->volume); + state_save_register_device_item(device, ch * 2 + slot, SLOT->sl); + state_save_register_device_item(device, ch * 2 + slot, SLOT->key); + + state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask); + state_save_register_device_item(device, ch * 2 + slot, SLOT->vib); + + state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable); + } + } +} +#endif + + +/* Register savestate for a virtual YM3812/YM3526/Y8950 */ + +/*static void OPL_save_state(FM_OPL *OPL) +{ + OPLsave_state_channel(device, OPL->P_CH); + + state_save_register_device_item(device, 0, OPL->eg_cnt); + state_save_register_device_item(device, 0, OPL->eg_timer); + + state_save_register_device_item(device, 0, OPL->rhythm); + + state_save_register_device_item(device, 0, OPL->lfo_am_depth); + state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range); + state_save_register_device_item(device, 0, OPL->lfo_am_cnt); + state_save_register_device_item(device, 0, OPL->lfo_pm_cnt); + + state_save_register_device_item(device, 0, OPL->noise_rng); + state_save_register_device_item(device, 0, OPL->noise_p); + + if( OPL->type & OPL_TYPE_WAVESEL ) + { + state_save_register_device_item(device, 0, OPL->wavesel); + } + + state_save_register_device_item_array(device, 0, OPL->T); + state_save_register_device_item_array(device, 0, OPL->st); + +#if BUILD_Y8950 + if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) ) + { + YM_DELTAT_savestate(device, OPL->deltat); + } + + if ( OPL->type & OPL_TYPE_IO ) + { + state_save_register_device_item(device, 0, OPL->portDirection); + state_save_register_device_item(device, 0, OPL->portLatch); + } +#endif + + state_save_register_device_item(device, 0, OPL->address); + state_save_register_device_item(device, 0, OPL->status); + state_save_register_device_item(device, 0, OPL->statusmask); + state_save_register_device_item(device, 0, OPL->mode); + + state_save_register_postload(device->machine, OPL_postload, OPL); +}*/ + + +/* Create one of virtual YM3812/YM3526/Y8950 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + + if (OPL_LockTable() == -1) return NULL; + + /* calculate OPL state size */ + state_size = sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); +#endif + + /* allocate memory block */ + ptr = (char *)malloc(state_size); + + if (ptr==NULL) + return NULL; + + /* clear */ + memset(ptr,0,state_size); + + OPL = (FM_OPL *)ptr; + + ptr += sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) + { + OPL->deltat = (YM_DELTAT *)ptr; + } + ptr += sizeof(YM_DELTAT); +#endif + + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + + /* init global tables */ + OPL_initalize(OPL); + + return OPL; +} + +/* Destroy one of virtual YM3812 */ +static void OPLDestroy(FM_OPL *OPL) +{ + OPL_UnLockTable(); + free(OPL); +} + +/* Optional handlers */ + +static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param) +{ + OPL->timer_handler = timer_handler; + OPL->TimerParam = param; +} +static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} +static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} + +static int OPLWrite(FM_OPL *OPL,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + OPL->address = v & 0xff; + } + else + { /* data port */ + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/); + OPLWriteReg(OPL,OPL->address,v); + } + return OPL->status>>7; +} + +static unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { + /* status port */ + + #if BUILD_Y8950 + + if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */ + { + return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1); + } + + #endif + + /* OPL and OPL2 */ + return OPL->status & (OPL->statusmask|0x80); + } + +#if BUILD_Y8950 + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); +#ifdef _DEBUG + else + logerror("Y8950: read unmapped KEYBOARD port\n"); +#endif + } + return 0; + + case 0x0f: /* ADPCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { + UINT8 val; + + val = YM_DELTAT_ADPCM_Read(OPL->deltat); + /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/ + return val; + } + return 0; + + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); +#ifdef _DEBUG + else + logerror("Y8950:read unmapped I/O port\n"); +#endif + } + return 0; + case 0x1a: /* PCM-DATA */ + if(OPL->type&OPL_TYPE_ADPCM) + { +#ifdef _DEBUG + logerror("Y8950 A/D convertion is accessed but not implemented !\n"); +#endif + return 0x80; /* 2's complement PCM data - result from A/D convertion */ + } + return 0; + } +#endif + + return 0xff; +} + +/* CSM Key Controll */ +INLINE void CSMKeyControll(OPL_CH *CH) +{ + FM_KEYON (&CH->SLOT[SLOT1], 4); + FM_KEYON (&CH->SLOT[SLOT2], 4); + + /* The key off should happen exactly one sample later - not implemented correctly yet */ + + FM_KEYOFF(&CH->SLOT[SLOT1], ~4); + FM_KEYOFF(&CH->SLOT[SLOT2], ~4); +} + + +static int OPLTimerOver(FM_OPL *OPL,int c) +{ + if( c ) + { /* Timer B */ + OPL_STATUS_SET(OPL,0x20); + } + else + { /* Timer A */ + OPL_STATUS_SET(OPL,0x40); + /* CSM mode key,TL controll */ + if( OPL->mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/); + for(ch=0; ch<9; ch++) + CSMKeyControll( &OPL->P_CH[ch] ); + } + } + /* reload timer */ + //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c])); + return OPL->status>>7; +} + + +#define MAX_OPL_CHIPS 2 + + +#if (BUILD_YM3812) + +void * ym3812_init(UINT32 clock, UINT32 rate) +{ + /* emulator create */ + FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812); + if (YM3812) + { + //OPL_save_state(YM3812); + ym3812_reset_chip(YM3812); + } + return YM3812; +} + +void ym3812_shutdown(void *chip) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + /* emulator shutdown */ + OPLDestroy(YM3812); +} +void ym3812_reset_chip(void *chip) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLResetChip(YM3812); +} + +int ym3812_write(void *chip, int a, int v) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + return OPLWrite(YM3812, a, v); +} + +unsigned char ym3812_read(void *chip, int a) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + /* YM3812 always returns bit2 and bit1 in HIGH state */ + return OPLRead(YM3812, a) | 0x06 ; +} +int ym3812_timer_over(void *chip, int c) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + return OPLTimerOver(YM3812, c); +} + +void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetTimerHandler(YM3812, timer_handler, param); +} +void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetIRQHandler(YM3812, IRQHandler, param); +} +void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) +{ + FM_OPL *YM3812 = (FM_OPL *)chip; + OPLSetUpdateHandler(YM3812, UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM3812's +** +** 'which' is the virtual YM3812 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length) +{ + FM_OPL *OPL = (FM_OPL *)chip; + UINT8 rhythm = OPL->rhythm&0x20; + OPLSAMPLE *bufL = buffer[0]; + OPLSAMPLE *bufR = buffer[1]; + int i; + + if (! length) + { + refresh_eg(OPL); + return; + } + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + + advance_lfo(OPL); + + /* FM part */ + OPL_CALC_CH(OPL, &OPL->P_CH[0]); + OPL_CALC_CH(OPL, &OPL->P_CH[1]); + OPL_CALC_CH(OPL, &OPL->P_CH[2]); + OPL_CALC_CH(OPL, &OPL->P_CH[3]); + OPL_CALC_CH(OPL, &OPL->P_CH[4]); + OPL_CALC_CH(OPL, &OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(OPL, &OPL->P_CH[6]); + OPL_CALC_CH(OPL, &OPL->P_CH[7]); + OPL_CALC_CH(OPL, &OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = OPL->output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + //lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + bufL[i] = lt; + bufR[i] = lt; + + advance(OPL); + } + +} +#endif /* BUILD_YM3812 */ + + + +#if (BUILD_YM3526) + +void *ym3526_init(UINT32 clock, UINT32 rate) +{ + /* emulator create */ + FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526); + if (YM3526) + { + //OPL_save_state(YM3526); + ym3526_reset_chip(YM3526); + } + return YM3526; +} + +void ym3526_shutdown(void *chip) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + /* emulator shutdown */ + OPLDestroy(YM3526); +} +void ym3526_reset_chip(void *chip) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLResetChip(YM3526); +} + +int ym3526_write(void *chip, int a, int v) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + return OPLWrite(YM3526, a, v); +} + +unsigned char ym3526_read(void *chip, int a) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + /* YM3526 always returns bit2 and bit1 in HIGH state */ + return OPLRead(YM3526, a) | 0x06 ; +} +int ym3526_timer_over(void *chip, int c) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + return OPLTimerOver(YM3526, c); +} + +void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetTimerHandler(YM3526, timer_handler, param); +} +void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetIRQHandler(YM3526, IRQHandler, param); +} +void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) +{ + FM_OPL *YM3526 = (FM_OPL *)chip; + OPLSetUpdateHandler(YM3526, UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length) +{ + FM_OPL *OPL = (FM_OPL *)chip; + UINT8 rhythm = OPL->rhythm&0x20; + OPLSAMPLE *bufL = buffer[0]; + OPLSAMPLE *bufR = buffer[1]; + int i; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + + advance_lfo(OPL); + + /* FM part */ + OPL_CALC_CH(OPL, &OPL->P_CH[0]); + OPL_CALC_CH(OPL, &OPL->P_CH[1]); + OPL_CALC_CH(OPL, &OPL->P_CH[2]); + OPL_CALC_CH(OPL, &OPL->P_CH[3]); + OPL_CALC_CH(OPL, &OPL->P_CH[4]); + OPL_CALC_CH(OPL, &OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(OPL, &OPL->P_CH[6]); + OPL_CALC_CH(OPL, &OPL->P_CH[7]); + OPL_CALC_CH(OPL, &OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = OPL->output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + //lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + bufL[i] = lt; + bufR[i] = lt; + + advance(OPL); + } + +} +#endif /* BUILD_YM3526 */ + + + + +#if BUILD_Y8950 + +static void Y8950_deltat_status_set(void *chip, UINT8 changebits) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPL_STATUS_SET(Y8950, changebits); +} +static void Y8950_deltat_status_reset(void *chip, UINT8 changebits) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPL_STATUS_RESET(Y8950, changebits); +} + +void *y8950_init(UINT32 clock, UINT32 rate) +{ + /* emulator create */ + FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950); + if (Y8950) + { + Y8950->deltat->status_set_handler = Y8950_deltat_status_set; + Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset; + Y8950->deltat->status_change_which_chip = Y8950; + Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */ + Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */ + + /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */ + /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */ + /* reset */ + //OPL_save_state(Y8950); + y8950_reset_chip(Y8950); + } + + return Y8950; +} + +void y8950_shutdown(void *chip) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + + free(Y8950->deltat->memory); Y8950->deltat->memory = NULL; + + /* emulator shutdown */ + OPLDestroy(Y8950); +} +void y8950_reset_chip(void *chip) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLResetChip(Y8950); +} + +int y8950_write(void *chip, int a, int v) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLWrite(Y8950, a, v); +} + +unsigned char y8950_read(void *chip, int a) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLRead(Y8950, a); +} +int y8950_timer_over(void *chip, int c) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + return OPLTimerOver(Y8950, c); +} + +void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetTimerHandler(Y8950, timer_handler, param); +} +void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetIRQHandler(Y8950, IRQHandler, param); +} +void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + OPLSetUpdateHandler(Y8950, UpdateHandler, param); +} + +void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr); + OPL->deltat->memory_size = deltat_mem_size; +} + +void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData) +{ + FM_OPL *Y8950 = (FM_OPL *)chip; + + if (Y8950->deltat->memory_size != ROMSize) + { + Y8950->deltat->memory = (UINT8*)realloc(Y8950->deltat->memory, ROMSize); + Y8950->deltat->memory_size = ROMSize; + memset(Y8950->deltat->memory, 0xFF, ROMSize); + YM_DELTAT_calc_mem_mask(Y8950->deltat); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(Y8950->deltat->memory + DataStart, ROMData, DataLength); + + return; +} + +/* +** Generate samples for one of the Y8950's +** +** 'which' is the virtual Y8950 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length) +{ + int i; + FM_OPL *OPL = (FM_OPL *)chip; + UINT8 rhythm = OPL->rhythm&0x20; + YM_DELTAT *DELTAT = OPL->deltat; + OPLSAMPLE *bufL = buffer[0]; + OPLSAMPLE *bufR = buffer[1]; + + for( i=0; i < length ; i++ ) + { + int lt; + + OPL->output[0] = 0; + OPL->output_deltat[0] = 0; + + advance_lfo(OPL); + + /* deltaT ADPCM */ + if( DELTAT->portstate&0x80 && ! OPL->MuteSpc[5] ) + YM_DELTAT_ADPCM_CALC(DELTAT); + + /* FM part */ + OPL_CALC_CH(OPL, &OPL->P_CH[0]); + OPL_CALC_CH(OPL, &OPL->P_CH[1]); + OPL_CALC_CH(OPL, &OPL->P_CH[2]); + OPL_CALC_CH(OPL, &OPL->P_CH[3]); + OPL_CALC_CH(OPL, &OPL->P_CH[4]); + OPL_CALC_CH(OPL, &OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(OPL, &OPL->P_CH[6]); + OPL_CALC_CH(OPL, &OPL->P_CH[7]); + OPL_CALC_CH(OPL, &OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = OPL->output[0] + (OPL->output_deltat[0]>>11); + + lt >>= FINAL_SH; + + /* limit check */ + //lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + bufL[i] = lt; + bufR[i] = lt; + + advance(OPL); + } + +} + +void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->porthandler_w = PortHandler_w; + OPL->porthandler_r = PortHandler_r; + OPL->port_param = param; +} + +void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param) +{ + FM_OPL *OPL = (FM_OPL *)chip; + OPL->keyboardhandler_w = KeyboardHandler_w; + OPL->keyboardhandler_r = KeyboardHandler_r; + OPL->keyboard_param = param; +} + +#endif + +void opl_set_mute_mask(void *chip, UINT32 MuteMask) +{ + FM_OPL *opl = (FM_OPL *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 9; CurChn ++) + opl->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + for (CurChn = 0; CurChn < 6; CurChn ++) + opl->MuteSpc[CurChn] = (MuteMask >> (9 + CurChn)) & 0x01; + + return; +} diff --git a/Frameworks/GME/gme/fmopl.h b/Frameworks/GME/vgmplay/chips/fmopl.h similarity index 66% rename from Frameworks/GME/gme/fmopl.h rename to Frameworks/GME/vgmplay/chips/fmopl.h index 868d38bec..5351b3274 100644 --- a/Frameworks/GME/gme/fmopl.h +++ b/Frameworks/GME/vgmplay/chips/fmopl.h @@ -1,116 +1,122 @@ -#pragma once - -#ifndef __FMOPL_H__ -#define __FMOPL_H__ - -/* --- select emulation chips --- */ -#define BUILD_YM3812 (1) -#define BUILD_YM3526 (1) -#define BUILD_Y8950 (1) - -/* select output bits size of output : 8 or 16 */ -#define OPL_SAMPLE_BITS 16 - -/* compiler dependence */ -#ifndef __OSDCOMM_H__ -#define __OSDCOMM_H__ -typedef unsigned char UINT8; /* unsigned 8bit */ -typedef unsigned short UINT16; /* unsigned 16bit */ -typedef unsigned int UINT32; /* unsigned 32bit */ -typedef signed char INT8; /* signed 8bit */ -typedef signed short INT16; /* signed 16bit */ -typedef signed int INT32; /* signed 32bit */ - -typedef INT32 stream_sample_t; - -#endif /* __OSDCOMM_H__ */ - -typedef stream_sample_t OPLSAMPLE; -/* -#if (OPL_SAMPLE_BITS==16) -typedef INT16 OPLSAMPLE; -#endif -#if (OPL_SAMPLE_BITS==8) -typedef INT8 OPLSAMPLE; -#endif -*/ - -//typedef void (*OPL_TIMERHANDLER)(void *param,int timer,attotime period); -typedef void (*OPL_IRQHANDLER)(void *param,int irq); -typedef void (*OPL_UPDATEHANDLER)(void *param,int min_interval_us); -typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data); -typedef unsigned char (*OPL_PORTHANDLER_R)(void *param); - - -#if BUILD_YM3812 - -void *ym3812_init(UINT32 clock, UINT32 rate); -void ym3812_shutdown(void *chip); -void ym3812_reset_chip(void *chip); -int ym3812_write(void *chip, int a, int v); -unsigned char ym3812_read(void *chip, int a); -int ym3812_timer_over(void *chip, int c); -void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length); - -//void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); -void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); -void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); - -#endif /* BUILD_YM3812 */ - - -#if BUILD_YM3526 - -/* -** Initialize YM3526 emulator(s). -** -** 'num' is the number of virtual YM3526's to allocate -** 'clock' is the chip clock in Hz -** 'rate' is sampling rate -*/ -void *ym3526_init(UINT32 clock, UINT32 rate); -/* shutdown the YM3526 emulators*/ -void ym3526_shutdown(void *chip); -void ym3526_reset_chip(void *chip); -int ym3526_write(void *chip, int a, int v); -unsigned char ym3526_read(void *chip, int a); -int ym3526_timer_over(void *chip, int c); -/* -** Generate samples for one of the YM3526's -** -** 'which' is the virtual YM3526 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length); - -//void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); -void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); -void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); - -#endif /* BUILD_YM3526 */ - - -#if BUILD_Y8950 - -/* Y8950 port handlers */ -void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param); -void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param); -void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ); - -void * y8950_init(UINT32 clock, UINT32 rate); -void y8950_shutdown(void *chip); -void y8950_reset_chip(void *chip); -int y8950_write(void *chip, int a, int v); -unsigned char y8950_read (void *chip, int a); -int y8950_timer_over(void *chip, int c); -void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length); - -//void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); -void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); -void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); - -#endif /* BUILD_Y8950 */ - - -#endif /* __FMOPL_H__ */ +#pragma once + +//#include "attotime.h" + +/* --- select emulation chips --- */ +//#define BUILD_YM3812 (HAS_YM3812) +//#define BUILD_YM3526 (HAS_YM3526) +//#define BUILD_Y8950 (HAS_Y8950) +#define BUILD_YM3812 1 +#define BUILD_YM3526 1 +#ifndef NO_Y8950 +#define BUILD_Y8950 1 +#else +#define BUILD_Y8950 0 +#endif + +/* select output bits size of output : 8 or 16 */ +#define OPL_SAMPLE_BITS 16 + +/* compiler dependence */ +/*#ifndef __OSDCOMM_H__ +#define __OSDCOMM_H__ +typedef unsigned char UINT8; // unsigned 8bit +typedef unsigned short UINT16; // unsigned 16bit +typedef unsigned int UINT32; // unsigned 32bit +typedef signed char INT8; // signed 8bit +typedef signed short INT16; // signed 16bit +typedef signed int INT32; // signed 32bit +#endif*/ /* __OSDCOMM_H__ */ + +typedef stream_sample_t OPLSAMPLE; +/* +#if (OPL_SAMPLE_BITS==16) +typedef INT16 OPLSAMPLE; +#endif +#if (OPL_SAMPLE_BITS==8) +typedef INT8 OPLSAMPLE; +#endif +*/ + +//typedef void (*OPL_TIMERHANDLER)(void *param,int timer,attotime period); +typedef void (*OPL_TIMERHANDLER)(void *param,int timer,int period); +typedef void (*OPL_IRQHANDLER)(void *param,int irq); +typedef void (*OPL_UPDATEHANDLER)(void *param/*,int min_interval_us*/); +typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data); +typedef unsigned char (*OPL_PORTHANDLER_R)(void *param); + + +#if BUILD_YM3812 + +void *ym3812_init(UINT32 clock, UINT32 rate); +void ym3812_shutdown(void *chip); +void ym3812_reset_chip(void *chip); +int ym3812_write(void *chip, int a, int v); +unsigned char ym3812_read(void *chip, int a); +int ym3812_timer_over(void *chip, int c); +void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length); + +void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); +void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); +void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); + +#endif /* BUILD_YM3812 */ + + +#if BUILD_YM3526 + +/* +** Initialize YM3526 emulator(s). +** +** 'num' is the number of virtual YM3526's to allocate +** 'clock' is the chip clock in Hz +** 'rate' is sampling rate +*/ +void *ym3526_init(UINT32 clock, UINT32 rate); +/* shutdown the YM3526 emulators*/ +void ym3526_shutdown(void *chip); +void ym3526_reset_chip(void *chip); +int ym3526_write(void *chip, int a, int v); +unsigned char ym3526_read(void *chip, int a); +int ym3526_timer_over(void *chip, int c); +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length); + +void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); +void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); +void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); + +#endif /* BUILD_YM3526 */ + + +#if BUILD_Y8950 + +/* Y8950 port handlers */ +void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param); +void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param); +void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size ); +void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart, + offs_t DataLength, const UINT8* ROMData); + +void * y8950_init(UINT32 clock, UINT32 rate); +void y8950_shutdown(void *chip); +void y8950_reset_chip(void *chip); +int y8950_write(void *chip, int a, int v); +unsigned char y8950_read (void *chip, int a); +int y8950_timer_over(void *chip, int c); +void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length); + +void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param); +void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param); +void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param); + +#endif /* BUILD_Y8950 */ + +void opl_set_mute_mask(void *chip, UINT32 MuteMask); + diff --git a/Frameworks/GME/vgmplay/chips/gb.c b/Frameworks/GME/vgmplay/chips/gb.c new file mode 100644 index 000000000..281816c7b --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/gb.c @@ -0,0 +1,920 @@ +/************************************************************************************** +* Game Boy sound emulation (c) Anthony Kruize (trandor@labyrinth.net.au) +* +* Anyways, sound on the Game Boy consists of 4 separate 'channels' +* Sound1 = Quadrangular waves with SWEEP and ENVELOPE functions (NR10,11,12,13,14) +* Sound2 = Quadrangular waves with ENVELOPE functions (NR21,22,23,24) +* Sound3 = Wave patterns from WaveRAM (NR30,31,32,33,34) +* Sound4 = White noise with an envelope (NR41,42,43,44) +* +* Each sound channel has 2 modes, namely ON and OFF... whoa +* +* These tend to be the two most important equations in +* converting between Hertz and GB frequency registers: +* (Sounds will have a 2.4% higher frequency on Super GB.) +* gb = 2048 - (131072 / Hz) +* Hz = 131072 / (2048 - gb) +* +* Changes: +* +* 10/2/2002 AK - Preliminary sound code. +* 13/2/2002 AK - Added a hack for mode 4, other fixes. +* 23/2/2002 AK - Use lookup tables, added sweep to mode 1. Re-wrote the square +* wave generation. +* 13/3/2002 AK - Added mode 3, better lookup tables, other adjustments. +* 15/3/2002 AK - Mode 4 can now change frequencies. +* 31/3/2002 AK - Accidently forgot to handle counter/consecutive for mode 1. +* 3/4/2002 AK - Mode 1 sweep can still occur if shift is 0. Don't let frequency +* go past the maximum allowed value. Fixed Mode 3 length table. +* Slight adjustment to Mode 4's period table generation. +* 5/4/2002 AK - Mode 4 is done correctly, using a polynomial counter instead +* of being a total hack. +* 6/4/2002 AK - Slight tweak to mode 3's frequency calculation. +* 13/4/2002 AK - Reset envelope value when sound is initialized. +* 21/4/2002 AK - Backed out the mode 3 frequency calculation change. +* Merged init functions into gameboy_sound_w(). +* 14/5/2002 AK - Removed magic numbers in the fixed point math. +* 12/6/2002 AK - Merged SOUNDx structs into one SOUND struct. +* 26/10/2002 AK - Finally fixed channel 3! +* +***************************************************************************************/ + +#include "mamedef.h" +#include // for rand +#include // for memset +//#include "emu.h" +#include "gb.h" +//#include "streams.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define NR10 0x00 +#define NR11 0x01 +#define NR12 0x02 +#define NR13 0x03 +#define NR14 0x04 +#define NR21 0x06 +#define NR22 0x07 +#define NR23 0x08 +#define NR24 0x09 +#define NR30 0x0A +#define NR31 0x0B +#define NR32 0x0C +#define NR33 0x0D +#define NR34 0x0E +#define NR41 0x10 +#define NR42 0x11 +#define NR43 0x12 +#define NR44 0x13 +#define NR50 0x14 +#define NR51 0x15 +#define NR52 0x16 +#define AUD3W0 0x20 +#define AUD3W1 0x21 +#define AUD3W2 0x22 +#define AUD3W3 0x23 +#define AUD3W4 0x24 +#define AUD3W5 0x25 +#define AUD3W6 0x26 +#define AUD3W7 0x27 +#define AUD3W8 0x28 +#define AUD3W9 0x29 +#define AUD3WA 0x2A +#define AUD3WB 0x2B +#define AUD3WC 0x2C +#define AUD3WD 0x2D +#define AUD3WE 0x2E +#define AUD3WF 0x2F + +#define LEFT 1 +#define RIGHT 2 +#define MAX_FREQUENCIES 2048 +#define FIXED_POINT 16 + +/* Represents wave duties of 12.5%, 25%, 50% and 75% */ +static const float wave_duty_table[4] = { 8.0f, 4.0f, 2.0f, 1.33333f }; + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct SOUND +{ + /* Common */ + UINT8 on; + UINT8 channel; + INT32 length; + INT32 pos; + //UINT32 pos; + UINT32 period; + INT32 count; + INT8 mode; + /* Mode 1, 2, 3 */ + INT8 duty; + /* Mode 1, 2, 4 */ + INT32 env_value; + INT8 env_direction; + INT32 env_length; + INT32 env_count; + INT8 signal; + /* Mode 1 */ + UINT32 frequency; + INT32 swp_shift; + INT32 swp_direction; + INT32 swp_time; + INT32 swp_count; + /* Mode 3 */ + INT8 level; + UINT8 offset; + UINT32 dutycount; + /* Mode 4 */ + INT32 ply_step; + INT16 ply_value; + UINT8 Muted; +}; + +struct SOUNDC +{ + UINT8 on; + UINT8 vol_left; + UINT8 vol_right; + UINT8 mode1_left; + UINT8 mode1_right; + UINT8 mode2_left; + UINT8 mode2_right; + UINT8 mode3_left; + UINT8 mode3_right; + UINT8 mode4_left; + UINT8 mode4_right; +}; + + +typedef struct _gb_sound_t gb_sound_t; +struct _gb_sound_t +{ + //sound_stream *channel; + //int rate; + UINT32 rate; // fixes bad calculations of length_mode3_table + + INT32 env_length_table[8]; + INT32 swp_time_table[8]; + UINT32 period_table[MAX_FREQUENCIES]; + UINT32 period_mode3_table[MAX_FREQUENCIES]; + UINT32 period_mode4_table[8][16]; + UINT32 length_table[64]; + UINT32 length_mode3_table[256]; + + struct SOUND snd_1; + struct SOUND snd_2; + struct SOUND snd_3; + struct SOUND snd_4; + struct SOUNDC snd_control; + + UINT8 snd_regs[0x30]; + + UINT8 LoudWaveChn; + UINT8 LowNoiseChn; + UINT8 AccuracyHack; +}; + + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*INLINE gb_sound_t *get_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == GAMEBOY); + return (gb_sound_t *) downcast(device)->token(); +}*/ + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +//static STREAM_UPDATE( gameboy_update ); + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +//READ8_DEVICE_HANDLER( gb_wave_r ) +UINT8 gb_wave_r(void *_info, offs_t offset) +{ + //gb_sound_t *gb = get_token(device); + gb_sound_t *gb = (gb_sound_t *)_info; + + /* TODO: properly emulate scrambling of wave ram area when playback is active */ + return ( gb->snd_regs[ AUD3W0 + offset ] | gb->snd_3.on ); +} + +//WRITE8_DEVICE_HANDLER( gb_wave_w ) +void gb_wave_w(void *_info, offs_t offset, UINT8 data) +{ + //gb_sound_t *gb = get_token(device); + gb_sound_t *gb = (gb_sound_t *)_info; + + gb->snd_regs[ AUD3W0 + offset ] = data; +} + +//READ8_DEVICE_HANDLER( gb_sound_r ) +UINT8 gb_sound_r(void *_info, offs_t offset) +{ + //gb_sound_t *gb = get_token(device); + gb_sound_t *gb = (gb_sound_t *)_info; + + switch( offset ) { + case 0x05: + case 0x0F: + return 0xFF; + case NR52: + return 0x70 | gb->snd_regs[offset]; + default: + return gb->snd_regs[offset]; + } +} + +//static void gb_sound_w_internal(running_device *device, int offset, UINT8 data ) +static void gb_sound_w_internal(gb_sound_t *gb, int offset, UINT8 data ) +{ + //gb_sound_t *gb = get_token(device); + + /* Store the value */ + gb->snd_regs[offset] = data; + + switch( offset ) + { + /*MODE 1 */ + case NR10: /* Sweep (R/W) */ + gb->snd_1.swp_shift = data & 0x7; + gb->snd_1.swp_direction = (data & 0x8) >> 3; + gb->snd_1.swp_direction |= gb->snd_1.swp_direction - 1; + gb->snd_1.swp_time = gb->swp_time_table[ (data & 0x70) >> 4 ]; + break; + case NR11: /* Sound length/Wave pattern duty (R/W) */ + gb->snd_1.duty = (data & 0xC0) >> 6; + gb->snd_1.length = gb->length_table[data & 0x3F]; + break; + case NR12: /* Envelope (R/W) */ + gb->snd_1.env_value = data >> 4; + gb->snd_1.env_direction = (data & 0x8) >> 3; + gb->snd_1.env_direction |= gb->snd_1.env_direction - 1; + gb->snd_1.env_length = gb->env_length_table[data & 0x7]; + break; + case NR13: /* Frequency lo (R/W) */ + gb->snd_1.frequency = ((gb->snd_regs[NR14]&0x7)<<8) | gb->snd_regs[NR13]; + gb->snd_1.period = gb->period_table[gb->snd_1.frequency]; + break; + case NR14: /* Frequency hi / Initialize (R/W) */ + gb->snd_1.mode = (data & 0x40) >> 6; + gb->snd_1.frequency = ((gb->snd_regs[NR14]&0x7)<<8) | gb->snd_regs[NR13]; + gb->snd_1.period = gb->period_table[gb->snd_1.frequency]; + if( data & 0x80 ) + { + if( !gb->snd_1.on ) + gb->snd_1.pos = 0; + gb->snd_1.on = 1; + gb->snd_1.count = 0; + gb->snd_1.env_value = gb->snd_regs[NR12] >> 4; + gb->snd_1.env_count = 0; + gb->snd_1.swp_count = 0; + gb->snd_1.signal = 0x1; + gb->snd_regs[NR52] |= 0x1; + } + break; + + /*MODE 2 */ + case NR21: /* Sound length/Wave pattern duty (R/W) */ + gb->snd_2.duty = (data & 0xC0) >> 6; + gb->snd_2.length = gb->length_table[data & 0x3F]; + break; + case NR22: /* Envelope (R/W) */ +// gb->snd_2.env_value = data >> 4; +// gb->snd_2.env_direction = (data & 0x8 ) >> 3; + // Thanks to Delek for the fix + gb->snd_2.env_direction = (data & 0x8) >> 3; + if (gb->snd_2.env_direction) + { + gb->snd_2.env_value ++; + if (gb->snd_2.env_value > 0x0F) + gb->snd_2.env_value = 0; + } + else + { + gb->snd_2.env_value = data >> 4; + } + gb->snd_2.env_direction |= gb->snd_2.env_direction - 1; + gb->snd_2.env_length = gb->env_length_table[data & 0x7]; + break; + case NR23: /* Frequency lo (R/W) */ + gb->snd_2.period = gb->period_table[((gb->snd_regs[NR24]&0x7)<<8) | gb->snd_regs[NR23]]; + break; + case NR24: /* Frequency hi / Initialize (R/W) */ + gb->snd_2.mode = (data & 0x40) >> 6; + gb->snd_2.period = gb->period_table[((gb->snd_regs[NR24]&0x7)<<8) | gb->snd_regs[NR23]]; + if( data & 0x80 ) + { + if( !gb->snd_2.on ) + gb->snd_2.pos = 0; + gb->snd_2.on = 1; + gb->snd_2.count = 0; + gb->snd_2.env_value = gb->snd_regs[NR22] >> 4; + gb->snd_2.env_count = 0; + gb->snd_2.signal = 0x1; + gb->snd_regs[NR52] |= 0x2; + } + break; + + /*MODE 3 */ + case NR30: /* Sound On/Off (R/W) */ + gb->snd_3.on = (data & 0x80) >> 7; + break; + case NR31: /* Sound Length (R/W) */ + gb->snd_3.length = gb->length_mode3_table[data]; + break; + case NR32: /* Select Output Level */ + gb->snd_3.level = (data & 0x60) >> 5; + break; + case NR33: /* Frequency lo (W) */ + gb->snd_3.period = gb->period_mode3_table[((gb->snd_regs[NR34]&0x7)<<8) + gb->snd_regs[NR33]]; + break; + case NR34: /* Frequency hi / Initialize (W) */ + gb->snd_3.mode = (data & 0x40) >> 6; + gb->snd_3.period = gb->period_mode3_table[((gb->snd_regs[NR34]&0x7)<<8) + gb->snd_regs[NR33]]; + if( data & 0x80 ) + { + if( !gb->snd_3.on ) + { + gb->snd_3.pos = 0; + gb->snd_3.offset = 0; + gb->snd_3.duty = 0; + } + gb->snd_3.on = 1; + gb->snd_3.count = 0; + gb->snd_3.duty = 1; + gb->snd_3.dutycount = 0; + gb->snd_regs[NR52] |= 0x4; + } + break; + + /*MODE 4 */ + case NR41: /* Sound Length (R/W) */ + gb->snd_4.length = gb->length_table[data & 0x3F]; + break; + case NR42: /* Envelope (R/W) */ + gb->snd_4.env_value = data >> 4; + gb->snd_4.env_direction = (data & 0x8 ) >> 3; + gb->snd_4.env_direction |= gb->snd_4.env_direction - 1; + gb->snd_4.env_length = gb->env_length_table[data & 0x7]; + break; + case NR43: /* Polynomial Counter/Frequency */ + gb->snd_4.period = gb->period_mode4_table[data & 0x7][(data & 0xF0) >> 4]; + gb->snd_4.ply_step = (data & 0x8) >> 3; + break; + case NR44: /* Counter/Consecutive / Initialize (R/W) */ + gb->snd_4.mode = (data & 0x40) >> 6; + if( data & 0x80 ) + { + if( !gb->snd_4.on ) + gb->snd_4.pos = 0; + gb->snd_4.on = 1; + gb->snd_4.count = 0; + gb->snd_4.env_value = gb->snd_regs[NR42] >> 4; + gb->snd_4.env_count = 0; + //gb->snd_4.signal = mame_rand(device->machine); + gb->snd_4.signal = rand() & 0xFF; + gb->snd_4.ply_value = 0x7fff; + gb->snd_regs[NR52] |= 0x8; + } + break; + + /* CONTROL */ + case NR50: /* Channel Control / On/Off / Volume (R/W) */ + gb->snd_control.vol_left = data & 0x7; + gb->snd_control.vol_right = (data & 0x70) >> 4; + break; + case NR51: /* Selection of Sound Output Terminal */ + gb->snd_control.mode1_right = data & 0x1; + gb->snd_control.mode1_left = (data & 0x10) >> 4; + gb->snd_control.mode2_right = (data & 0x2) >> 1; + gb->snd_control.mode2_left = (data & 0x20) >> 5; + gb->snd_control.mode3_right = (data & 0x4) >> 2; + gb->snd_control.mode3_left = (data & 0x40) >> 6; + gb->snd_control.mode4_right = (data & 0x8) >> 3; + gb->snd_control.mode4_left = (data & 0x80) >> 7; + break; + case NR52: /* Sound On/Off (R/W) */ + /* Only bit 7 is writable, writing to bits 0-3 does NOT enable or + disable sound. They are read-only */ + gb->snd_control.on = (data & 0x80) >> 7; + if( !gb->snd_control.on ) + { + gb_sound_w_internal( gb, NR10, 0x80 ); + gb_sound_w_internal( gb, NR11, 0x3F ); + gb_sound_w_internal( gb, NR12, 0x00 ); + gb_sound_w_internal( gb, NR13, 0xFE ); + gb_sound_w_internal( gb, NR14, 0xBF ); +// gb_sound_w_internal( gb, NR20, 0xFF ); + gb_sound_w_internal( gb, NR21, 0x3F ); + gb_sound_w_internal( gb, NR22, 0x00 ); + gb_sound_w_internal( gb, NR23, 0xFF ); + gb_sound_w_internal( gb, NR24, 0xBF ); + gb_sound_w_internal( gb, NR30, 0x7F ); + gb_sound_w_internal( gb, NR31, 0xFF ); + gb_sound_w_internal( gb, NR32, 0x9F ); + gb_sound_w_internal( gb, NR33, 0xFF ); + gb_sound_w_internal( gb, NR34, 0xBF ); +// gb_sound_w_internal( gb, NR40, 0xFF ); + gb_sound_w_internal( gb, NR41, 0xFF ); + gb_sound_w_internal( gb, NR42, 0x00 ); + gb_sound_w_internal( gb, NR43, 0x00 ); + gb_sound_w_internal( gb, NR44, 0xBF ); + gb_sound_w_internal( gb, NR50, 0x00 ); + gb_sound_w_internal( gb, NR51, 0x00 ); + gb->snd_1.on = 0; + gb->snd_2.on = 0; + gb->snd_3.on = 0; + gb->snd_4.on = 0; + gb->snd_regs[offset] = 0; + } + break; + } +} + +//WRITE8_DEVICE_HANDLER( gb_sound_w ) +void gb_sound_w(void *_info, offs_t offset, UINT8 data) +{ + //gb_sound_t *gb = get_token(device); + gb_sound_t *gb = (gb_sound_t *)_info; + + /* change in registers so update first */ + //stream_update(gb->channel); + + if (offset < AUD3W0) + { + /* Only register NR52 is accessible if the sound controller is disabled */ + if( !gb->snd_control.on && offset != NR52 ) + { + return; + } + + gb_sound_w_internal( gb, offset, data ); + } + else if (offset <= AUD3WF) + { + gb->snd_regs[offset] = data; + } +} + + + +//static STREAM_UPDATE( gameboy_update ) +void gameboy_update(void *_info, stream_sample_t **outputs, int samples) +{ + //gb_sound_t *gb = get_token(device); + gb_sound_t *gb = (gb_sound_t *)_info; + stream_sample_t *outl = outputs[0]; + stream_sample_t *outr = outputs[1]; + stream_sample_t sample, left, right, mode4_mask; + + while( samples-- > 0 ) + { + left = right = 0; + + /* Mode 1 - Wave with Envelope and Sweep */ + if( gb->snd_1.on && ! gb->snd_1.Muted ) + { + sample = gb->snd_1.signal * gb->snd_1.env_value; + if (! gb->AccuracyHack) + { + gb->snd_1.pos++; + if( gb->snd_1.pos == (UINT32)(gb->snd_1.period / wave_duty_table[gb->snd_1.duty]) >> FIXED_POINT) + { + gb->snd_1.signal = -gb->snd_1.signal; + } + else if( gb->snd_1.pos > (gb->snd_1.period >> FIXED_POINT) ) + { + gb->snd_1.pos = 0; + gb->snd_1.signal = -gb->snd_1.signal; + } + } + else + { + // accuracy hack - makes high frequencies sound better + gb->snd_1.pos += 1 << FIXED_POINT; + if( (gb->snd_1.pos >> FIXED_POINT) == (UINT32)(gb->snd_1.period / wave_duty_table[gb->snd_1.duty]) >> FIXED_POINT) + { + gb->snd_1.signal = -gb->snd_1.signal; + } + else if( gb->snd_1.pos >= gb->snd_1.period ) + { + gb->snd_1.pos -= gb->snd_1.period; + gb->snd_1.signal = -gb->snd_1.signal; + } + } + + if( gb->snd_1.length && gb->snd_1.mode ) + { + gb->snd_1.count++; + if( gb->snd_1.count >= gb->snd_1.length ) + { + gb->snd_1.on = 0; + gb->snd_regs[NR52] &= 0xFE; + } + } + + if( gb->snd_1.env_length ) + { + gb->snd_1.env_count++; + if( gb->snd_1.env_count >= gb->snd_1.env_length ) + { + gb->snd_1.env_count = 0; + gb->snd_1.env_value += gb->snd_1.env_direction; + if( gb->snd_1.env_value < 0 ) + gb->snd_1.env_value = 0; + if( gb->snd_1.env_value > 15 ) + gb->snd_1.env_value = 15; + } + } + + if( gb->snd_1.swp_time ) + { + gb->snd_1.swp_count++; + if( gb->snd_1.swp_count >= gb->snd_1.swp_time ) + { + gb->snd_1.swp_count = 0; + if( gb->snd_1.swp_direction > 0 ) + { + gb->snd_1.frequency -= gb->snd_1.frequency / (1 << gb->snd_1.swp_shift ); + if( gb->snd_1.frequency <= 0 ) + { + gb->snd_1.on = 0; + gb->snd_regs[NR52] &= 0xFE; + } + } + else + { + gb->snd_1.frequency += gb->snd_1.frequency / (1 << gb->snd_1.swp_shift ); + if( gb->snd_1.frequency >= MAX_FREQUENCIES ) + { + gb->snd_1.frequency = MAX_FREQUENCIES - 1; + } + } + + gb->snd_1.period = gb->period_table[gb->snd_1.frequency]; + } + } + + if( gb->snd_control.mode1_left ) + left += sample; + if( gb->snd_control.mode1_right ) + right += sample; + } + + /* Mode 2 - Wave with Envelope */ + if( gb->snd_2.on && ! gb->snd_2.Muted ) + { + sample = gb->snd_2.signal * gb->snd_2.env_value; + if (! gb->AccuracyHack) + { + gb->snd_2.pos++; + if( gb->snd_2.pos == (UINT32)(gb->snd_2.period / wave_duty_table[gb->snd_2.duty]) >> FIXED_POINT) + { + gb->snd_2.signal = -gb->snd_2.signal; + } + else if( gb->snd_2.pos > (gb->snd_2.period >> FIXED_POINT) ) + { + gb->snd_2.pos = 0; + gb->snd_2.signal = -gb->snd_2.signal; + } + } + else + { + gb->snd_2.pos += 1 << FIXED_POINT; + if( (gb->snd_2.pos >> FIXED_POINT) == (UINT32)(gb->snd_2.period / wave_duty_table[gb->snd_2.duty]) >> FIXED_POINT) + { + gb->snd_2.signal = -gb->snd_2.signal; + } + else if( gb->snd_2.pos >= gb->snd_2.period ) + { + gb->snd_2.pos -= gb->snd_2.period; + gb->snd_2.signal = -gb->snd_2.signal; + } + } + + if( gb->snd_2.length && gb->snd_2.mode ) + { + gb->snd_2.count++; + if( gb->snd_2.count >= gb->snd_2.length ) + { + gb->snd_2.on = 0; + gb->snd_regs[NR52] &= 0xFD; + } + } + + if( gb->snd_2.env_length ) + { + gb->snd_2.env_count++; + if( gb->snd_2.env_count >= gb->snd_2.env_length ) + { + gb->snd_2.env_count = 0; + gb->snd_2.env_value += gb->snd_2.env_direction; + if( gb->snd_2.env_value < 0 ) + gb->snd_2.env_value = 0; + if( gb->snd_2.env_value > 15 ) + gb->snd_2.env_value = 15; + } + } + + if( gb->snd_control.mode2_left ) + left += sample; + if( gb->snd_control.mode2_right ) + right += sample; + } + + /* Mode 3 - Wave patterns from WaveRAM */ + if( gb->snd_3.on && ! gb->snd_3.Muted ) + { + /* NOTE: This is extremely close, but not quite right. + The problem is for GB frequencies above 2000 the frequency gets + clipped. This is caused because gb->snd_3.pos is never 0 at the test.*/ + sample = gb->snd_regs[AUD3W0 + (gb->snd_3.offset/2)]; + if( !(gb->snd_3.offset % 2) ) + { + sample >>= 4; + } + sample = (sample & 0xF) - 8; + if (gb->LoudWaveChn) + sample <<= 1; + + if( gb->snd_3.level ) + sample >>= (gb->snd_3.level - 1); + else + sample = 0; + + if (! gb->AccuracyHack) + { + gb->snd_3.pos++; + if( gb->snd_3.pos >= ((UINT32)(((gb->snd_3.period ) >> 21)) + gb->snd_3.duty) ) + { + gb->snd_3.pos = 0; + if( gb->snd_3.dutycount == ((UINT32)(((gb->snd_3.period ) >> FIXED_POINT)) % 32) ) + { + gb->snd_3.duty--; + } + gb->snd_3.dutycount++; + gb->snd_3.offset++; + if( gb->snd_3.offset > 31 ) + { + gb->snd_3.offset = 0; + gb->snd_3.duty = 1; + gb->snd_3.dutycount = 0; + } + } + } + else + { + gb->snd_3.pos += 1 << 21; + if( gb->snd_3.pos >= (UINT32)gb->snd_3.period) + { + gb->snd_3.pos -= (UINT32)gb->snd_3.period; + gb->snd_3.dutycount++; + gb->snd_3.offset++; + if( gb->snd_3.offset > 31 ) + { + gb->snd_3.offset = 0; + gb->snd_3.dutycount = 0; + } + } + } + + if( gb->snd_3.length && gb->snd_3.mode ) + { + gb->snd_3.count++; + if( gb->snd_3.count >= gb->snd_3.length ) + { + gb->snd_3.on = 0; + gb->snd_regs[NR52] &= 0xFB; + } + } + + if( gb->snd_control.mode3_left ) + left += sample; + if( gb->snd_control.mode3_right ) + right += sample; + } + + /* Mode 4 - Noise with Envelope */ + if( gb->snd_4.on && ! gb->snd_4.Muted ) + { + /* Similar problem to Mode 3, we seem to miss some notes */ + sample = gb->snd_4.signal & gb->snd_4.env_value; + sample -= gb->snd_4.env_value / 2; // make Bipolar + if (! gb->LowNoiseChn) + sample <<= 1; // that's more like VisualBoy Advance (and sounds better) + gb->snd_4.pos++; + if( gb->snd_4.pos == (gb->snd_4.period >> (FIXED_POINT + 1)) ) + { + /* Using a Polynomial Counter (aka Linear Feedback Shift Register) + Mode 4 has a 7 bit and 15 bit counter so we need to shift the + bits around accordingly */ + mode4_mask = (((gb->snd_4.ply_value & 0x2) >> 1) ^ (gb->snd_4.ply_value & 0x1)) << (gb->snd_4.ply_step ? 6 : 14); + gb->snd_4.ply_value >>= 1; + gb->snd_4.ply_value |= mode4_mask; + gb->snd_4.ply_value &= (gb->snd_4.ply_step ? 0x7f : 0x7fff); + gb->snd_4.signal = (INT8)gb->snd_4.ply_value; + } + else if( gb->snd_4.pos > (gb->snd_4.period >> FIXED_POINT) ) + { + gb->snd_4.pos = 0; + mode4_mask = (((gb->snd_4.ply_value & 0x2) >> 1) ^ (gb->snd_4.ply_value & 0x1)) << (gb->snd_4.ply_step ? 6 : 14); + gb->snd_4.ply_value >>= 1; + gb->snd_4.ply_value |= mode4_mask; + gb->snd_4.ply_value &= (gb->snd_4.ply_step ? 0x7f : 0x7fff); + gb->snd_4.signal = (INT8)gb->snd_4.ply_value; + } + + if( gb->snd_4.length && gb->snd_4.mode ) + { + gb->snd_4.count++; + if( gb->snd_4.count >= gb->snd_4.length ) + { + gb->snd_4.on = 0; + gb->snd_regs[NR52] &= 0xF7; + } + } + + if( gb->snd_4.env_length ) + { + gb->snd_4.env_count++; + if( gb->snd_4.env_count >= gb->snd_4.env_length ) + { + gb->snd_4.env_count = 0; + gb->snd_4.env_value += gb->snd_4.env_direction; + if( gb->snd_4.env_value < 0 ) + gb->snd_4.env_value = 0; + if( gb->snd_4.env_value > 15 ) + gb->snd_4.env_value = 15; + } + } + + if( gb->snd_control.mode4_left ) + left += sample; + if( gb->snd_control.mode4_right ) + right += sample; + } + + /* Adjust for master volume */ + left *= gb->snd_control.vol_left; + right *= gb->snd_control.vol_right; + + /* pump up the volume */ + left <<= 6; + right <<= 6; + + /* Update the buffers */ + *(outl++) = left; + *(outr++) = right; + } + + gb->snd_regs[NR52] = (gb->snd_regs[NR52]&0xf0) | gb->snd_1.on | (gb->snd_2.on << 1) | (gb->snd_3.on << 2) | (gb->snd_4.on << 3); +} + + +//static DEVICE_START( gameboy_sound ) +int device_start_gameboy_sound(void **_info, int clock, int Flags, int SampleRate) +{ + //gb_sound_t *gb = get_token(device); + gb_sound_t *gb; + int I, J; + + gb = (gb_sound_t *) calloc(1, sizeof(gb_sound_t)); + *_info = (void *) gb; + + gb->LoudWaveChn = (Flags & 0x01) >> 0; + gb->LowNoiseChn = (Flags & 0x02) >> 1; + gb->AccuracyHack = ! ((Flags & 0x04) >> 2); + + memset(&gb->snd_1, 0, sizeof(gb->snd_1)); + memset(&gb->snd_2, 0, sizeof(gb->snd_2)); + memset(&gb->snd_3, 0, sizeof(gb->snd_3)); + memset(&gb->snd_4, 0, sizeof(gb->snd_4)); + + //gb->channel = stream_create(device, 0, 2, device->machine->sample_rate, 0, gameboy_update); + //gb->rate = device->machine->sample_rate; + gb->rate = SampleRate; + + /* Calculate the envelope and sweep tables */ + for( I = 0; I < 8; I++ ) + { + gb->env_length_table[I] = (I * ((1 << FIXED_POINT) / 64) * gb->rate) >> FIXED_POINT; + gb->swp_time_table[I] = (((I << FIXED_POINT) / 128) * gb->rate) >> (FIXED_POINT - 1); + } + + /* Calculate the period tables */ + for( I = 0; I < MAX_FREQUENCIES; I++ ) + { + gb->period_table[I] = ((1 << FIXED_POINT) / (131072 / (2048 - I))) * gb->rate; + gb->period_mode3_table[I] = ((1 << FIXED_POINT) / (65536 / (2048 - I))) * gb->rate; + } + /* Calculate the period table for mode 4 */ + for( I = 0; I < 8; I++ ) + { + for( J = 0; J < 16; J++ ) + { + /* I is the dividing ratio of frequencies + J is the shift clock frequency */ + gb->period_mode4_table[I][J] = ((1 << FIXED_POINT) / (524288 / ((I == 0)?0.5:I) / (1 << (J + 1)))) * gb->rate; + } + } + + /* Calculate the length table */ + for( I = 0; I < 64; I++ ) + { + gb->length_table[I] = ((64 - I) * ((1 << FIXED_POINT)/256) * gb->rate) >> FIXED_POINT; + } + /* Calculate the length table for mode 3 */ + for( I = 0; I < 256; I++ ) + { + gb->length_mode3_table[I] = ((256 - I) * ((1 << FIXED_POINT)/256) * gb->rate) >> FIXED_POINT; + } + + gb->snd_1.Muted = 0x00; + gb->snd_2.Muted = 0x00; + gb->snd_3.Muted = 0x00; + gb->snd_4.Muted = 0x00; + + return gb->rate; +} + +void device_stop_gameboy_sound(void *_info) +{ + free(_info); + return; +} + +void device_reset_gameboy_sound(void *_info) +{ + gb_sound_t *gb = (gb_sound_t *)_info; + + // moved there from device_start + gb_sound_w_internal( gb, NR52, 0x00 ); + gb->snd_regs[AUD3W0] = 0xac; + gb->snd_regs[AUD3W1] = 0xdd; + gb->snd_regs[AUD3W2] = 0xda; + gb->snd_regs[AUD3W3] = 0x48; + gb->snd_regs[AUD3W4] = 0x36; + gb->snd_regs[AUD3W5] = 0x02; + gb->snd_regs[AUD3W6] = 0xcf; + gb->snd_regs[AUD3W7] = 0x16; + gb->snd_regs[AUD3W8] = 0x2c; + gb->snd_regs[AUD3W9] = 0x04; + gb->snd_regs[AUD3WA] = 0xe5; + gb->snd_regs[AUD3WB] = 0x2c; + gb->snd_regs[AUD3WC] = 0xac; + gb->snd_regs[AUD3WD] = 0xdd; + gb->snd_regs[AUD3WE] = 0xda; + gb->snd_regs[AUD3WF] = 0x48; + + return; +} + +void gameboy_sound_set_mute_mask(void *_info, UINT32 MuteMask) +{ + gb_sound_t *gb = (gb_sound_t *)_info; + + gb->snd_1.Muted = (MuteMask >> 0) & 0x01; + gb->snd_2.Muted = (MuteMask >> 1) & 0x01; + gb->snd_3.Muted = (MuteMask >> 2) & 0x01; + gb->snd_4.Muted = (MuteMask >> 3) & 0x01; + + return; +} + + +/*DEVICE_GET_INFO( gameboy_sound ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(gb_sound_t); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(gameboy_sound); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "LR35902"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + } +}*/ + +//DEFINE_LEGACY_SOUND_DEVICE(GAMEBOY, gameboy_sound); diff --git a/Frameworks/GME/vgmplay/chips/gb.h b/Frameworks/GME/vgmplay/chips/gb.h new file mode 100644 index 000000000..9fe971303 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/gb.h @@ -0,0 +1,13 @@ + +/* Custom Sound Interface */ +UINT8 gb_wave_r(void *chip, offs_t offset); +void gb_wave_w(void *chip, offs_t offset, UINT8 data); +UINT8 gb_sound_r(void *chip, offs_t offset); +void gb_sound_w(void *chip, offs_t offset, UINT8 data); + +void gameboy_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_gameboy_sound(void **chip, int clock, int flags, int samplerate); +void device_stop_gameboy_sound(void *chip); +void device_reset_gameboy_sound(void *chip); + +void gameboy_sound_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/iremga20.c b/Frameworks/GME/vgmplay/chips/iremga20.c new file mode 100644 index 000000000..34024b8a4 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/iremga20.c @@ -0,0 +1,367 @@ +/********************************************************* + +Irem GA20 PCM Sound Chip + +It's not currently known whether this chip is stereo. + + +Revisions: + +04-15-2002 Acho A. Tang +- rewrote channel mixing +- added prelimenary volume and sample rate emulation + +05-30-2002 Acho A. Tang +- applied hyperbolic gain control to volume and used + a musical-note style progression in sample rate + calculation(still very inaccurate) + +02-18-2004 R. Belmont +- sample rate calculation reverse-engineered. + Thanks to Fujix, Yasuhiro Ogawa, the Guru, and Tormod + for real PCB samples that made this possible. + +02-03-2007 R. Belmont +- Cleaned up faux x86 assembly. + +*********************************************************/ + +//#include "emu.h" +#include +#include +#include // for NULL +#include "mamedef.h" +#include "iremga20.h" + +#define MAX_VOL 256 + +struct IremGA20_channel_def +{ + UINT32 rate; + //UINT32 size; + UINT32 start; + UINT32 pos; + UINT32 frac; + UINT32 end; + UINT32 volume; + UINT32 pan; + //UINT32 effect; + UINT8 play; + UINT8 Muted; +}; + +typedef struct _ga20_state ga20_state; +struct _ga20_state +{ + UINT8 *rom; + UINT32 rom_size; + //sound_stream * stream; + UINT16 regs[0x40]; + struct IremGA20_channel_def channel[4]; +}; + + +/*INLINE ga20_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == IREMGA20); + return (ga20_state *)downcast(device)->token(); +}*/ + + +//static STREAM_UPDATE( IremGA20_update ) +void IremGA20_update(void *param, stream_sample_t **outputs, int samples) +{ + ga20_state *chip = (ga20_state *)param; + UINT32 rate[4], pos[4], frac[4], end[4], vol[4], play[4]; + UINT8 *pSamples; + stream_sample_t *outL, *outR; + int i, sampleout; + + /* precache some values */ + for (i=0; i < 4; i++) + { + rate[i] = chip->channel[i].rate; + pos[i] = chip->channel[i].pos; + frac[i] = chip->channel[i].frac; + end[i] = chip->channel[i].end - 0x20; + vol[i] = chip->channel[i].volume; + play[i] = (! chip->channel[i].Muted) ? chip->channel[i].play : 0; + } + + i = samples; + pSamples = chip->rom; + outL = outputs[0]; + outR = outputs[1]; + + for (i = 0; i < samples; i++) + { + sampleout = 0; + + // update the 4 channels inline + if (play[0]) + { + sampleout += (pSamples[pos[0]] - 0x80) * vol[0]; + frac[0] += rate[0]; + pos[0] += frac[0] >> 24; + frac[0] &= 0xffffff; + play[0] = (pos[0] < end[0]); + } + if (play[1]) + { + sampleout += (pSamples[pos[1]] - 0x80) * vol[1]; + frac[1] += rate[1]; + pos[1] += frac[1] >> 24; + frac[1] &= 0xffffff; + play[1] = (pos[1] < end[1]); + } + if (play[2]) + { + sampleout += (pSamples[pos[2]] - 0x80) * vol[2]; + frac[2] += rate[2]; + pos[2] += frac[2] >> 24; + frac[2] &= 0xffffff; + play[2] = (pos[2] < end[2]); + } + if (play[3]) + { + sampleout += (pSamples[pos[3]] - 0x80) * vol[3]; + frac[3] += rate[3]; + pos[3] += frac[3] >> 24; + frac[3] &= 0xffffff; + play[3] = (pos[3] < end[3]); + } + + sampleout >>= 2; + outL[i] = sampleout; + outR[i] = sampleout; + } + + /* update the regs now */ + for (i=0; i < 4; i++) + { + chip->channel[i].pos = pos[i]; + chip->channel[i].frac = frac[i]; + if (! chip->channel[i].Muted) + chip->channel[i].play = play[i]; + } +} + +//WRITE8_DEVICE_HANDLER( irem_ga20_w ) +void irem_ga20_w(void *_info, offs_t offset, UINT8 data) +{ + //ga20_state *chip = get_safe_token(device); + ga20_state *chip = (ga20_state *)_info; + int channel; + + //logerror("GA20: Offset %02x, data %04x\n",offset,data); + + //chip->stream->update(); + + channel = offset >> 3; + + chip->regs[offset] = data; + + switch (offset & 0x7) + { + case 0: /* start address low */ + chip->channel[channel].start = ((chip->channel[channel].start)&0xff000) | (data<<4); + break; + + case 1: /* start address high */ + chip->channel[channel].start = ((chip->channel[channel].start)&0x00ff0) | (data<<12); + break; + + case 2: /* end address low */ + chip->channel[channel].end = ((chip->channel[channel].end)&0xff000) | (data<<4); + break; + + case 3: /* end address high */ + chip->channel[channel].end = ((chip->channel[channel].end)&0x00ff0) | (data<<12); + break; + + case 4: + chip->channel[channel].rate = 0x1000000 / (256 - data); + break; + + case 5: //AT: gain control + chip->channel[channel].volume = (data * MAX_VOL) / (data + 10); + break; + + case 6: //AT: this is always written 2(enabling both channels?) + chip->channel[channel].play = data; + chip->channel[channel].pos = chip->channel[channel].start; + chip->channel[channel].frac = 0; + break; + } +} + +//READ8_DEVICE_HANDLER( irem_ga20_r ) +UINT8 irem_ga20_r(void *_info, offs_t offset) +{ + //ga20_state *chip = get_safe_token(device); + ga20_state *chip = (ga20_state *)_info; + int channel; + + //chip->stream->update(); + + channel = offset >> 3; + + switch (offset & 0x7) + { + case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo) + return chip->channel[channel].play ? 1 : 0; + + default: + logerror("GA20: read unk. register %d, channel %d\n", offset & 0xf, channel); + break; + } + + return 0; +} + +static void iremga20_reset(ga20_state *chip) +{ + int i; + + for( i = 0; i < 4; i++ ) { + chip->channel[i].rate = 0; + //chip->channel[i].size = 0; + chip->channel[i].start = 0; + chip->channel[i].pos = 0; + chip->channel[i].frac = 0; + chip->channel[i].end = 0; + chip->channel[i].volume = 0; + chip->channel[i].pan = 0; + //chip->channel[i].effect = 0; + chip->channel[i].play = 0; + } +} + + +//static DEVICE_RESET( iremga20 ) +void device_reset_iremga20(void *_info) +{ + //iremga20_reset(get_safe_token(device)); + ga20_state *chip = (ga20_state *)_info; + + iremga20_reset(chip); + memset(chip->regs, 0x00, 0x40 * sizeof(UINT16)); +} + +//static DEVICE_START( iremga20 ) +int device_start_iremga20(void **_info, int clock) +{ + //ga20_state *chip = get_safe_token(device); + ga20_state *chip; + int i; + + chip = (ga20_state *) calloc(1, sizeof(ga20_state)); + *_info = (void *) chip; + + /* Initialize our chip structure */ + //chip->rom = *device->region(); + //chip->rom_size = device->region()->bytes(); + chip->rom = NULL; + chip->rom_size = 0x00; + + iremga20_reset(chip); + + for ( i = 0; i < 0x40; i++ ) + chip->regs[i] = 0; + + //chip->stream = device->machine().sound().stream_alloc( *device, 0, 2, device->clock()/4, chip, IremGA20_update ); + + /*device->save_item(NAME(chip->regs)); + for (i = 0; i < 4; i++) + { + device->save_item(NAME(chip->channel[i].rate), i); + device->save_item(NAME(chip->channel[i].size), i); + device->save_item(NAME(chip->channel[i].start), i); + device->save_item(NAME(chip->channel[i].pos), i); + device->save_item(NAME(chip->channel[i].end), i); + device->save_item(NAME(chip->channel[i].volume), i); + device->save_item(NAME(chip->channel[i].pan), i); + device->save_item(NAME(chip->channel[i].effect), i); + device->save_item(NAME(chip->channel[i].play), i); + }*/ + for (i = 0; i < 4; i ++) + chip->channel[i].Muted = 0x00; + + return clock / 4; +} + +void device_stop_iremga20(void *_info) +{ + ga20_state *chip = (ga20_state *)_info; + + free(chip->rom); chip->rom = NULL; + + free(chip); + + return; +} + +void iremga20_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + ga20_state *chip = (ga20_state *)_info; + + if (chip->rom_size != ROMSize) + { + chip->rom = (UINT8*)realloc(chip->rom, ROMSize); + chip->rom_size = ROMSize; + memset(chip->rom, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(chip->rom + DataStart, ROMData, DataLength); + + return; +} + + +void iremga20_set_mute_mask(void *_info, UINT32 MuteMask) +{ + ga20_state *chip = (ga20_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 4; CurChn ++) + chip->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( iremga20 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ga20_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( iremga20 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( iremga20 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "Irem GA20"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Irem custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(IREMGA20, iremga20);*/ diff --git a/Frameworks/GME/vgmplay/chips/iremga20.h b/Frameworks/GME/vgmplay/chips/iremga20.h new file mode 100644 index 000000000..871b8e3d2 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/iremga20.h @@ -0,0 +1,30 @@ +/********************************************************* + + Irem GA20 PCM Sound Chip + +*********************************************************/ +#pragma once + +#ifndef __IREMGA20_H__ +#define __IREMGA20_H__ + +//#include "devlegcy.h" + +void IremGA20_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_iremga20(void **chip, int clock); +void device_stop_iremga20(void *chip); +void device_reset_iremga20(void *chip); + +//WRITE8_DEVICE_HANDLER( irem_ga20_w ); +//READ8_DEVICE_HANDLER( irem_ga20_r ); +UINT8 irem_ga20_r(void *chip, offs_t offset); +void irem_ga20_w(void *chip, offs_t offset, UINT8 data); + +void iremga20_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void iremga20_set_mute_mask(void *chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(IREMGA20, iremga20); + +#endif /* __IREMGA20_H__ */ diff --git a/Frameworks/GME/vgmplay/chips/k051649.c b/Frameworks/GME/vgmplay/chips/k051649.c new file mode 100644 index 000000000..705779526 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/k051649.c @@ -0,0 +1,426 @@ +/*************************************************************************** + + Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber + + This file is pieced together by Bryan McPhail from a combination of + Namco Sound, Amuse by Cab, Haunted Castle schematics and whoever first + figured out SCC! + + The 051649 is a 5 channel sound generator, each channel gets its + waveform from RAM (32 bytes per waveform, 8 bit signed data). + + This sound chip is the same as the sound chip in some Konami + megaROM cartridges for the MSX. It is actually well researched + and documented: + + http://bifi.msxnet.org/msxnet/tech/scc.html + + Thanks to Sean Young (sean@mess.org) for some bugfixes. + + K052539 is more or less equivalent to this chip except channel 5 + does not share waveram with channel 4. + +***************************************************************************/ + +#include "mamedef.h" +#include +#include +//#include "emu.h" +//#include "streams.h" +#include "k051649.h" + +#define FREQ_BITS 16 +#define DEF_GAIN 8 + +/* this structure defines the parameters for a channel */ +typedef struct +{ + unsigned long counter; + int frequency; + int volume; + int key; + signed char waveram[32]; /* 19991207.CAB */ + UINT8 Muted; +} k051649_sound_channel; + +typedef struct _k051649_state k051649_state; +struct _k051649_state +{ + k051649_sound_channel channel_list[5]; + + /* global sound parameters */ + //sound_stream * stream; + int mclock,rate; + + /* mixer tables and internal buffers */ + INT16 *mixer_table; + INT16 *mixer_lookup; + short *mixer_buffer; + + int cur_reg; + UINT8 test; +}; + +/*INLINE k051649_state *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == K051649); + return (k051649_state *)downcast(device)->token(); +}*/ + +/* build a table to divide by the number of voices */ +static void make_mixer_table(/*running_machine *machine,*/ k051649_state *info, int voices) +{ + int count = voices * 256; + int i; + + /* allocate memory */ + //info->mixer_table = auto_alloc_array(machine, INT16, 512 * voices); + info->mixer_table = (INT16*)malloc(sizeof(INT16) * 2 * count); + + /* find the middle of the table */ + info->mixer_lookup = info->mixer_table + count; + + /* fill in the table - 16 bit case */ + for (i = 0; i < count; i++) + { + int val = i * DEF_GAIN * 16 / voices; + //if (val > 32767) val = 32767; + if (val > 32768) val = 32768; + info->mixer_lookup[ i] = val; + info->mixer_lookup[-i] = -val; + } +} + + +/* generate sound to the mix buffer */ +//static STREAM_UPDATE( k051649_update ) +void k051649_update(void *param, stream_sample_t **outputs, int samples) +{ + k051649_state *info = (k051649_state *)param; + k051649_sound_channel *voice=info->channel_list; + stream_sample_t *buffer = outputs[0]; + stream_sample_t *buffer2 = outputs[1]; + short *mix; + int i,j; + + // zap the contents of the mixer buffer + memset(info->mixer_buffer, 0, samples * sizeof(short)); + + for (j=0; j<5; j++) { + // channel is halted for freq < 9 + if (voice[j].frequency > 8 && ! voice[j].Muted) + { + const signed char *w = voice[j].waveram; /* 19991207.CAB */ + int v=voice[j].volume * voice[j].key; + int c=voice[j].counter; + /* Amuse source: Cab suggests this method gives greater resolution */ + /* Sean Young 20010417: the formula is really: f = clock/(16*(f+1))*/ + int step = (int)(((INT64)info->mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (info->rate / 32)) + 0.5); + + mix = info->mixer_buffer; + + // add our contribution + for (i = 0; i < samples; i++) + { + int offs; + + c += step; + offs = (c >> FREQ_BITS) & 0x1f; + *mix++ += (w[offs] * v)>>3; + } + + // update the counter for this voice + voice[j].counter = c; + } + } + + // mix it down + mix = info->mixer_buffer; + for (i = 0; i < samples; i++) + *buffer++ = *buffer2++ = info->mixer_lookup[*mix++]; +} + +//static DEVICE_START( k051649 ) +int device_start_k051649(void **_info, int clock) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info; + UINT8 CurChn; + + info = (k051649_state *) calloc(1, sizeof(k051649_state)); + *_info = (void *) info; + + /* get stream channels */ + //info->rate = device->clock()/16; + info->rate = clock/16; + //info->stream = stream_create(device, 0, 1, info->rate, info, k051649_update); + //info->mclock = device->clock(); + info->mclock = clock; + + /* allocate a buffer to mix into - 1 second's worth should be more than enough */ + //info->mixer_buffer = auto_alloc_array(device->machine, short, 2 * info->rate); + info->mixer_buffer = (short*)malloc(sizeof(short) * info->rate); + + /* build the mixer table */ + //make_mixer_table(device->machine, info, 5); + make_mixer_table(info, 5); + + for (CurChn = 0; CurChn < 5; CurChn ++) + info->channel_list[CurChn].Muted = 0x00; + + return info->rate; +} + +void device_stop_k051649(void *_info) +{ + k051649_state *info = (k051649_state *)_info; + + free(info->mixer_buffer); + free(info->mixer_table); + + free(info); + + return; +} + +//static DEVICE_RESET( k051649 ) +void device_reset_k051649(void *_info) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + k051649_sound_channel *voice = info->channel_list; + int i; + + // reset all the voices + for (i = 0; i < 5; i++) + { + voice[i].frequency = 0; + voice[i].volume = 0; + voice[i].counter = 0; + voice[i].key = 0; + } + + // other parameters + info->test = 0x00; + info->cur_reg = 0x00; + + return; +} + +/********************************************************************************/ + +//WRITE8_DEVICE_HANDLER( k051649_waveform_w ) +void k051649_waveform_w(void *_info, offs_t offset, UINT8 data) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + + // waveram is read-only? + if (info->test & 0x40 || (info->test & 0x80 && offset >= 0x60)) + return; + + //stream_update(info->stream); + + if (offset >= 0x60) + { + // channel 5 shares waveram with channel 4 + info->channel_list[3].waveram[offset&0x1f]=data; + info->channel_list[4].waveram[offset&0x1f]=data; + } + else + info->channel_list[offset>>5].waveram[offset&0x1f]=data; +} + +//READ8_DEVICE_HANDLER ( k051649_waveform_r ) +UINT8 k051649_waveform_r(void *_info, offs_t offset) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + + // test-register bits 6/7 expose the internal counter + if (info->test & 0xc0) + { + //stream_update(info->stream); + + if (offset >= 0x60) + offset += (info->channel_list[3 + (info->test >> 6 & 1)].counter >> FREQ_BITS); + else if (info->test & 0x40) + offset += (info->channel_list[offset>>5].counter >> FREQ_BITS); + } + return info->channel_list[offset>>5].waveram[offset&0x1f]; +} + +/* SY 20001114: Channel 5 doesn't share the waveform with channel 4 on this chip */ +//WRITE8_DEVICE_HANDLER( k052539_waveform_w ) +void k052539_waveform_w(void *_info, offs_t offset, UINT8 data) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + + // waveram is read-only? + if (info->test & 0x40) + return; + + //stream_update(info->stream); + info->channel_list[offset>>5].waveram[offset&0x1f]=data; +} + +//READ8_DEVICE_HANDLER ( k052539_waveform_r ) +UINT8 k052539_waveform_r(void *_info, offs_t offset) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + + // test-register bit 6 exposes the internal counter + if (info->test & 0x40) + { + //stream_update(info->stream); + offset += (info->channel_list[offset>>5].counter >> FREQ_BITS); + } + return info->channel_list[offset>>5].waveram[offset&0x1f]; +} + +//WRITE8_DEVICE_HANDLER( k051649_volume_w ) +void k051649_volume_w(void *_info, offs_t offset, UINT8 data) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + //stream_update(info->stream); + info->channel_list[offset&0x7].volume=data&0xf; +} + +//WRITE8_DEVICE_HANDLER( k051649_frequency_w ) +void k051649_frequency_w(void *_info, offs_t offset, UINT8 data) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + k051649_sound_channel* chn = &info->channel_list[offset >> 1]; + + //stream_update(info->stream); + + // test-register bit 5 resets the internal counter + if (info->test & 0x20) + chn->counter = ~0; + else if (chn->frequency < 9) + chn->counter |= ((1 << FREQ_BITS) - 1); + + // update frequency + if (offset & 1) + chn->frequency = (chn->frequency & 0x0FF) | ((data << 8) & 0xF00); + else + chn->frequency = (chn->frequency & 0xF00) | (data << 0); + chn->counter &= 0xFFFF0000; // Valley Bell: Behaviour according to openMSX +} + +//WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ) +void k051649_keyonoff_w(void *_info, offs_t offset, UINT8 data) +{ + //k051649_state *info = get_safe_token(device); + k051649_state *info = (k051649_state *)_info; + int i; + //stream_update(info->stream); + + for (i = 0; i < 5; i++) + { + info->channel_list[i].key=data&1; + data >>= 1; + } +} + +//WRITE8_MEMBER( k051649_device::k051649_test_w ) +void k051649_test_w(void *_info, offs_t offset, UINT8 data) +{ + k051649_state *info = (k051649_state *)_info; + info->test = data; +} + + +//READ8_MEMBER ( k051649_device::k051649_test_r ) +UINT8 k051649_test_r(void *info, offs_t offset) +{ + // reading the test register sets it to $ff! + k051649_test_w(info, offset, 0xff); + return 0xff; +} + + +void k051649_w(void *_info, offs_t offset, UINT8 data) +{ + k051649_state *info = (k051649_state *)_info; + + switch(offset & 1) + { + case 0x00: + info->cur_reg = data; + break; + case 0x01: + switch(offset >> 1) + { + case 0x00: + k051649_waveform_w(info, info->cur_reg, data); + break; + case 0x01: + k051649_frequency_w(info, info->cur_reg, data); + break; + case 0x02: + k051649_volume_w(info, info->cur_reg, data); + break; + case 0x03: + k051649_keyonoff_w(info, info->cur_reg, data); + break; + case 0x04: + k052539_waveform_w(info, info->cur_reg, data); + break; + case 0x05: + k051649_test_w(info, info->cur_reg, data); + break; + } + break; + } + + return; +} + + +void k051649_set_mute_mask(void *_info, UINT32 MuteMask) +{ + k051649_state *info = (k051649_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 5; CurChn ++) + info->channel_list[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( k051649 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k051649_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k051649 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( k051649 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "K051649"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + + +//DEFINE_LEGACY_SOUND_DEVICE(K051649, k051649); diff --git a/Frameworks/GME/vgmplay/chips/k051649.h b/Frameworks/GME/vgmplay/chips/k051649.h new file mode 100644 index 000000000..50faff592 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/k051649.h @@ -0,0 +1,39 @@ +#pragma once + +//#ifndef __K051649_H__ +//#define __K051649_H__ + +//#include "devlegcy.h" + +/*WRITE8_DEVICE_HANDLER( k051649_waveform_w ); +READ8_DEVICE_HANDLER( k051649_waveform_r ); +WRITE8_DEVICE_HANDLER( k051649_volume_w ); +WRITE8_DEVICE_HANDLER( k051649_frequency_w ); +WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ); + +WRITE8_DEVICE_HANDLER( k052539_waveform_w ); + +DECLARE_LEGACY_SOUND_DEVICE(K051649, k051649);*/ + +void k051649_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_k051649(void **chip, int clock); +void device_stop_k051649(void *chip); +void device_reset_k051649(void *chip); + +void k051649_waveform_w(void *chip, offs_t offset, UINT8 data); +UINT8 k051649_waveform_r(void *chip, offs_t offset); +void k051649_volume_w(void *chip, offs_t offset, UINT8 data); +void k051649_frequency_w(void *chip, offs_t offset, UINT8 data); +void k051649_keyonoff_w(void *chip, offs_t offset, UINT8 data); + +void k052539_waveform_w(void *chip, offs_t offset, UINT8 data); +UINT8 k052539_waveform_r(void *chip, offs_t offset); + +void k051649_test_w(void *chip, offs_t offset, UINT8 data); +UINT8 k051649_test_r(void *chip, offs_t offset); + +void k051649_w(void *chip, offs_t offset, UINT8 data); + +void k051649_set_mute_mask(void *chip, UINT32 MuteMask); + +//#endif /* __K051649_H__ */ diff --git a/Frameworks/GME/gme/k053260.c b/Frameworks/GME/vgmplay/chips/k053260.c similarity index 75% rename from Frameworks/GME/gme/k053260.c rename to Frameworks/GME/vgmplay/chips/k053260.c index 51fa789df..aafa06d19 100644 --- a/Frameworks/GME/gme/k053260.c +++ b/Frameworks/GME/vgmplay/chips/k053260.c @@ -86,10 +86,10 @@ static void InitDeltaTable( k053260_state *ic, int rate, int clock ) } //static DEVICE_RESET( k053260 ) -void device_reset_k053260(void *chip) +void device_reset_k053260(void *_info) { //k053260_state *ic = get_safe_token(device); - k053260_state *ic = (k053260_state *) chip; + k053260_state *ic = (k053260_state *)_info; int i; for( i = 0; i < 4; i++ ) { @@ -122,19 +122,25 @@ INLINE int limit( int val, int max, int min ) #define MINOUT -0x8000 //static STREAM_UPDATE( k053260_update ) -void k053260_update(void *chip, stream_sample_t **outputs, int samples) +void k053260_update(void *param, stream_sample_t **outputs, int samples) { - static const long dpcmcnv[] = { 0,1,2,4,8,16,32,64, -128, -64, -32, -16, -8, -4, -2, -1}; + static const INT8 dpcmcnv[] = { 0,1,2,4,8,16,32,64, -128, -64, -32, -16, -8, -4, -2, -1}; - int i, j, lvol[4], rvol[4], play[4], loop[4], ppcm_data[4], ppcm[4]; - unsigned char *rom[4]; + int i, j, lvol[4], rvol[4], play[4], loop[4], ppcm[4]; + UINT8 *rom[4]; UINT32 delta[4], end[4], pos[4]; + INT8 ppcm_data[4]; int dataL, dataR; - signed char d; - k053260_state *ic = (k053260_state *) chip; + INT8 d; + k053260_state *ic = (k053260_state *)param; /* precache some values */ for ( i = 0; i < 4; i++ ) { + if (ic->channels[i].Muted) + { + play[i] = 0; + continue; + } rom[i]= &ic->rom[ic->channels[i].start + ( ic->channels[i].bank << 16 )]; delta[i] = ic->delta_table[ic->channels[i].rate]; lvol[i] = ic->channels[i].volume * ic->channels[i].pan; @@ -183,13 +189,14 @@ void k053260_update(void *chip, stream_sample_t **outputs, int samples) newdata = ( ( rom[i][pos[i] >> BASE_SHIFT] ) ) & 0x0f; /*low nybble*/ } - ppcm_data[i] = (( ( ppcm_data[i] * 62 ) >> 6 ) + dpcmcnv[newdata]); + /*ppcm_data[i] = (( ( ppcm_data[i] * 62 ) >> 6 ) + dpcmcnv[newdata]); if ( ppcm_data[i] > 127 ) ppcm_data[i] = 127; else if ( ppcm_data[i] < -128 ) - ppcm_data[i] = -128; + ppcm_data[i] = -128;*/ + ppcm_data[i] += dpcmcnv[newdata]; } @@ -216,6 +223,8 @@ void k053260_update(void *chip, stream_sample_t **outputs, int samples) /* update the regs now */ for ( i = 0; i < 4; i++ ) { + if (ic->channels[i].Muted) + continue; ic->channels[i].pos = pos[i]; ic->channels[i].play = play[i]; ic->channels[i].ppcm_data = ppcm_data[i]; @@ -223,7 +232,7 @@ void k053260_update(void *chip, stream_sample_t **outputs, int samples) } //static DEVICE_START( k053260 ) -void * device_start_k053260(int clock) +int device_start_k053260(void **_info, int clock) { //static const k053260_interface defintrf = { 0 }; //k053260_state *ic = get_safe_token(device); @@ -233,6 +242,7 @@ void * device_start_k053260(int clock) int i; ic = (k053260_state *) calloc(1, sizeof(k053260_state)); + *_info = (void *) ic; /* Initialize our chip structure */ //ic->device = device; @@ -284,26 +294,32 @@ void * device_start_k053260(int clock) //if ( ic->intf->irq ) // device->machine().scheduler().timer_pulse( attotime::from_hz(device->clock()) * 32, ic->intf->irq, "ic->intf->irq" ); - return ic; //return rate; + for (i = 0; i < 4; i ++) + ic->channels[i].Muted = 0x00; + + return rate; } -void device_stop_k053260(void *chip) +void device_stop_k053260(void *_info) { - k053260_state *ic = (k053260_state *) chip; + k053260_state *ic = (k053260_state *)_info; free(ic->delta_table); free(ic->rom); ic->rom = NULL; - free(ic); + + free(ic); + + return; } INLINE void check_bounds( k053260_state *ic, int channel ) { - UINT32 channel_start = ( ic->channels[channel].bank << 16 ) + ic->channels[channel].start; - UINT32 channel_end = channel_start + ic->channels[channel].size - 1; + int channel_start = ( ic->channels[channel].bank << 16 ) + ic->channels[channel].start; + int channel_end = channel_start + ic->channels[channel].size - 1; if ( channel_start > ic->rom_size ) { - /*logerror("K53260: Attempting to start playing past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end );*/ + logerror("K53260: Attempting to start playing past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end ); ic->channels[channel].play = 0; @@ -311,24 +327,25 @@ INLINE void check_bounds( k053260_state *ic, int channel ) } if ( channel_end > ic->rom_size ) { - /*logerror("K53260: Attempting to play past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end );*/ + logerror("K53260: Attempting to play past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end ); ic->channels[channel].size = ic->rom_size - channel_start; } - /*if (LOG) logerror("K053260: Sample Start = %06x, Sample End = %06x, Sample rate = %04x, PPCM = %s\n", channel_start, channel_end, ic->channels[channel].rate, ic->channels[channel].ppcm ? "yes" : "no" );*/ + if (LOG) logerror("K053260: Sample Start = %06x, Sample End = %06x, Sample rate = %04x, PPCM = %s\n", channel_start, channel_end, ic->channels[channel].rate, ic->channels[channel].ppcm ? "yes" : "no" ); } //WRITE8_DEVICE_HANDLER( k053260_w ) -void k053260_w(void *chip, offs_t offset, UINT8 data) +void k053260_w(void *_info, offs_t offset, UINT8 data) { int i, t; int r = offset; int v = data; - k053260_state *ic = (k053260_state *) chip; + //k053260_state *ic = get_safe_token(device); + k053260_state *ic = (k053260_state *)_info; if ( r > 0x2f ) { - /*logerror("K053260: Writing past registers\n" );*/ + logerror("K053260: Writing past registers\n" ); return; } @@ -437,9 +454,10 @@ void k053260_w(void *chip, offs_t offset, UINT8 data) } //READ8_DEVICE_HANDLER( k053260_r ) -UINT8 k053260_r(void *chip, offs_t offset) +UINT8 k053260_r(void *_info, offs_t offset) { - k053260_state *ic = (k053260_state *) chip; + //k053260_state *ic = get_safe_token(device); + k053260_state *ic = (k053260_state *)_info; switch ( offset ) { case 0x29: /* channel status */ @@ -462,7 +480,7 @@ UINT8 k053260_r(void *chip, offs_t offset) if ( offs > ic->rom_size ) { //logerror("%s: K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", device->machine().describe_context(),offs,ic->rom_size ); - /*logerror("K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", offs,ic->rom_size );*/ + logerror("K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", offs,ic->rom_size ); return 0; } @@ -475,10 +493,10 @@ UINT8 k053260_r(void *chip, offs_t offset) return ic->regs[offset]; } -void k053260_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void k053260_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { - k053260_state *info = (k053260_state *) chip; + k053260_state *info = (k053260_state *)_info; if (info->rom_size != ROMSize) { @@ -497,11 +515,41 @@ void k053260_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t Data } -void k053260_set_mute_mask(void *chip, UINT32 MuteMask) +void k053260_set_mute_mask(void *_info, UINT32 MuteMask) { - k053260_state *info = (k053260_state *) chip; + k053260_state *info = (k053260_state *)_info; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) info->channels[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; } + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( k053260 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k053260_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k053260 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( k053260 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "K053260"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(K053260, k053260);*/ diff --git a/Frameworks/GME/gme/k053260.h b/Frameworks/GME/vgmplay/chips/k053260.h similarity index 52% rename from Frameworks/GME/gme/k053260.h rename to Frameworks/GME/vgmplay/chips/k053260.h index 25eb198ad..d48dd56b7 100644 --- a/Frameworks/GME/gme/k053260.h +++ b/Frameworks/GME/vgmplay/chips/k053260.h @@ -6,12 +6,6 @@ #pragma once -#include "mamedef.h" - -#ifdef __cplusplus -extern "C" { -#endif - //#include "devlegcy.h" /*typedef struct _k053260_interface k053260_interface; @@ -21,22 +15,18 @@ struct _k053260_interface { };*/ -void k053260_update(void *, stream_sample_t **outputs, int samples); -void * device_start_k053260(int clock); -void device_stop_k053260(void *); -void device_reset_k053260(void *); +void k053260_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_k053260(void **chip, int clock); +void device_stop_k053260(void *chip); +void device_reset_k053260(void *chip); //WRITE8_DEVICE_HANDLER( k053260_w ); //READ8_DEVICE_HANDLER( k053260_r ); -void k053260_w(void *, offs_t offset, UINT8 data); -UINT8 k053260_r(void *, offs_t offset); +void k053260_w(void *chip, offs_t offset, UINT8 data); +UINT8 k053260_r(void *chip, offs_t offset); -void k053260_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void k053260_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); -void k053260_set_mute_mask(void *, UINT32 MuteMask); +void k053260_set_mute_mask(void *chip, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(K053260, k053260); - -#ifdef __cplusplus -}; -#endif diff --git a/Frameworks/GME/gme/k054539.c b/Frameworks/GME/vgmplay/chips/k054539.c similarity index 56% rename from Frameworks/GME/gme/k054539.c rename to Frameworks/GME/vgmplay/chips/k054539.c index 63f35ae09..0ceabbd7e 100644 --- a/Frameworks/GME/gme/k054539.c +++ b/Frameworks/GME/vgmplay/chips/k054539.c @@ -1,26 +1,15 @@ /********************************************************* - Konami 054539 PCM Sound Chip + Konami 054539 (TOP) PCM Sound Chip A lot of information comes from Amuse. Big thanks to them. - - -CHANNEL_DEBUG enables the following keys: - - PAD. : toggle debug mode - PAD0 : toggle chip (0 / 1) - PAD4,6 : select channel (0 - 7) - PAD8,2 : adjust gain (00=0.0 10=1.0, 20=2.0, etc.) - PAD5 : reset gain factor to 1.0 - *********************************************************/ //#include "emu.h" #include #include -#define _USE_MATH_DEFINES #include #include "mamedef.h" #ifdef _DEBUG @@ -28,10 +17,8 @@ CHANNEL_DEBUG enables the following keys: #endif #include "k054539.h" -#undef NULL #define NULL ((void *)0) -#define CHANNEL_DEBUG 0 #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) @@ -52,19 +39,26 @@ CHANNEL_DEBUG enables the following keys: 00: type (b2-3), reverse (b5) 01: loop (b0) - 214: keyon (b0-7 = channel 0-7) - 215: keyoff "" - 22c: channel active? "" - 22d: data read/write port - 22e: rom/ram select (00..7f == rom banks, 80 = ram) - 22f: enable pcm (b0), disable register ram updating (b7) + 214: Key on (b0-7 = channel 0-7) + 215: Key off "" + 225: ? + 227: Timer frequency + 228: ? + 229: ? + 22a: ? + 22b: ? + 22c: Channel active? (b0-7 = channel 0-7) + 22d: Data read/write port + 22e: ROM/RAM select (00..7f == ROM banks, 80 = Reverb RAM) + 22f: Global control: + .......x - Enable PCM + ......x. - Timer related? + ...x.... - Enable ROM/RAM readback from 0x22d + ..x..... - Timer output enable? + x....... - Disable register RAM updates - The chip has a 0x4000 bytes reverb buffer (the ram from 0x22e). - The reverb delay is actually an offset in this buffer. This driver - uses some tricks (doubling the buffer size so that the longest - reverbs don't fold over the sound to output, and adding a space at - the end to fold back overflows in) to be able to do frame-based - rendering instead of sample-based. + The chip has an optional 0x8000 byte reverb buffer. + The reverb delay is actually an offset in this buffer. */ typedef struct _k054539_channel k054539_channel; @@ -114,18 +108,18 @@ struct _k054539_state { //* //void k054539_init_flags(device_t *device, int flags) -void k054539_init_flags(void *chip, int flags) +void k054539_init_flags(void *_info, int flags) { //k054539_state *info = get_safe_token(device); - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; info->k054539_flags = flags; } //void k054539_set_gain(device_t *device, int channel, double gain) -void k054539_set_gain(void *chip, int channel, double gain) +void k054539_set_gain(void *_info, int channel, double gain) { //k054539_state *info = get_safe_token(device); - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; if (gain >= 0) info->k054539_gain[channel] = gain; } //* @@ -148,10 +142,9 @@ static void k054539_keyoff(k054539_state *info, int channel) } //static STREAM_UPDATE( k054539_update ) -void k054539_update(void *chip, stream_sample_t **outputs, int samples) +void k054539_update(void *param, stream_sample_t **outputs, int samples) { - //k054539_state *info = (k054539_state *)param; - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)param; #define VOL_CAP 1.80 static const INT16 dpcm[16] = { @@ -159,113 +152,100 @@ void k054539_update(void *chip, stream_sample_t **outputs, int samples) -64<<8, -49<<8, -36<<8, -25<<8, -16<<8, -9<<8, -4<<8, -1<<8 }; - int ch, reverb_pos; - short *rbase; + INT16 *rbase = (INT16 *)info->ram; unsigned char *rom; UINT32 rom_mask; - + int i, ch; + double lval, rval; unsigned char *base1, *base2; k054539_channel *chan; - stream_sample_t *bufl, *bufr; - UINT32 cur_pos, cur_pfrac; - int cur_val, cur_pval; - int delta, rdelta, fdelta, pdelta; - int vol, bval, pan, i; - - double gain, lvol, rvol, rbvol; - - reverb_pos = info->reverb_pos; - rbase = (short *)(info->ram); + int delta, vol, bval, pan; + double cur_gain, lvol, rvol, rbvol; + int rdelta; + UINT32 cur_pos; + int fdelta, pdelta; + int cur_pfrac, cur_val, cur_pval; memset(outputs[0], 0, samples*sizeof(*outputs[0])); memset(outputs[1], 0, samples*sizeof(*outputs[1])); + if(!(info->regs[0x22f] & 1)) + return; + rom = info->rom; rom_mask = info->rom_mask; - if(!(info->regs[0x22f] & 1)) return; + for(i = 0; i != samples; i++) { + if(!(info->k054539_flags & K054539_DISABLE_REVERB)) + lval = rval = rbase[info->reverb_pos]; + else + lval = rval = 0; + rbase[info->reverb_pos] = 0; - info->reverb_pos = (reverb_pos + samples) & 0x3fff; + for(ch=0; ch<8; ch++) + if(info->regs[0x22c] & (1<Muted[ch]) { + base1 = info->regs + 0x20*ch; + base2 = info->regs + 0x200 + 0x2*ch; + chan = info->channels + ch; + delta = base1[0x00] | (base1[0x01] << 8) | (base1[0x02] << 16); - for(ch=0; ch<8; ch++) - if ((info->regs[0x22c] & (1<Muted[ch]) - { - base1 = info->regs + 0x20*ch; - base2 = info->regs + 0x200 + 0x2*ch; - chan = info->channels + ch; -//* - delta = base1[0x00] | (base1[0x01] << 8) | (base1[0x02] << 16); + vol = base1[0x03]; - vol = base1[0x03]; + bval = vol + base1[0x04]; + if (bval > 255) + bval = 255; - bval = vol + base1[0x04]; - if (bval > 255) bval = 255; + pan = base1[0x05]; + // DJ Main: 81-87 right, 88 middle, 89-8f left + if (pan >= 0x81 && pan <= 0x8f) + pan -= 0x81; + else if (pan >= 0x11 && pan <= 0x1f) + pan -= 0x11; + else + pan = 0x18 - 0x11; - pan = base1[0x05]; -// DJ Main: 81-87 right, 88 middle, 89-8f left -if (pan >= 0x81 && pan <= 0x8f) -pan -= 0x81; -else - if (pan >= 0x11 && pan <= 0x1f) pan -= 0x11; else pan = 0x18 - 0x11; + cur_gain = info->k054539_gain[ch]; - gain = info->k054539_gain[ch]; + lvol = info->voltab[vol] * info->pantab[pan] * cur_gain; + if (lvol > VOL_CAP) + lvol = VOL_CAP; - lvol = info->voltab[vol] * info->pantab[pan] * gain; - if (lvol > VOL_CAP) lvol = VOL_CAP; + rvol = info->voltab[vol] * info->pantab[0xe - pan] * cur_gain; + if (rvol > VOL_CAP) + rvol = VOL_CAP; - rvol = info->voltab[vol] * info->pantab[0xe - pan] * gain; - if (rvol > VOL_CAP) rvol = VOL_CAP; + rbvol= info->voltab[bval] * cur_gain / 2; + if (rbvol > VOL_CAP) + rbvol = VOL_CAP; - rbvol= info->voltab[bval] * gain / 2; - if (rbvol > VOL_CAP) rbvol = VOL_CAP; + rdelta = (base1[6] | (base1[7] << 8)) >> 3; + rdelta = (rdelta + info->reverb_pos) & 0x3fff; -/* - INT x FLOAT could be interpreted as INT x (int)FLOAT instead of (float)INT x FLOAT on some compilers - causing precision loss. (rdelta - 0x2000) wraps around on zero reverb and the scale factor should - actually be 1/freq_ratio because the target is an offset to the reverb buffer not sample source. -*/ - rdelta = (base1[6] | (base1[7] << 8)) >> 3; -// rdelta = (reverb_pos + (int)((rdelta - 0x2000) * info->freq_ratio)) & 0x3fff; - rdelta = (int)(rdelta + reverb_pos) & 0x3fff; + cur_pos = (base1[0x0c] | (base1[0x0d] << 8) | (base1[0x0e] << 16)) & rom_mask; - cur_pos = (base1[0x0c] | (base1[0x0d] << 8) | (base1[0x0e] << 16)) & rom_mask; + if(base2[0] & 0x20) { + delta = -delta; + fdelta = +0x10000; + pdelta = -1; + } else { + fdelta = -0x10000; + pdelta = +1; + } - bufl = outputs[0]; - bufr = outputs[1]; -//* + if(cur_pos != chan->pos) { + chan->pos = cur_pos; + cur_pfrac = 0; + cur_val = 0; + cur_pval = 0; + } else { + cur_pfrac = chan->pfrac; + cur_val = chan->val; + cur_pval = chan->pval; + } - if(base2[0] & 0x20) { - delta = -delta; - fdelta = +0x10000; - pdelta = -1; - } else { - fdelta = -0x10000; - pdelta = +1; - } - - if(cur_pos != chan->pos) { - chan->pos = cur_pos; - cur_pfrac = 0; - cur_val = 0; - cur_pval = 0; - } else { - cur_pfrac = chan->pfrac; - cur_val = chan->val; - cur_pval = chan->pval; - } - -#define UPDATE_CHANNELS \ - do { \ - *bufl++ += (INT16)(cur_val*lvol); \ - *bufr++ += (INT16)(cur_val*rvol); \ - rbase[rdelta++] += (INT16)(cur_val*rbvol); \ - rdelta &= 0x3fff; \ - } while(0) - - switch(base2[0] & 0xc) { - case 0x0: { // 8bit pcm - for(i=0; i>1]; - if(cur_val == 0x88) { - if(base2[1] & 1) { - cur_pos = ((base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) & rom_mask) << 1; - cur_val = rom[cur_pos>>1]; - if(cur_val != 0x88) - goto next_iter; - } - k054539_keyoff(info, ch); - goto end_channel_8; + if(cur_val == 0x88 && (base2[1] & 1)) { + cur_pos = ((base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) & rom_mask) << 1; + cur_val = rom[cur_pos>>1]; + } + if(cur_val == 0x88) { + k054539_keyoff(info, ch); + cur_val = 0; + break; } - next_iter: if(cur_pos & 1) cur_val >>= 4; else @@ -356,104 +324,35 @@ else cur_val = 32767; } - UPDATE_CHANNELS; + cur_pfrac >>= 1; + if(cur_pos & 1) + cur_pfrac |= 0x8000; + cur_pos >>= 1; + break; } - end_channel_8: - cur_pfrac >>= 1; - if(cur_pos & 1) - cur_pfrac |= 0x8000; - cur_pos >>= 1; - break; - } - default: - /*LOG(("Unknown sample type %x for channel %d\n", base2[0] & 0xc, ch));*/ - break; - } - chan->pos = cur_pos; - chan->pfrac = cur_pfrac; - chan->pval = cur_pval; - chan->val = cur_val; - if(k054539_regupdate(info)) { - base1[0x0c] = cur_pos & 0xff; - base1[0x0d] = cur_pos>> 8 & 0xff; - base1[0x0e] = cur_pos>>16 & 0xff; - } - } + default: + LOG(("Unknown sample type %x for channel %d\n", base2[0] & 0xc, ch)); + break; + } + lval += cur_val * lvol; + rval += cur_val * rvol; + rbase[(rdelta + info->reverb_pos) & 0x1fff] += (INT16)(cur_val*rbvol); - //* drivers should be given the option to disable reverb when things go terribly wrong - if(!(info->k054539_flags & K054539_DISABLE_REVERB)) - { - for(i=0; ipos = cur_pos; + chan->pfrac = cur_pfrac; + chan->pval = cur_pval; + chan->val = cur_val; + + if(k054539_regupdate(info)) { + base1[0x0c] = cur_pos & 0xff; + base1[0x0d] = cur_pos>> 8 & 0xff; + base1[0x0e] = cur_pos>>16 & 0xff; + } + } + info->reverb_pos = (info->reverb_pos + 1) & 0x1fff; + outputs[0][i] = (INT32)lval; + outputs[1][i] = (INT32)rval; } - - if(reverb_pos + samples > 0x4000) { - i = 0x4000 - reverb_pos; - memset(rbase + reverb_pos, 0, i*2); - memset(rbase, 0, (samples-i)*2); - } else - memset(rbase + reverb_pos, 0, samples*2); - - #if CHANNEL_DEBUG - { - static const char gc_msg[32] = "chip : "; - static int gc_active=0, gc_chip=0, gc_pos[2]={0,0}; - double *gc_fptr; - char *gc_cptr; - double gc_f0; - int gc_i, gc_j, gc_k, gc_l; - - if (device->machine().input().code_pressed_once(KEYCODE_DEL_PAD)) - { - gc_active ^= 1; - if (!gc_active) popmessage(NULL); - } - - if (gc_active) - { - if (device->machine().input().code_pressed_once(KEYCODE_0_PAD)) gc_chip ^= 1; - - gc_i = gc_pos[gc_chip]; - gc_j = 0; - if (device->machine().input().code_pressed_once(KEYCODE_4_PAD)) { gc_i--; gc_j = 1; } - if (device->machine().input().code_pressed_once(KEYCODE_6_PAD)) { gc_i++; gc_j = 1; } - if (gc_j) { gc_i &= 7; gc_pos[gc_chip] = gc_i; } - - if (device->machine().input().code_pressed_once(KEYCODE_5_PAD)) - info->k054539_gain[gc_i] = 1.0; - else - { - gc_fptr = &info->k054539_gain[gc_i]; - gc_f0 = *gc_fptr; - gc_j = 0; - if (device->machine().input().code_pressed_once(KEYCODE_2_PAD)) { gc_f0 -= 0.1; gc_j = 1; } - if (device->machine().input().code_pressed_once(KEYCODE_8_PAD)) { gc_f0 += 0.1; gc_j = 1; } - if (gc_j) { if (gc_f0 < 0) gc_f0 = 0; *gc_fptr = gc_f0; } - } - - gc_fptr = &info->k054539_gain[0] + 8; - gc_cptr = gc_msg + 7; - for (gc_j=-8; gc_j; gc_j++) - { - gc_k = (int)(gc_fptr[gc_j] * 10); - gc_l = gc_k / 10; - gc_k = gc_k % 10; - gc_cptr[0] = gc_l + '0'; - gc_cptr[1] = gc_k + '0'; - gc_cptr += 3; - } - gc_i = (gc_i + gc_i*2 + 6); - gc_msg[4] = gc_chip + '0'; - gc_msg[gc_i ] = '['; - gc_msg[gc_i+3] = ']'; - popmessage("%s", gc_msg); - gc_msg[gc_i+3] = gc_msg[gc_i] = ' '; - } - } - #endif } @@ -469,19 +368,18 @@ static int k054539_init_chip(k054539_state *info, int clock) { //int i; + if (clock < 1000000) // if < 1 MHz, then it's the sample rate, not the clock + clock *= 384; // (for backwards compatibility with old VGM logs) info->clock = clock; // most of these are done in device_reset // memset(info->regs, 0, sizeof(info->regs)); // memset(info->k054539_posreg_latch, 0, sizeof(info->k054539_posreg_latch)); //* info->k054539_flags |= K054539_UPDATE_AT_KEYON; //* make it default until proven otherwise - // Real size of 0x4000, the addon is to simplify the reverb buffer computations - //info->ram = auto_alloc_array(device->machine(), unsigned char, 0x4000*2+device->clock()/50*2); - info->ram = (unsigned char*)malloc(0x4000 * 2 + info->clock / 50 * 2); + info->ram = (unsigned char*)malloc(0x4000); // info->reverb_pos = 0; // info->cur_ptr = 0; - //memset(info->ram, 0, 0x4000*2+device->clock()/50*2); -// memset(info->ram, 0, 0x4000 * 2 + info->clock / 50 * 2); +// memset(info->ram, 0, 0x4000); /*const memory_region *region = (info->intf->rgnoverride != NULL) ? device->machine().region(info->intf->rgnoverride) : device->region(); info->rom = *region; @@ -502,20 +400,20 @@ static int k054539_init_chip(k054539_state *info, int clock) // 480 hz is TRUSTED by gokuparo disco stage - the looping sample doesn't line up otherwise // device->machine().scheduler().timer_pulse(attotime::from_hz(480), FUNC(k054539_irq), 0, info); - //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock(), info, k054539_update); + //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock() / 384, info, k054539_update); //device->save_item(NAME(info->regs)); //device->save_pointer(NAME(info->ram), 0x4000); //device->save_item(NAME(info->cur_ptr)); - return info->clock; + return info->clock / 384; } //WRITE8_DEVICE_HANDLER( k054539_w ) -void k054539_w(void *chip, offs_t offset, UINT8 data) +void k054539_w(void *_info, offs_t offset, UINT8 data) { //k054539_state *info = get_safe_token(device); - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; #if 0 int voice, reg; @@ -534,9 +432,7 @@ void k054539_w(void *chip, offs_t offset, UINT8 data) if(info->regs[0x22c] & (1<= 0xc && reg <= 0xe) - { return; - } } } #endif @@ -601,6 +497,17 @@ void k054539_w(void *chip, offs_t offset, UINT8 data) k054539_keyoff(info, ch); break; + /*case 0x227: + { + attotime period = attotime::from_hz((float)(38 + data) * (clock()/384.0f/14400.0f)) / 2.0f; + + m_timer->adjust(period, 0, period); + + m_timer_state = 0; + m_timer_handler(m_timer_state); + }*/ + break; + case 0x22d: if(regbase[0x22e] == 0x80) info->cur_zone[info->cur_ptr] = data; @@ -616,6 +523,14 @@ void k054539_w(void *chip, offs_t offset, UINT8 data) info->cur_limit = data == 0x80 ? 0x4000 : 0x20000; info->cur_ptr = 0; break; + + /*case 0x22f: + if (!(data & 0x20)) // Disable timer output? + { + m_timer_state = 0; + m_timer_handler(m_timer_state); + } + break;*/ default: #if 0 @@ -641,17 +556,15 @@ void k054539_w(void *chip, offs_t offset, UINT8 data) static void reset_zones(k054539_state *info) { int data = info->regs[0x22e]; - info->cur_zone = - data == 0x80 ? info->ram : - info->rom + 0x20000*data; + info->cur_zone = data == 0x80 ? info->ram : info->rom + 0x20000*data; info->cur_limit = data == 0x80 ? 0x4000 : 0x20000; } //READ8_DEVICE_HANDLER( k054539_r ) -UINT8 k054539_r(void *chip, offs_t offset) +UINT8 k054539_r(void *_info, offs_t offset) { //k054539_state *info = get_safe_token(device); - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; switch(offset) { case 0x22d: if(info->regs[0x22f] & 0x10) { @@ -665,14 +578,14 @@ UINT8 k054539_r(void *chip, offs_t offset) case 0x22c: break; default: - /*LOG(("K054539 read %03x\n", offset));*/ + LOG(("K054539 read %03x\n", offset)); break; } return info->regs[offset]; } //static DEVICE_START( k054539 ) -void * device_start_k054539(int clock) +int device_start_k054539(void **_info, int clock) { //static const k054539_interface defintrf = { 0 }; int i; @@ -680,6 +593,7 @@ void * device_start_k054539(int clock) k054539_state *info; info = (k054539_state *) calloc(1, sizeof(k054539_state)); + *_info = (void *) info; //info->device = device; for (i = 0; i < 8; i++) @@ -716,24 +630,25 @@ void * device_start_k054539(int clock) for (i = 0; i < 8; i ++) info->Muted[i] = 0x00; - - k054539_init_chip(info, clock); - return info; + return k054539_init_chip(info, clock); } -void device_stop_k054539(void *chip) +void device_stop_k054539(void *_info) { - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; free(info->rom); info->rom = NULL; - free(info->ram); + free(info->ram); info->ram = NULL; + free(info); + + return; } -void device_reset_k054539(void *chip) +void device_reset_k054539(void *_info) { - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; memset(info->regs, 0, sizeof(info->regs)); memset(info->k054539_posreg_latch, 0, sizeof(info->k054539_posreg_latch)); @@ -741,13 +656,15 @@ void device_reset_k054539(void *chip) info->reverb_pos = 0; info->cur_ptr = 0; - memset(info->ram, 0, 0x4000 * 2 + info->clock / 50 * 2); + memset(info->ram, 0, 0x4000); + + return; } -void k054539_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void k054539_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; if (info->rom_size != ROMSize) { @@ -773,12 +690,14 @@ void k054539_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t Data DataLength = ROMSize - DataStart; memcpy(info->rom + DataStart, ROMData, DataLength); + + return; } -void k054539_set_mute_mask(void *chip, UINT32 MuteMask) +void k054539_set_mute_mask(void *_info, UINT32 MuteMask) { - k054539_state *info = (k054539_state *) chip; + k054539_state *info = (k054539_state *)_info; UINT8 CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) @@ -786,3 +705,34 @@ void k054539_set_mute_mask(void *chip, UINT32 MuteMask) return; } + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( k054539 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k054539_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k054539 ); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: // nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "K054539"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + + +//DEFINE_LEGACY_SOUND_DEVICE(K054539, k054539); diff --git a/Frameworks/GME/gme/k054539.h b/Frameworks/GME/vgmplay/chips/k054539.h similarity index 69% rename from Frameworks/GME/gme/k054539.h rename to Frameworks/GME/vgmplay/chips/k054539.h index 5090403a4..6e6218219 100644 --- a/Frameworks/GME/gme/k054539.h +++ b/Frameworks/GME/vgmplay/chips/k054539.h @@ -6,8 +6,6 @@ #pragma once -#include "mamedef.h" - //#include "devlegcy.h" /*typedef struct _k054539_interface k054539_interface; @@ -18,20 +16,17 @@ struct _k054539_interface void (*irq)(device_t *); };*/ -#ifdef __cplusplus -extern "C" { -#endif -void k054539_update(void *, stream_sample_t **outputs, int samples); -void * device_start_k054539(int clock); -void device_stop_k054539(void *); -void device_reset_k054539(void *); +void k054539_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_k054539(void **chip, int clock); +void device_stop_k054539(void *chip); +void device_reset_k054539(void *chip); //WRITE8_DEVICE_HANDLER( k054539_w ); //READ8_DEVICE_HANDLER( k054539_r ); -void k054539_w(void *, offs_t offset, UINT8 data); -UINT8 k054539_r(void *, offs_t offset); +void k054539_w(void *chip, offs_t offset, UINT8 data); +UINT8 k054539_r(void *chip, offs_t offset); //* control flags, may be set at DRIVER_INIT(). #define K054539_RESET_FLAGS 0 @@ -40,7 +35,7 @@ UINT8 k054539_r(void *, offs_t offset); #define K054539_UPDATE_AT_KEYON 4 //void k054539_init_flags(device_t *device, int flags); -void k054539_init_flags(void *, int flags); +void k054539_init_flags(void *chip, int flags); /* Note that the eight PCM channels of a K054539 do not have separate @@ -55,16 +50,12 @@ void k054539_init_flags(void *, int flags); gain : 0.0=silent, 1.0=no gain, 2.0=twice as loud, etc. */ //void k054539_set_gain(device_t *device, int channel, double gain); -void k054539_set_gain(void *, int channel, double gain); +void k054539_set_gain(void *chip, int channel, double gain); -void k054539_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void k054539_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); -void k054539_set_mute_mask(void *, UINT32 MuteMask); +void k054539_set_mute_mask(void *chip, UINT32 MuteMask); //DECLARE_LEGACY_SOUND_DEVICE(K054539, k054539); - -#ifdef __cplusplus -}; -#endif diff --git a/Frameworks/GME/gme/mamedef.h b/Frameworks/GME/vgmplay/chips/mamedef.h similarity index 78% rename from Frameworks/GME/gme/mamedef.h rename to Frameworks/GME/vgmplay/chips/mamedef.h index 3c5ad267d..f3e8dffdc 100644 --- a/Frameworks/GME/gme/mamedef.h +++ b/Frameworks/GME/vgmplay/chips/mamedef.h @@ -1,6 +1,5 @@ -#ifndef GME_MAMEDEFS_H -#define GME_MAMEDEFS_H - +#ifndef __MAMEDEF_H__ +#define __MAMEDEF_H__ // typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) /* 8-bit values */ @@ -34,8 +33,10 @@ typedef UINT32 offs_t; /* stream_sample_t is used to represent a single sample in a sound stream */ typedef INT32 stream_sample_t; -#ifndef NULL -#define NULL ((void *)0) +#ifdef VGM_BIG_ENDIAN +#define BYTE_XOR_BE(x) (x) +#else +#define BYTE_XOR_BE(x) ((x) ^ 0x01) #endif #if defined(_MSC_VER) @@ -46,10 +47,14 @@ typedef INT32 stream_sample_t; #else #define INLINE static inline #endif -#define _USE_MATH_DEFINES -#include +#define M_PI 3.14159265358979323846 +#ifdef _DEBUG +#define logerror printf +#else #define logerror - - #endif + +typedef void (*SRATE_CALLBACK)(void*, UINT32); + +#endif // __MAMEDEF_H__ diff --git a/Frameworks/GME/vgmplay/chips/multipcm.c b/Frameworks/GME/vgmplay/chips/multipcm.c new file mode 100644 index 000000000..69df3eff5 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/multipcm.c @@ -0,0 +1,868 @@ +/* + * Sega System 32 Multi/Model 1/Model 2 custom PCM chip (315-5560) emulation. + * + * by Miguel Angel Horna (ElSemi) for Model 2 Emulator and MAME. + * Information by R.Belmont and the YMF278B (OPL4) manual. + * + * voice registers: + * 0: Pan + * 1: Index of sample + * 2: LSB of pitch (low 2 bits seem unused so) + * 3: MSB of pitch (ooooppppppppppxx) (o=octave (4 bit signed), p=pitch (10 bits), x=unused? + * 4: voice control: top bit = 1 for key on, 0 for key off + * 5: bit 0: 0: interpolate volume changes, 1: direct set volume, + bits 1-7 = volume attenuate (0=max, 7f=min) + * 6: LFO frequency + Phase LFO depth + * 7: Amplitude LFO size + * + * The first sample ROM contains a variable length table with 12 + * bytes per instrument/sample. This is very similar to the YMF278B. + * + * The first 3 bytes are the offset into the file (big endian). + * The next 2 are the loop start offset into the file (big endian) + * The next 2 are the 2's complement of the total sample size (big endian) + * The next byte is LFO freq + depth (copied to reg 6 ?) + * The next 3 are envelope params (Attack, Decay1 and 2, sustain level, release, Key Rate Scaling) + * The next byte is Amplitude LFO size (copied to reg 7 ?) + * + * TODO + * - The YM278B manual states that the chip supports 512 instruments. The MultiPCM probably supports them + * too but the high bit position is unknown (probably reg 2 low bit). Any game use more than 256? + * + */ + +//#include "emu.h" +//#include "streams.h" +#include "mamedef.h" +#include +#include +#include +#include "multipcm.h" + +#define NULL ((void *)0) + +//???? +#define MULTIPCM_CLOCKDIV (180.0) + +struct _Sample +{ + unsigned int Start; + unsigned int Loop; + unsigned int End; + unsigned char AR,DR1,DR2,DL,RR; + unsigned char KRS; + unsigned char LFOVIB; + unsigned char AM; +}; + +typedef enum {ATTACK,DECAY1,DECAY2,RELEASE} _STATE; +struct _EG +{ + int volume; // + _STATE state; + int step; + //step vals + int AR; //Attack + int D1R; //Decay1 + int D2R; //Decay2 + int RR; //Release + int DL; //Decay level +}; + +struct _LFO +{ + unsigned short phase; + UINT32 phase_step; + int *table; + int *scale; +}; + + +struct _SLOT +{ + unsigned char Num; + unsigned char Regs[8]; + int Playing; + struct _Sample *Sample; + unsigned int Base; + unsigned int offset; + unsigned int step; + unsigned int Pan,TL; + unsigned int DstTL; + int TLStep; + signed int Prev; + struct _EG EG; + struct _LFO PLFO; //Phase lfo + struct _LFO ALFO; //AM lfo + + UINT8 Muted; +}; + +typedef struct _MultiPCM MultiPCM; +struct _MultiPCM +{ + //sound_stream * stream; + struct _Sample Samples[0x200]; //Max 512 samples + struct _SLOT Slots[28]; + unsigned int CurSlot; + unsigned int Address; + unsigned int BankR,BankL; + float Rate; + UINT32 ROMMask; + UINT32 ROMSize; + INT8 *ROM; + //I include these in the chip because they depend on the chip clock + unsigned int ARStep[0x40],DRStep[0x40]; //Envelope step table + unsigned int FNS_Table[0x400]; //Frequency step table +}; + + +static UINT8 IsInit = 0x00; +static signed int LPANTABLE[0x800],RPANTABLE[0x800]; + +#define FIX(v) ((UINT32) ((float) (1<type() == MULTIPCM); + return (MultiPCM *)downcast(device)->token(); +}*/ + + +/******************************* + ENVELOPE SECTION +*******************************/ + +//Times are based on a 44100Hz timebase. It's adjusted to the actual sampling rate on startup + +static const double BaseTimes[64]={0,0,0,0,6222.95,4978.37,4148.66,3556.01,3111.47,2489.21,2074.33,1778.00,1555.74,1244.63,1037.19,889.02, +777.87,622.31,518.59,444.54,388.93,311.16,259.32,222.27,194.47,155.60,129.66,111.16,97.23,77.82,64.85,55.60, +48.62,38.91,32.43,27.80,24.31,19.46,16.24,13.92,12.15,9.75,8.12,6.98,6.08,4.90,4.08,3.49, +3.04,2.49,2.13,1.90,1.72,1.41,1.18,1.04,0.91,0.73,0.59,0.50,0.45,0.45,0.45,0.45}; +#define AR2DR 14.32833 +static signed int lin2expvol[0x400]; +static int TLSteps[2]; + +#define EG_SHIFT 16 + +static int EG_Update(struct _SLOT *slot) +{ + switch(slot->EG.state) + { + case ATTACK: + slot->EG.volume+=slot->EG.AR; + if(slot->EG.volume>=(0x3ff<EG.state=DECAY1; + if(slot->EG.D1R>=(0x400<EG.state=DECAY2; + slot->EG.volume=0x3ff<EG.volume-=slot->EG.D1R; + if(slot->EG.volume<=0) + slot->EG.volume=0; + if(slot->EG.volume>>EG_SHIFT<=(slot->EG.DL<<(10-4))) + slot->EG.state=DECAY2; + break; + case DECAY2: + slot->EG.volume-=slot->EG.D2R; + if(slot->EG.volume<=0) + slot->EG.volume=0; + break; + case RELEASE: + slot->EG.volume-=slot->EG.RR; + if(slot->EG.volume<=0) + { + slot->EG.volume=0; + slot->Playing=0; + } + break; + default: + return 1<EG.volume>>EG_SHIFT]; +} + +static unsigned int Get_RATE(unsigned int *Steps,unsigned int rate,unsigned int val) +{ + int r=4*val+rate; + if(val==0) + return Steps[0]; + if(val==0xf) + return Steps[0x3f]; + if(r>0x3f) + r=0x3f; + return Steps[r]; +} + +static void EG_Calc(MultiPCM *ptChip,struct _SLOT *slot) +{ + int octave=((slot->Regs[3]>>4)-1)&0xf; + int rate; + if(octave&8) octave=octave-16; + if(slot->Sample->KRS!=0xf) + rate=(octave+slot->Sample->KRS)*2+((slot->Regs[3]>>3)&1); + else + rate=0; + + slot->EG.AR=Get_RATE(ptChip->ARStep,rate,slot->Sample->AR); + slot->EG.D1R=Get_RATE(ptChip->DRStep,rate,slot->Sample->DR1); + slot->EG.D2R=Get_RATE(ptChip->DRStep,rate,slot->Sample->DR2); + slot->EG.RR=Get_RATE(ptChip->DRStep,rate,slot->Sample->RR); + slot->EG.DL=0xf-slot->Sample->DL; + +} + +/***************************** + LFO SECTION +*****************************/ + +#define LFO_SHIFT 8 + + +#define LFIX(v) ((unsigned int) ((float) (1<phase+=LFO->phase_step; + p=LFO->table[(LFO->phase>>LFO_SHIFT)&0xff]; + p=LFO->scale[p+128]; + return p<<(SHIFT-LFO_SHIFT); +} + +INLINE signed int ALFO_Step(struct _LFO *LFO) +{ + int p; + LFO->phase+=LFO->phase_step; + p=LFO->table[(LFO->phase>>LFO_SHIFT)&0xff]; + p=LFO->scale[p]; + return p<<(SHIFT-LFO_SHIFT); +} + +static void LFO_ComputeStep(MultiPCM *ptChip,struct _LFO *LFO,UINT32 LFOF,UINT32 LFOS,int ALFO) +{ + float step=(float) LFOFreq[LFOF]*256.0/(float) ptChip->Rate; + LFO->phase_step=(unsigned int) ((float) (1<table=ALFO_TRI; + LFO->scale=ASCALES[LFOS]; + } + else + { + LFO->table=PLFO_TRI; + LFO->scale=PSCALES[LFOS]; + } +} + + + +static void WriteSlot(MultiPCM *ptChip,struct _SLOT *slot,int reg,unsigned char data) +{ + slot->Regs[reg]=data; + + switch(reg) + { + case 0: //PANPOT + slot->Pan=(data>>4)&0xf; + break; + case 1: //Sample + //according to YMF278 sample write causes some base params written to the regs (envelope+lfos) + //the game should never change the sample while playing. + { + struct _Sample *Sample=ptChip->Samples+slot->Regs[1]; + WriteSlot(ptChip,slot,6,Sample->LFOVIB); + WriteSlot(ptChip,slot,7,Sample->AM); + } + break; + case 2: //Pitch + case 3: + { + unsigned int oct=((slot->Regs[3]>>4)-1)&0xf; + unsigned int pitch=((slot->Regs[3]&0xf)<<6)|(slot->Regs[2]>>2); + pitch=ptChip->FNS_Table[pitch]; + if(oct&0x8) + pitch>>=(16-oct); + else + pitch<<=oct; + slot->step=pitch/ptChip->Rate; + } + break; + case 4: //KeyOn/Off (and more?) + { + if(data&0x80) //KeyOn + { + slot->Sample=ptChip->Samples+slot->Regs[1]; + slot->Playing=1; + slot->Base=slot->Sample->Start; + slot->offset=0; + slot->Prev=0; + slot->TL=slot->DstTL<EG.state=ATTACK; + slot->EG.volume=0; + + if(slot->Base>=0x100000) + { + if(slot->Pan&8) + slot->Base=(slot->Base&0xfffff)|(ptChip->BankL); + else + slot->Base=(slot->Base&0xfffff)|(ptChip->BankR); + } + + } + else + { + if(slot->Playing) + { + if(slot->Sample->RR!=0xf) + slot->EG.state=RELEASE; + else + slot->Playing=0; + } + } + } + break; + case 5: //TL+Interpolation + { + slot->DstTL=(data>>1)&0x7f; + if(!(data&1)) //Interpolate TL + { + if((slot->TL>>SHIFT)>slot->DstTL) + slot->TLStep=TLSteps[0]; //decrease + else + slot->TLStep=TLSteps[1]; //increase + } + else + slot->TL=slot->DstTL<PLFO),(slot->Regs[6]>>3)&7,slot->Regs[6]&7,0); + LFO_ComputeStep(ptChip,&(slot->ALFO),(slot->Regs[6]>>3)&7,slot->Regs[7]&7,1); + } + } + break; + case 7: //ALFO + { + if(data) + { + LFO_ComputeStep(ptChip,&(slot->PLFO),(slot->Regs[6]>>3)&7,slot->Regs[6]&7,0); + LFO_ComputeStep(ptChip,&(slot->ALFO),(slot->Regs[6]>>3)&7,slot->Regs[7]&7,1); + } + } + break; + } +} + +//static STREAM_UPDATE( MultiPCM_update ) +void MultiPCM_update(void *param, stream_sample_t **outputs, int samples) +{ + MultiPCM *ptChip = (MultiPCM *)param; + stream_sample_t *datap[2]; + int i,sl; + + datap[0] = outputs[0]; + datap[1] = outputs[1]; + + memset(datap[0], 0, sizeof(*datap[0])*samples); + memset(datap[1], 0, sizeof(*datap[1])*samples); + + + for(i=0;iSlots+sl; + if(slot->Playing && ! slot->Muted) + { + unsigned int vol=(slot->TL>>SHIFT)|(slot->Pan<<7); + unsigned int adr=slot->offset>>SHIFT; + signed int sample; + unsigned int step=slot->step; + signed int csample=(signed short) (ptChip->ROM[(slot->Base+adr) & ptChip->ROMMask]<<8); + signed int fpart=slot->offset&((1<Prev*((1<>SHIFT; + + if(slot->Regs[6]&7) //Vibrato enabled + { + step=step*PLFO_Step(&(slot->PLFO)); + step>>=SHIFT; + } + + slot->offset+=step; + if(slot->offset>=(slot->Sample->End<offset=slot->Sample->Loop<offset>>SHIFT)) + { + slot->Prev=csample; + } + + if((slot->TL>>SHIFT)!=slot->DstTL) + slot->TL+=slot->TLStep; + + if(slot->Regs[7]&7) //Tremolo enabled + { + sample=sample*ALFO_Step(&(slot->ALFO)); + sample>>=SHIFT; + } + + sample=(sample*EG_Update(slot))>>10; + + smpl+=(LPANTABLE[vol]*sample)>>SHIFT; + smpr+=(RPANTABLE[vol]*sample)>>SHIFT; + } + } +/*#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) + datap[0][i]=ICLIP16(smpl); + datap[1][i]=ICLIP16(smpr);*/ + datap[0][i] = smpl; + datap[1][i] = smpr; + } +} + +//READ8_DEVICE_HANDLER( multipcm_r ) +UINT8 multipcm_r(void *_info, offs_t offset) +{ +// MultiPCM *ptChip = get_safe_token(device); +// MultiPCM *ptChip = &MultiPCMData[ChipID]; + return 0; +} + +//static DEVICE_START( multipcm ) +int device_start_multipcm(void **_info, int clock) +{ + //MultiPCM *ptChip = get_safe_token(device); + MultiPCM *ptChip; + int i; + + ptChip = (MultiPCM *) calloc(1, sizeof(MultiPCM)); + *_info = (void *) ptChip; + + //ptChip->ROM=*device->region(); + ptChip->ROMMask = 0x00; + ptChip->ROMSize = 0x00; + ptChip->ROM = NULL; + //ptChip->Rate=(float) device->clock() / MULTIPCM_CLOCKDIV; + ptChip->Rate=(float) clock / MULTIPCM_CLOCKDIV; + + //ptChip->stream = stream_create(device, 0, 2, ptChip->Rate, ptChip, MultiPCM_update); + + if (! IsInit) + { + //Volume+pan table + for(i=0;i<0x800;++i) + { + float SegaDB=0; + float TL; + float LPAN,RPAN; + + unsigned char iTL=i&0x7f; + unsigned char iPAN=(i>>7)&0xf; + + SegaDB=(float) iTL*(-24.0)/(float) 0x40; + + TL=pow(10.0,SegaDB/20.0); + + + if(iPAN==0x8) + { + LPAN=RPAN=0.0; + } + else if(iPAN==0x0) + { + LPAN=RPAN=1.0; + } + else if(iPAN&0x8) + { + LPAN=1.0; + + iPAN=0x10-iPAN; + + SegaDB=(float) iPAN*(-12.0)/(float) 0x4; + + RPAN=pow(10.0,SegaDB/20.0); + + if((iPAN&0x7)==7) + RPAN=0.0; + } + else + { + RPAN=1.0; + + SegaDB=(float) iPAN*(-12.0)/(float) 0x4; + + LPAN=pow(10.0,SegaDB/20.0); + if((iPAN&0x7)==7) + LPAN=0.0; + } + + TL/=4.0; + + LPANTABLE[i]=FIX((LPAN*TL)); + RPANTABLE[i]=FIX((RPAN*TL)); + } + IsInit = 0x01; + } + + //Pitch steps + for(i=0;i<0x400;++i) + { + float fcent=ptChip->Rate*(1024.0+(float) i)/1024.0; + ptChip->FNS_Table[i]=(unsigned int ) ((float) (1<ARStep[i]=(float) (0x400<DRStep[i]=(float) (0x400<ARStep[0]=ptChip->ARStep[1]=ptChip->ARStep[2]=ptChip->ARStep[3]=0; + ptChip->ARStep[0x3f]=0x400<DRStep[0]=ptChip->DRStep[1]=ptChip->DRStep[2]=ptChip->DRStep[3]=0; + + //TL Interpolation steps + //lower + TLSteps[0]=-(float) (0x80<exponential ramps + for(i=0;i<0x400;++i) + { + float db=-(96.0-(96.0*(float) i/(float) 0x400)); + lin2expvol[i]=pow(10.0,db/20.0)*(float) (1<ROM+i*12; + ptChip->Samples[i].Start=(ptSample[0]<<16)|(ptSample[1]<<8)|(ptSample[2]<<0); + ptChip->Samples[i].Loop=(ptSample[3]<<8)|(ptSample[4]<<0); + ptChip->Samples[i].End=0xffff-((ptSample[5]<<8)|(ptSample[6]<<0)); + ptChip->Samples[i].LFOVIB=ptSample[7]; + ptChip->Samples[i].DR1=ptSample[8]&0xf; + ptChip->Samples[i].AR=(ptSample[8]>>4)&0xf; + ptChip->Samples[i].DR2=ptSample[9]&0xf; + ptChip->Samples[i].DL=(ptSample[9]>>4)&0xf; + ptChip->Samples[i].RR=ptSample[10]&0xf; + ptChip->Samples[i].KRS=(ptSample[10]>>4)&0xf; + ptChip->Samples[i].AM=ptSample[11]; + }*/ + + /*state_save_register_device_item(device, 0, ptChip->CurSlot); + state_save_register_device_item(device, 0, ptChip->Address); + state_save_register_device_item(device, 0, ptChip->BankL); + state_save_register_device_item(device, 0, ptChip->BankR);*/ + + // reset is done via DEVICE_RESET + /*for(i=0;i<28;++i) + { + ptChip->Slots[i].Num=i; + ptChip->Slots[i].Playing=0; + + state_save_register_device_item(device, i, ptChip->Slots[i].Num); + state_save_register_device_item_array(device, i, ptChip->Slots[i].Regs); + state_save_register_device_item(device, i, ptChip->Slots[i].Playing); + state_save_register_device_item(device, i, ptChip->Slots[i].Base); + state_save_register_device_item(device, i, ptChip->Slots[i].offset); + state_save_register_device_item(device, i, ptChip->Slots[i].step); + state_save_register_device_item(device, i, ptChip->Slots[i].Pan); + state_save_register_device_item(device, i, ptChip->Slots[i].TL); + state_save_register_device_item(device, i, ptChip->Slots[i].DstTL); + state_save_register_device_item(device, i, ptChip->Slots[i].TLStep); + state_save_register_device_item(device, i, ptChip->Slots[i].Prev); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.volume); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.state); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.step); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.AR); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.D1R); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.D2R); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.RR); + state_save_register_device_item(device, i, ptChip->Slots[i].EG.DL); + state_save_register_device_item(device, i, ptChip->Slots[i].PLFO.phase); + state_save_register_device_item(device, i, ptChip->Slots[i].PLFO.phase_step); + state_save_register_device_item(device, i, ptChip->Slots[i].ALFO.phase); + state_save_register_device_item(device, i, ptChip->Slots[i].ALFO.phase_step); + }*/ + + LFO_Init(); + + multipcm_set_bank(ptChip, 0x00, 0x00); + + return (int)(ptChip->Rate + 0.5); +} + + +void device_stop_multipcm(void *_info) +{ + MultiPCM *ptChip = (MultiPCM *)_info; + + free(ptChip->ROM); ptChip->ROM = NULL; + + free(ptChip); + + return; +} + +void device_reset_multipcm(void *_info) +{ + MultiPCM *ptChip = (MultiPCM *)_info; + int i; + + for(i=0;i<28;++i) + { + ptChip->Slots[i].Num=i; + ptChip->Slots[i].Playing=0; + } + + return; +} + + +//WRITE8_DEVICE_HANDLER( multipcm_w ) +void multipcm_w(void *_info, offs_t offset, UINT8 data) +{ + //MultiPCM *ptChip = get_safe_token(device); + MultiPCM *ptChip = (MultiPCM *)_info; + switch(offset) + { + case 0: //Data write + WriteSlot(ptChip,ptChip->Slots+ptChip->CurSlot,ptChip->Address,data); + break; + case 1: + ptChip->CurSlot=val2chan[data&0x1f]; + break; + case 2: + ptChip->Address=(data>7)?7:data; + break; + } + /*ptChip->CurSlot = val2chan[(offset >> 3) & 0x1F]; + ptChip->Address = offset & 0x07; + WriteSlot(ptChip, ptChip->Slots + ptChip->CurSlot, ptChip->Address, data);*/ +} + +/* MAME/M1 access functions */ + +//void multipcm_set_bank(running_device *device, UINT32 leftoffs, UINT32 rightoffs) +void multipcm_set_bank(void *_info, UINT32 leftoffs, UINT32 rightoffs) +{ + //MultiPCM *ptChip = get_safe_token(device); + MultiPCM *ptChip = (MultiPCM *)_info; + ptChip->BankL = leftoffs; + ptChip->BankR = rightoffs; +} + +void multipcm_bank_write(void *_info, UINT8 offset, UINT16 data) +{ + MultiPCM *ptChip = (MultiPCM *)_info; + + if (offset & 0x01) + ptChip->BankL = data << 16; + if (offset & 0x02) + ptChip->BankR = data << 16; + + return; +} + +void multipcm_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + MultiPCM *ptChip = (MultiPCM *)_info; + UINT16 CurSmpl; + struct _Sample* TempSmpl; + UINT8* ptSample; + + if (ptChip->ROMSize != ROMSize) + { + ptChip->ROM = (UINT8*)realloc(ptChip->ROM, ROMSize); + ptChip->ROMSize = ROMSize; + + for (ptChip->ROMMask = 1; ptChip->ROMMask < ROMSize; ptChip->ROMMask <<= 1) + ; + ptChip->ROMMask --; + + memset(ptChip->ROM, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(ptChip->ROM + DataStart, ROMData, DataLength); + + if (DataStart < 0x200 * 12) + { + for (CurSmpl = 0; CurSmpl < 512; CurSmpl ++) + { + TempSmpl = &ptChip->Samples[CurSmpl]; + ptSample = (UINT8*)ptChip->ROM + CurSmpl * 12; + + TempSmpl->Start = (ptSample[0]<<16)|(ptSample[1]<<8)|(ptSample[2]<<0); + TempSmpl->Loop = (ptSample[3]<<8)|(ptSample[4]<<0); + TempSmpl->End = 0xffff-((ptSample[5]<<8)|(ptSample[6]<<0)); + TempSmpl->LFOVIB = ptSample[7]; + TempSmpl->DR1 = ptSample[8]&0xf; + TempSmpl->AR = (ptSample[8]>>4)&0xf; + TempSmpl->DR2 = ptSample[9]&0xf; + TempSmpl->DL = (ptSample[9]>>4)&0xf; + TempSmpl->RR = ptSample[10]&0xf; + TempSmpl->KRS = (ptSample[10]>>4)&0xf; + TempSmpl->AM = ptSample[11]; + } + } + + return; +} + + +void multipcm_set_mute_mask(void *_info, UINT32 MuteMask) +{ + MultiPCM* ptChip = (MultiPCM *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 28; CurChn ++) + ptChip->Slots[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + +#if 0 // for debugging only +UINT8 multipcm_get_channels(UINT8 ChipID, UINT32* ChannelMask) +{ + MultiPCM* ptChip = &MultiPCMData[ChipID]; + UINT8 CurChn; + UINT8 UsedChns; + UINT32 ChnMask; + + ChnMask = 0x00000000; + UsedChns = 0x00; + for (CurChn = 0; CurChn < 28; CurChn ++) + { + if (ptChip->Slots[CurChn].Playing) + { + ChnMask |= (1 << CurChn); + UsedChns ++; + } + } + if (ChannelMask != NULL) + *ChannelMask = ChnMask; + + return UsedChns; +} +#endif + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( multipcm ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(MultiPCM); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( multipcm ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: // Nothing break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "Sega/Yamaha 315-5560"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "2.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + + +//DEFINE_LEGACY_SOUND_DEVICE(MULTIPCM, multipcm); diff --git a/Frameworks/GME/vgmplay/chips/multipcm.h b/Frameworks/GME/vgmplay/chips/multipcm.h new file mode 100644 index 000000000..1e1dedc54 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/multipcm.h @@ -0,0 +1,22 @@ +#pragma once + +//#include "devlegcy.h" + +void MultiPCM_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_multipcm(void **chip, int clock); +void device_stop_multipcm(void *chip); +void device_reset_multipcm(void *chip); + +//WRITE8_DEVICE_HANDLER( multipcm_w ); +//READ8_DEVICE_HANDLER( multipcm_r ); +void multipcm_w(void *chip, offs_t offset, UINT8 data); +UINT8 multipcm_r(void *chip, offs_t offset); + +void multipcm_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); +//void multipcm_set_bank(running_device *device, UINT32 leftoffs, UINT32 rightoffs); +void multipcm_set_bank(void *chip, UINT32 leftoffs, UINT32 rightoffs); +void multipcm_bank_write(void *chip, UINT8 offset, UINT16 data); + +void multipcm_set_mute_mask(void *chip, UINT32 MuteMask); +//DECLARE_LEGACY_SOUND_DEVICE(MULTIPCM, multipcm); diff --git a/Frameworks/GME/vgmplay/chips/nes_apu.c b/Frameworks/GME/vgmplay/chips/nes_apu.c new file mode 100644 index 000000000..70fef2cc8 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/nes_apu.c @@ -0,0 +1,941 @@ +/***************************************************************************** + + MAME/MESS NES APU CORE + + Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by + Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by + Who Wants to Know? (wwtk@mail.com) + + This core is written with the advise and consent of Matthew Conte and is + released under the GNU Public License. This core is freely avaiable for + use in any freeware project, subject to the following terms: + + Any modifications to this code must be duly noted in the source and + approved by Matthew Conte and myself prior to public submission. + + timing notes: + master = 21477270 + 2A03 clock = master/12 + sequencer = master/89490 or CPU/7457 + + ***************************************************************************** + + NES_APU.C + + Actual NES APU interface. + + LAST MODIFIED 02/29/2004 + + - Based on Matthew Conte's Nofrendo/Nosefart core and redesigned to + use MAME system calls and to enable multiple APUs. Sound at this + point should be just about 100% accurate, though I cannot tell for + certain as yet. + + A queue interface is also available for additional speed. However, + the implementation is not yet 100% (DPCM sounds are inaccurate), + so it is disabled by default. + + ***************************************************************************** + + BUGFIXES: + + - Various bugs concerning the DPCM channel fixed. (Oliver Achten) + - Fixed $4015 read behaviour. (Oliver Achten) + + *****************************************************************************/ + +#include "mamedef.h" +#include +#include +#include // for NULL +//#include "emu.h" +//#include "streams.h" +#include "nes_apu.h" +//#include "cpu/m6502/m6502.h" + +#include "nes_defs.h" + +/* GLOBAL CONSTANTS */ +#define SYNCS_MAX1 0x20 +#define SYNCS_MAX2 0x80 + +/* GLOBAL VARIABLES */ +typedef struct _nesapu_state nesapu_state; +struct _nesapu_state +{ + apu_t APU; /* Actual APUs */ + float apu_incsize; /* Adjustment increment */ + uint32 samps_per_sync; /* Number of samples per vsync */ + uint32 buffer_size; /* Actual buffer size in bytes */ + uint32 real_rate; /* Actual playback rate */ + uint8 noise_lut[NOISE_LONG]; /* Noise sample lookup table */ + uint32 vbl_times[0x20]; /* VBL durations in samples */ + uint32 sync_times1[SYNCS_MAX1]; /* Samples per sync table */ + uint32 sync_times2[SYNCS_MAX2]; /* Samples per sync table */ + //sound_stream *stream; +}; + +static UINT8 DPCMBase0 = 0x01; + +//#define MAX_CHIPS 0x02 +//static nesapu_state NESAPUData[MAX_CHIPS]; + +/*INLINE nesapu_state *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == NES); + return (nesapu_state *)downcast(device)->token(); +}*/ + +/* INTERNAL FUNCTIONS */ + +/* INITIALIZE WAVE TIMES RELATIVE TO SAMPLE RATE */ +static void create_vbltimes(uint32 * table,const uint8 *vbl,unsigned int rate) +{ + int i; + + for (i = 0; i < 0x20; i++) + table[i] = vbl[i] * rate; +} + +/* INITIALIZE SAMPLE TIMES IN TERMS OF VSYNCS */ +static void create_syncs(nesapu_state *info, unsigned long sps) +{ + int i; + unsigned long val = sps; + + for (i = 0; i < SYNCS_MAX1; i++) + { + info->sync_times1[i] = val; + val += sps; + } + + val = 0; + for (i = 0; i < SYNCS_MAX2; i++) + { + info->sync_times2[i] = val; + info->sync_times2[i] >>= 2; + val += sps; + } +} + +/* INITIALIZE NOISE LOOKUP TABLE */ +static void create_noise(uint8 *buf, const int bits, int size) +{ + int m = 0x0011; + int xor_val, i; + + for (i = 0; i < size; i++) + { + xor_val = m & 1; + m >>= 1; + xor_val ^= (m & 1); + m |= xor_val << (bits - 1); + + buf[i] = m; + } +} + +/* TODO: sound channels should *ALL* have DC volume decay */ + +/* OUTPUT SQUARE WAVE SAMPLE (VALUES FROM -16 to +15) */ +static int8 apu_square(nesapu_state *info, square_t *chan) +{ + int env_delay; + int sweep_delay; + int8 output; + uint8 freq_index; + + /* reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle + ** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on + ** reg2: 8 bits of freq + ** reg3: 0-2=high freq, 7-4=vbl length counter + */ + + if (FALSE == chan->enabled || chan->Muted) + return 0; + + /* enveloping */ + env_delay = info->sync_times1[chan->regs[0] & 0x0F]; + + /* decay is at a rate of (env_regs + 1) / 240 secs */ + chan->env_phase -= 4; + while (chan->env_phase < 0) + { + chan->env_phase += env_delay; + if (chan->regs[0] & 0x20) + chan->env_vol = (chan->env_vol + 1) & 15; + else if (chan->env_vol < 15) + chan->env_vol++; + } + + /* vbl length counter */ + if (chan->vbl_length > 0 && 0 == (chan->regs [0] & 0x20)) + chan->vbl_length--; + + if (0 == chan->vbl_length) + return 0; + + /* freqsweeps */ + if ((chan->regs[1] & 0x80) && (chan->regs[1] & 7)) + { + sweep_delay = info->sync_times1[(chan->regs[1] >> 4) & 7]; + chan->sweep_phase -= 2; + while (chan->sweep_phase < 0) + { + chan->sweep_phase += sweep_delay; + if (chan->regs[1] & 8) + chan->freq -= chan->freq >> (chan->regs[1] & 7); + else + chan->freq += chan->freq >> (chan->regs[1] & 7); + } + } + +// if ((0 == (chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[chan->regs[1] & 7]) +// || (chan->freq >> 16) < 4) +// return 0; + + // Thanks to Delek for the fix + if (chan->regs[1] & 0x80) + freq_index = chan->regs[1] & 7; //If sweeping is enabled, I choose it as normal. + else + freq_index = 7; //If sweeping is disabled, I choose the lower limit. + + if ((0 == (chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[freq_index]) + || (chan->freq >> 16) < 4) + return 0; + + chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ + + while (chan->phaseacc < 0) + { + chan->phaseacc += (chan->freq >> 16); + chan->adder = (chan->adder + 1) & 0x0F; + } + + if (chan->regs[0] & 0x10) /* fixed volume */ + output = chan->regs[0] & 0x0F; + else + output = 0x0F - chan->env_vol; + + if (chan->adder < (duty_lut[chan->regs[0] >> 6])) + output = -output; + + return (int8) output; +} + +/* OUTPUT TRIANGLE WAVE SAMPLE (VALUES FROM -16 to +15) */ +static int8 apu_triangle(nesapu_state *info, triangle_t *chan) +{ + int freq; + int8 output; + /* reg0: 7=holdnote, 6-0=linear length counter + ** reg2: low 8 bits of frequency + ** reg3: 7-3=length counter, 2-0=high 3 bits of frequency + */ + + if (FALSE == chan->enabled || chan->Muted) + return 0; + + if (FALSE == chan->counter_started && 0 == (chan->regs[0] & 0x80)) + { + if (chan->write_latency) + chan->write_latency--; + if (0 == chan->write_latency) + chan->counter_started = TRUE; + } + + if (chan->counter_started) + { + if (chan->linear_length > 0) + chan->linear_length--; + if (chan->vbl_length && 0 == (chan->regs[0] & 0x80)) + chan->vbl_length--; + + if (0 == chan->vbl_length) + return 0; + } + + if (0 == chan->linear_length) + return 0; + + freq = (((chan->regs[3] & 7) << 8) + chan->regs[2]) + 1; + + if (freq < 4) /* inaudible */ + return 0; + + chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ + while (chan->phaseacc < 0) + { + chan->phaseacc += freq; + chan->adder = (chan->adder + 1) & 0x1F; + + output = (chan->adder & 7) << 1; + if (chan->adder & 8) + output = 0x10 - output; + if (chan->adder & 0x10) + output = -output; + + chan->output_vol = output; + } + + return (int8) chan->output_vol; +} + +/* OUTPUT NOISE WAVE SAMPLE (VALUES FROM -16 to +15) */ +static int8 apu_noise(nesapu_state *info, noise_t *chan) +{ + int freq, env_delay; + uint8 outvol; + uint8 output; + + /* reg0: 0-3=volume, 4=envelope, 5=hold + ** reg2: 7=small(93 byte) sample,3-0=freq lookup + ** reg3: 7-4=vbl length counter + */ + + if (FALSE == chan->enabled || chan->Muted) + return 0; + + /* enveloping */ + env_delay = info->sync_times1[chan->regs[0] & 0x0F]; + + /* decay is at a rate of (env_regs + 1) / 240 secs */ + chan->env_phase -= 4; + while (chan->env_phase < 0) + { + chan->env_phase += env_delay; + if (chan->regs[0] & 0x20) + chan->env_vol = (chan->env_vol + 1) & 15; + else if (chan->env_vol < 15) + chan->env_vol++; + } + + /* length counter */ + if (0 == (chan->regs[0] & 0x20)) + { + if (chan->vbl_length > 0) + chan->vbl_length--; + } + + if (0 == chan->vbl_length) + return 0; + + freq = noise_freq[chan->regs[2] & 0x0F]; + chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ + while (chan->phaseacc < 0) + { + chan->phaseacc += freq; + + chan->cur_pos++; + if (NOISE_SHORT == chan->cur_pos && (chan->regs[2] & 0x80)) + chan->cur_pos = 0; + else if (NOISE_LONG == chan->cur_pos) + chan->cur_pos = 0; + } + + if (chan->regs[0] & 0x10) /* fixed volume */ + outvol = chan->regs[0] & 0x0F; + else + outvol = 0x0F - chan->env_vol; + + output = info->noise_lut[chan->cur_pos]; + if (output > outvol) + output = outvol; + + if (info->noise_lut[chan->cur_pos] & 0x80) /* make it negative */ + output = -output; + + return (int8) output; +} + +/* RESET DPCM PARAMETERS */ +INLINE void apu_dpcmreset(dpcm_t *chan) +{ + chan->address = 0xC000 + (uint16) (chan->regs[2] << 6); + chan->length = (uint16) (chan->regs[3] << 4) + 1; + chan->bits_left = chan->length << 3; + chan->irq_occurred = FALSE; + chan->enabled = TRUE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ + // Note: according to NSFPlay, it does NOT do that + chan->vol = 0; /* Fixed * DPCM DAC resets itself when restarted */ +} + +/* OUTPUT DPCM WAVE SAMPLE (VALUES FROM -64 to +63) */ +/* TODO: centerline naughtiness */ +static int8 apu_dpcm(nesapu_state *info, dpcm_t *chan) +{ + int freq, bit_pos; + + /* reg0: 7=irq gen, 6=looping, 3-0=pointer to clock table + ** reg1: output dc level, 7 bits unsigned + ** reg2: 8 bits of 64-byte aligned address offset : $C000 + (value * 64) + ** reg3: length, (value * 16) + 1 + */ + if (chan->Muted) + return 0; + + if (chan->enabled) + { + freq = dpcm_clocks[chan->regs[0] & 0x0F]; + chan->phaseacc -= (float) info->apu_incsize; /* # of cycles per sample */ + + while (chan->phaseacc < 0) + { + chan->phaseacc += freq; + + if (0 == chan->length) + { + chan->enabled = FALSE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ + chan->vol=0; /* Fixed * DPCM DAC resets itself when restarted */ + if (chan->regs[0] & 0x40) + apu_dpcmreset(chan); + else + { + if (chan->regs[0] & 0x80) /* IRQ Generator */ + { + chan->irq_occurred = TRUE; + //n2a03_irq(info->APU.dpcm.memory->cpu); + } + break; + } + } + + + chan->bits_left--; + bit_pos = 7 - (chan->bits_left & 7); + if (7 == bit_pos) + { + //chan->cur_byte = info->APU.dpcm.memory->read_byte(chan->address); + chan->cur_byte = info->APU.dpcm.memory[chan->address]; + chan->address++; + // On overflow, the address is set to 8000 + if (chan->address >= 0x10000) + chan->address -= 0x8000; + chan->length--; + } + + if (chan->cur_byte & (1 << bit_pos)) +// chan->regs[1]++; + chan->vol+=2; /* FIXED * DPCM channel only uses the upper 6 bits of the DAC */ + else +// chan->regs[1]--; + chan->vol-=2; + } + } + + if (! DPCMBase0) + { + if (chan->vol > 63) + chan->vol = 63; + else if (chan->vol < -64) + chan->vol = -64; + } + else + { + if (chan->vol > 127) + chan->vol = 127; + else if (chan->vol < 0) + chan->vol = 0; + } + + return (int8) (chan->vol); + //return (int8) 0; +} + +/* WRITE REGISTER VALUE */ +INLINE void apu_regwrite(nesapu_state *info,int address, uint8 value) +{ + int chan = (address & 4) ? 1 : 0; + + switch (address) + { + /* squares */ + case APU_WRA0: + case APU_WRB0: + info->APU.squ[chan].regs[0] = value; + break; + + case APU_WRA1: + case APU_WRB1: + info->APU.squ[chan].regs[1] = value; + break; + + case APU_WRA2: + case APU_WRB2: + info->APU.squ[chan].regs[2] = value; + if (info->APU.squ[chan].enabled) + info->APU.squ[chan].freq = ((((info->APU.squ[chan].regs[3] & 7) << 8) + value) + 1) << 16; + break; + + case APU_WRA3: + case APU_WRB3: + info->APU.squ[chan].regs[3] = value; + + if (info->APU.squ[chan].enabled) + { + // TODO: Test, if it sounds better with or without it. + // info->APU.squ[chan].adder = 0; // Thanks to Delek + info->APU.squ[chan].vbl_length = info->vbl_times[value >> 3]; + info->APU.squ[chan].env_vol = 0; + info->APU.squ[chan].freq = ((((value & 7) << 8) + info->APU.squ[chan].regs[2]) + 1) << 16; + } + + break; + + /* triangle */ + case APU_WRC0: + info->APU.tri.regs[0] = value; + + if (info->APU.tri.enabled) + { /* ??? */ + if (FALSE == info->APU.tri.counter_started) + info->APU.tri.linear_length = info->sync_times2[value & 0x7F]; + } + + break; + + //case 0x4009: + case APU_WRC1: + /* unused */ + info->APU.tri.regs[1] = value; + break; + + case APU_WRC2: + info->APU.tri.regs[2] = value; + break; + + case APU_WRC3: + info->APU.tri.regs[3] = value; + + /* this is somewhat of a hack. there is some latency on the Real + ** Thing between when trireg0 is written to and when the linear + ** length counter actually begins its countdown. we want to prevent + ** the case where the program writes to the freq regs first, then + ** to reg 0, and the counter accidentally starts running because of + ** the sound queue's timestamp processing. + ** + ** set to a few NES sample -- should be sufficient + ** + ** 3 * (1789772.727 / 44100) = ~122 cycles, just around one scanline + ** + ** should be plenty of time for the 6502 code to do a couple of table + ** dereferences and load up the other triregs + */ + + /* used to be 3, but now we run the clock faster, so base it on samples/sync */ + info->APU.tri.write_latency = (info->samps_per_sync + 239) / 240; + + if (info->APU.tri.enabled) + { + info->APU.tri.counter_started = FALSE; + info->APU.tri.vbl_length = info->vbl_times[value >> 3]; + info->APU.tri.linear_length = info->sync_times2[info->APU.tri.regs[0] & 0x7F]; + } + + break; + + /* noise */ + case APU_WRD0: + info->APU.noi.regs[0] = value; + break; + + case 0x400D: + /* unused */ + info->APU.noi.regs[1] = value; + break; + + case APU_WRD2: + info->APU.noi.regs[2] = value; + info->APU.noi.cur_pos = 0; // Thanks to Delek for this fix. + break; + + case APU_WRD3: + info->APU.noi.regs[3] = value; + + if (info->APU.noi.enabled) + { + info->APU.noi.vbl_length = info->vbl_times[value >> 3]; + info->APU.noi.env_vol = 0; /* reset envelope */ + } + break; + + /* DMC */ + case APU_WRE0: + info->APU.dpcm.regs[0] = value; + if (0 == (value & 0x80)) + info->APU.dpcm.irq_occurred = FALSE; + break; + + case APU_WRE1: /* 7-bit DAC */ + //info->APU.dpcm.regs[1] = value - 0x40; + info->APU.dpcm.regs[1] = value & 0x7F; + if (! DPCMBase0) + info->APU.dpcm.vol = (info->APU.dpcm.regs[1]-64); + else + info->APU.dpcm.vol = info->APU.dpcm.regs[1]; + break; + + case APU_WRE2: + info->APU.dpcm.regs[2] = value; + //apu_dpcmreset(info->APU.dpcm); + break; + + case APU_WRE3: + info->APU.dpcm.regs[3] = value; + break; + + case APU_IRQCTRL: + if(value & 0x80) + info->APU.step_mode = 5; + else + info->APU.step_mode = 4; + break; + + case APU_SMASK: + if (value & 0x01) + info->APU.squ[0].enabled = TRUE; + else + { + info->APU.squ[0].enabled = FALSE; + info->APU.squ[0].vbl_length = 0; + } + + if (value & 0x02) + info->APU.squ[1].enabled = TRUE; + else + { + info->APU.squ[1].enabled = FALSE; + info->APU.squ[1].vbl_length = 0; + } + + if (value & 0x04) + info->APU.tri.enabled = TRUE; + else + { + info->APU.tri.enabled = FALSE; + info->APU.tri.vbl_length = 0; + info->APU.tri.linear_length = 0; + info->APU.tri.counter_started = FALSE; + info->APU.tri.write_latency = 0; + } + + if (value & 0x08) + info->APU.noi.enabled = TRUE; + else + { + info->APU.noi.enabled = FALSE; + info->APU.noi.vbl_length = 0; + } + + if (value & 0x10) + { + /* only reset dpcm values if DMA is finished */ + if (FALSE == info->APU.dpcm.enabled) + { + info->APU.dpcm.enabled = TRUE; + apu_dpcmreset(&info->APU.dpcm); + } + } + else + info->APU.dpcm.enabled = FALSE; + + info->APU.dpcm.irq_occurred = FALSE; + + break; + default: +#ifdef MAME_DEBUG +logerror("invalid apu write: $%02X at $%04X\n", value, address); +#endif + break; + } +} + +/* UPDATE SOUND BUFFER USING CURRENT DATA */ +//INLINE void apu_update(nesapu_state *info, stream_sample_t *buffer16, int samples) +INLINE void apu_update(nesapu_state *info, stream_sample_t **buffer16, int samples) +{ + int accum; + stream_sample_t* bufL = buffer16[0]; + stream_sample_t* bufR = buffer16[1]; + + while (samples--) + { + /*accum = apu_square(info, &info->APU.squ[0]); + accum += apu_square(info, &info->APU.squ[1]); + accum += apu_triangle(info, &info->APU.tri); + accum += apu_noise(info, &info->APU.noi); + accum += apu_dpcm(info, &info->APU.dpcm); + + // 8-bit clamps + if (accum > 127) + accum = 127; + else if (accum < -128) + accum = -128; + + *(bufL++)=accum<<8; + *(bufR++)=accum<<8;*/ + + // These volumes should match NSFPlay's NES core better + accum = apu_square(info, &info->APU.squ[0]) << 8; // << 8 * 1.0 + accum += apu_square(info, &info->APU.squ[1]) << 8; // << 8 * 1.0 + accum += apu_triangle(info, &info->APU.tri) * 0xC0; // << 8 * 0.75 + accum += apu_noise(info, &info->APU.noi) * 0xC0; // << 8 * 0.75 + accum += apu_dpcm(info, &info->APU.dpcm) * 0xC0; // << 8 * 0.75 + + *(bufL++)=accum; + *(bufR++)=accum; + } +} + +/* READ VALUES FROM REGISTERS */ +INLINE uint8 apu_read(nesapu_state *info,int address) +{ + if (address == 0x15) /*FIXED* Address $4015 has different behaviour*/ + { + int readval = 0; + if (info->APU.squ[0].vbl_length > 0) + readval |= 0x01; + + if (info->APU.squ[1].vbl_length > 0) + readval |= 0x02; + + if (info->APU.tri.vbl_length > 0) + readval |= 0x04; + + if (info->APU.noi.vbl_length > 0) + readval |= 0x08; + + if (info->APU.dpcm.enabled == TRUE) + readval |= 0x10; + + if (info->APU.dpcm.irq_occurred == TRUE) + readval |= 0x80; + + return readval; + } + else + return info->APU.regs[address]; +} + +/* WRITE VALUE TO TEMP REGISTRY AND QUEUE EVENT */ +INLINE void apu_write(nesapu_state *info,int address, uint8 value) +{ + info->APU.regs[address]=value; + //stream_update(info->stream); + apu_regwrite(info,address,value); +} + +/* EXTERNAL INTERFACE FUNCTIONS */ + +/* REGISTER READ/WRITE FUNCTIONS */ +//READ8_DEVICE_HANDLER( nes_psg_r ) +UINT8 nes_psg_r(void* chip, offs_t offset) +{ + //return apu_read(get_safe_token(device),offset); + return apu_read((nesapu_state*)chip, offset); +} +//WRITE8_DEVICE_HANDLER( nes_psg_w ) +void nes_psg_w(void* chip, offs_t offset, UINT8 data) +{ + //apu_write(get_safe_token(device),offset,data); + apu_write((nesapu_state*)chip, offset, data); +} + +/* UPDATE APU SYSTEM */ +//static STREAM_UPDATE( nes_psg_update_sound ) +void nes_psg_update_sound(void* chip, stream_sample_t **outputs, int samples) +{ + //nesapu_state *info = (nesapu_state *)param; + nesapu_state *info = (nesapu_state*)chip; + //apu_update(info, outputs[0], samples); + apu_update(info, outputs, samples); +} + + +/* INITIALIZE APU SYSTEM */ +#define SCREEN_HZ 60 +//static DEVICE_START( nesapu ) +void* device_start_nesapu(int clock, int rate) +{ + //const nes_interface *intf = (const nes_interface *)device->baseconfig().static_config(); + //nesapu_state *info = get_safe_token(device); + nesapu_state *info; + //int rate = clock / 4; + //int i; + +// if (ChipID >= MAX_CHIPS) +// return 0; + + info = (nesapu_state*)malloc(sizeof(nesapu_state)); + if (info == NULL) + return NULL; + + /* Initialize global variables */ + //info->samps_per_sync = rate / ATTOSECONDS_TO_HZ(device->machine->primary_screen->frame_period().attoseconds); + info->samps_per_sync = rate / SCREEN_HZ; + info->buffer_size = info->samps_per_sync; + //info->real_rate = info->samps_per_sync * ATTOSECONDS_TO_HZ(device->machine->primary_screen->frame_period().attoseconds); + info->real_rate = info->samps_per_sync * SCREEN_HZ; + //info->apu_incsize = (float) (device->clock() / (float) info->real_rate); + info->apu_incsize = (float) (clock / (float) info->real_rate); + + /* Use initializer calls */ + create_noise(info->noise_lut, 13, NOISE_LONG); + create_vbltimes(info->vbl_times,vbl_length,info->samps_per_sync); + create_syncs(info, info->samps_per_sync); + + /* Adjust buffer size if 16 bits */ + info->buffer_size+=info->samps_per_sync; + + /* Initialize individual chips */ + //(info->APU.dpcm).memory = cputag_get_address_space(device->machine, intf->cpu_tag, ADDRESS_SPACE_PROGRAM); + // no idea how to obtain this + info->APU.dpcm.memory = NULL; + + //info->stream = stream_create(device, 0, 1, rate, info, nes_psg_update_sound); + + /* register for save */ + /*for (i = 0; i < 2; i++) + { + state_save_register_device_item_array(device, i, info->APU.squ[i].regs); + state_save_register_device_item(device, i, info->APU.squ[i].vbl_length); + state_save_register_device_item(device, i, info->APU.squ[i].freq); + state_save_register_device_item(device, i, info->APU.squ[i].phaseacc); + state_save_register_device_item(device, i, info->APU.squ[i].output_vol); + state_save_register_device_item(device, i, info->APU.squ[i].env_phase); + state_save_register_device_item(device, i, info->APU.squ[i].sweep_phase); + state_save_register_device_item(device, i, info->APU.squ[i].adder); + state_save_register_device_item(device, i, info->APU.squ[i].env_vol); + state_save_register_device_item(device, i, info->APU.squ[i].enabled); + } + + state_save_register_device_item_array(device, 0, info->APU.tri.regs); + state_save_register_device_item(device, 0, info->APU.tri.linear_length); + state_save_register_device_item(device, 0, info->APU.tri.vbl_length); + state_save_register_device_item(device, 0, info->APU.tri.write_latency); + state_save_register_device_item(device, 0, info->APU.tri.phaseacc); + state_save_register_device_item(device, 0, info->APU.tri.output_vol); + state_save_register_device_item(device, 0, info->APU.tri.adder); + state_save_register_device_item(device, 0, info->APU.tri.counter_started); + state_save_register_device_item(device, 0, info->APU.tri.enabled); + + state_save_register_device_item_array(device, 0, info->APU.noi.regs); + state_save_register_device_item(device, 0, info->APU.noi.cur_pos); + state_save_register_device_item(device, 0, info->APU.noi.vbl_length); + state_save_register_device_item(device, 0, info->APU.noi.phaseacc); + state_save_register_device_item(device, 0, info->APU.noi.output_vol); + state_save_register_device_item(device, 0, info->APU.noi.env_phase); + state_save_register_device_item(device, 0, info->APU.noi.env_vol); + state_save_register_device_item(device, 0, info->APU.noi.enabled); + + state_save_register_device_item_array(device, 0, info->APU.dpcm.regs); + state_save_register_device_item(device, 0, info->APU.dpcm.address); + state_save_register_device_item(device, 0, info->APU.dpcm.length); + state_save_register_device_item(device, 0, info->APU.dpcm.bits_left); + state_save_register_device_item(device, 0, info->APU.dpcm.phaseacc); + state_save_register_device_item(device, 0, info->APU.dpcm.output_vol); + state_save_register_device_item(device, 0, info->APU.dpcm.cur_byte); + state_save_register_device_item(device, 0, info->APU.dpcm.enabled); + state_save_register_device_item(device, 0, info->APU.dpcm.irq_occurred); + state_save_register_device_item(device, 0, info->APU.dpcm.vol); + + state_save_register_device_item_array(device, 0, info->APU.regs); + +#ifdef USE_QUEUE + state_save_register_device_item_array(device, 0, info->APU.queue); + state_save_register_device_item(device, 0, info->APU.head); + state_save_register_device_item(device, 0, info->APU.tail); +#else + state_save_register_device_item(device, 0, info->APU.buf_pos); + state_save_register_device_item(device, 0, info->APU.step_mode); +#endif + */ + + info->APU.squ[0].Muted = 0x00; + info->APU.squ[1].Muted = 0x00; + info->APU.tri.Muted = 0x00; + info->APU.noi.Muted = 0x00; + info->APU.dpcm.Muted = 0x00; + + return info; +} + +void device_stop_nesapu(void* chip) +{ + nesapu_state *info = (nesapu_state*)chip; + + info->APU.dpcm.memory = NULL; + + return; +} + +void device_reset_nesapu(void* chip) +{ + nesapu_state *info = (nesapu_state*)chip; + const UINT8* MemPtr; + UINT8 CurReg; + + MemPtr = info->APU.dpcm.memory; + memset(&info->APU, 0x00, sizeof(apu_t)); + info->APU.dpcm.memory = MemPtr; + apu_dpcmreset(&info->APU.dpcm); + + for (CurReg = 0x00; CurReg < 0x18; CurReg ++) + apu_write(info, CurReg, 0x00); + + apu_write(info, 0x15, 0x00); + apu_write(info, 0x15, 0x0F); + + return; +} + +void nesapu_set_rom(void* chip, const UINT8* ROMData) +{ + nesapu_state *info = (nesapu_state*)chip; + info->APU.dpcm.memory = ROMData; + + return; +} + +void nesapu_set_mute_mask(void* chip, UINT32 MuteMask) +{ + nesapu_state *info = (nesapu_state*)chip; + + info->APU.squ[0].Muted = (MuteMask >> 0) & 0x01; + info->APU.squ[1].Muted = (MuteMask >> 1) & 0x01; + info->APU.tri.Muted = (MuteMask >> 2) & 0x01; + info->APU.noi.Muted = (MuteMask >> 3) & 0x01; + info->APU.dpcm.Muted = (MuteMask >> 4) & 0x01; + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( nesapu ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(nesapu_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( nesapu ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: // Nothing break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "N2A03"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Nintendo custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(NES, nesapu);*/ diff --git a/Frameworks/GME/vgmplay/chips/nes_apu.h b/Frameworks/GME/vgmplay/chips/nes_apu.h new file mode 100644 index 000000000..33a263a06 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/nes_apu.h @@ -0,0 +1,63 @@ +/***************************************************************************** + + MAME/MESS NES APU CORE + + Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by + Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by + Who Wants to Know? (wwtk@mail.com) + + This core is written with the advise and consent of Matthew Conte and is + released under the GNU Public License. This core is freely avaiable for + use in any freeware project, subject to the following terms: + + Any modifications to this code must be duly noted in the source and + approved by Matthew Conte and myself prior to public submission. + + ***************************************************************************** + + NES_APU.H + + NES APU external interface. + + *****************************************************************************/ + +#pragma once + +#ifndef __NES_APU_H__ +#define __NES_APU_H__ + +//#include "devlegcy.h" + + +/* AN EXPLANATION + * + * The NES APU is actually integrated into the Nintendo processor. + * You must supply the same number of APUs as you do processors. + * Also make sure to correspond the memory regions to those used in the + * processor, as each is shared. + */ + +/*typedef struct _nes_interface nes_interface; +struct _nes_interface +{ + const char *cpu_tag; // CPU tag +}; + +READ8_DEVICE_HANDLER( nes_psg_r ); +WRITE8_DEVICE_HANDLER( nes_psg_w ); + +DECLARE_LEGACY_SOUND_DEVICE(NES, nesapu);*/ + +UINT8 nes_psg_r(void* chip, offs_t offset); +void nes_psg_w(void* chip, offs_t offset, UINT8 data); + +void nes_psg_update_sound(void* chip, stream_sample_t **outputs, int samples); +void* device_start_nesapu(int clock, int rate); +void device_stop_nesapu(void* chip); +void device_reset_nesapu(void* chip); + +void nesapu_set_rom(void* chip, const UINT8* ROMData); + +void nesapu_set_mute_mask(void* chip, UINT32 MuteMask); + +#endif /* __NES_APU_H__ */ diff --git a/Frameworks/GME/vgmplay/chips/nes_defs.h b/Frameworks/GME/vgmplay/chips/nes_defs.h new file mode 100644 index 000000000..6513ece0f --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/nes_defs.h @@ -0,0 +1,219 @@ +/***************************************************************************** + + MAME/MESS NES APU CORE + + Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by + Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by + Who Wants to Know? (wwtk@mail.com) + + This core is written with the advise and consent of Matthew Conte and is + released under the GNU Public License. This core is freely avaiable for + use in any freeware project, subject to the following terms: + + Any modifications to this code must be duly noted in the source and + approved by Matthew Conte and myself prior to public submission. + + ***************************************************************************** + + NES_DEFS.H + + NES APU internal type definitions and constants. + + *****************************************************************************/ + +#pragma once + +#ifndef __NES_DEFS_H__ +#define __NES_DEFS_H__ + +/* BOOLEAN CONSTANTS */ +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* REGULAR TYPE DEFINITIONS */ +typedef INT8 int8; +typedef INT16 int16; +typedef INT32 int32; +typedef UINT8 uint8; +typedef UINT16 uint16; +typedef UINT32 uint32; +typedef UINT8 boolean; + + +/* QUEUE TYPES */ +#ifdef USE_QUEUE + +#define QUEUE_SIZE 0x2000 +#define QUEUE_MAX (QUEUE_SIZE-1) + +typedef struct queue_s +{ + int pos; + unsigned char reg,val; +} queue_t; + +#endif + +/* REGISTER DEFINITIONS */ +#define APU_WRA0 0x00 +#define APU_WRA1 0x01 +#define APU_WRA2 0x02 +#define APU_WRA3 0x03 +#define APU_WRB0 0x04 +#define APU_WRB1 0x05 +#define APU_WRB2 0x06 +#define APU_WRB3 0x07 +#define APU_WRC0 0x08 +#define APU_WRC1 0x09 +#define APU_WRC2 0x0A +#define APU_WRC3 0x0B +#define APU_WRD0 0x0C +#define APU_WRD2 0x0E +#define APU_WRD3 0x0F +#define APU_WRE0 0x10 +#define APU_WRE1 0x11 +#define APU_WRE2 0x12 +#define APU_WRE3 0x13 +#define APU_SMASK 0x15 +#define APU_IRQCTRL 0x17 + +#define NOISE_LONG 0x4000 +#define NOISE_SHORT 93 + +/* CHANNEL TYPE DEFINITIONS */ + +/* Square Wave */ +typedef struct square_s +{ + uint8 regs[4]; + int vbl_length; + int freq; + float phaseacc; + float output_vol; + float env_phase; + float sweep_phase; + uint8 adder; + uint8 env_vol; + boolean enabled; + boolean Muted; +} square_t; + +/* Triangle Wave */ +typedef struct triangle_s +{ + uint8 regs[4]; /* regs[1] unused */ + int linear_length; + int vbl_length; + int write_latency; + float phaseacc; + float output_vol; + uint8 adder; + boolean counter_started; + boolean enabled; + boolean Muted; +} triangle_t; + +/* Noise Wave */ +typedef struct noise_s +{ + uint8 regs[4]; /* regs[1] unused */ + int cur_pos; + int vbl_length; + float phaseacc; + float output_vol; + float env_phase; + uint8 env_vol; + boolean enabled; + boolean Muted; +} noise_t; + +/* DPCM Wave */ +typedef struct dpcm_s +{ + uint8 regs[4]; + uint32 address; + uint32 length; + int bits_left; + float phaseacc; + float output_vol; + uint8 cur_byte; + boolean enabled; + boolean irq_occurred; + //address_space *memory; + const uint8 *memory; + //signed char vol; + signed short vol; + boolean Muted; +} dpcm_t; + +/* APU type */ +typedef struct apu +{ + /* Sound channels */ + square_t squ[2]; + triangle_t tri; + noise_t noi; + dpcm_t dpcm; + + /* APU registers */ + unsigned char regs[0x17]; + + /* Sound pointers */ + void *buffer; + +#ifdef USE_QUEUE + + /* Event queue */ + queue_t queue[QUEUE_SIZE]; + int head, tail; + +#else + + int buf_pos; + +#endif + + int step_mode; +} apu_t; + +/* CONSTANTS */ + +/* vblank length table used for squares, triangle, noise */ +static const uint8 vbl_length[32] = +{ + 5, 127, 10, 1, 19, 2, 40, 3, 80, 4, 30, 5, 7, 6, 13, 7, + 6, 8, 12, 9, 24, 10, 48, 11, 96, 12, 36, 13, 8, 14, 16, 15 +}; + +/* frequency limit of square channels */ +static const int freq_limit[8] = +{ + //0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0, + // Fixed, thanks to Delek + 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F2, +}; + +/* table of noise frequencies */ +static const int noise_freq[16] = +{ + //4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 2046 + // Fixed, thanks to Delek + 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 +}; + +/* dpcm transfer freqs */ +static const int dpcm_clocks[16] = +{ + 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 85, 72, 54 +}; + +/* ratios of pos/neg pulse for square waves */ +/* 2/16 = 12.5%, 4/16 = 25%, 8/16 = 50%, 12/16 = 75% */ +static const int duty_lut[4] = +{ + 2, 4, 8, 12 +}; + +#endif /* __NES_DEFS_H__ */ diff --git a/Frameworks/GME/vgmplay/chips/nes_intf.c b/Frameworks/GME/vgmplay/chips/nes_intf.c new file mode 100644 index 000000000..66d726da8 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/nes_intf.c @@ -0,0 +1,339 @@ +/**************************************************************** + + MAME / MESS functions + +****************************************************************/ + +#include "mamedef.h" +#include // for memset +#include // for free +#include // for NULL +#include "../stdbool.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "nes_apu.h" +#include "np_nes_apu.h" +#include "np_nes_dmc.h" +#include "np_nes_fds.h" +#include "nes_intf.h" + + +#ifdef ENABLE_ALL_CORES +#define EC_MAME 0x01 // NES core from MAME +#endif +#define EC_NSFPLAY 0x00 // NES core from NSFPlay +// Note: FDS core from NSFPlay is always used + +/* for stream system */ +typedef struct _nes_state nes_state; +struct _nes_state +{ + void* chip_apu; + void* chip_dmc; + void* chip_fds; + UINT8* Memory; + int EMU_CORE; +}; + +void nes_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + nes_state *info = (nes_state *)_info; + int CurSmpl; + INT32 Buffer[4]; + + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + nes_psg_update_sound(info->chip_apu, outputs, samples); + break; +#endif + case EC_NSFPLAY: + for (CurSmpl = 0x00; CurSmpl < samples; CurSmpl ++) + { + NES_APU_np_Render(info->chip_apu, &Buffer[0]); + NES_DMC_np_Render(info->chip_dmc, &Buffer[2]); + outputs[0][CurSmpl] = Buffer[0] + Buffer[2]; + outputs[1][CurSmpl] = Buffer[1] + Buffer[3]; + } + break; + } + + if (info->chip_fds != NULL) + { + for (CurSmpl = 0x00; CurSmpl < samples; CurSmpl ++) + { + NES_FDS_Render(info->chip_fds, &Buffer[0]); + outputs[0][CurSmpl] += Buffer[0]; + outputs[1][CurSmpl] += Buffer[1]; + } + } + + return; +} + +static void nes_set_chip_option(nes_state *info, int NesOptions); +int device_start_nes(void **_info, int EMU_CORE, int clock, int Options, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + nes_state *info; + int rate; + bool EnableFDS; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_NSFPLAY; +#else + EMU_CORE = EC_NSFPLAY; +#endif + + EnableFDS = (clock >> 31) & 0x01; + clock &= 0x7FFFFFFF; + + info = (nes_state *) calloc(1, sizeof(nes_state)); + *_info = (void *) info; + + info->EMU_CORE = EMU_CORE; + rate = clock / 4; + if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + + switch(EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + info->chip_apu = device_start_nesapu(clock, rate); + if (info->chip_apu == NULL) + return 0; + + info->chip_dmc = NULL; + info->chip_fds = NULL; + + info->Memory = (UINT8*)malloc(0x8000); + memset(info->Memory, 0x00, 0x8000); + nesapu_set_rom(info->chip_apu, info->Memory - 0x8000); + break; +#endif + case EC_NSFPLAY: + info->chip_apu = NES_APU_np_Create(clock, rate); + if (info->chip_apu == NULL) + return 0; + + info->chip_dmc = NES_DMC_np_Create(clock, rate); + if (info->chip_dmc == NULL) + { + NES_APU_np_Destroy(info->chip_apu); + info->chip_apu = NULL; + return 0; + } + + NES_DMC_np_SetAPU(info->chip_dmc, info->chip_apu); + + info->Memory = (UINT8*)malloc(0x8000); + memset(info->Memory, 0x00, 0x8000); + NES_DMC_np_SetMemory(info->chip_dmc, info->Memory - 0x8000); + break; + } + + if (EnableFDS) + { + info->chip_fds = NES_FDS_Create(clock, rate); + // If it returns NULL, that's okay. + } + else + { + info->chip_fds = NULL; + } + nes_set_chip_option(info, Options); + + return rate; +} + +void device_stop_nes(void *_info) +{ + nes_state *info = (nes_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + device_stop_nesapu(info->chip_apu); + break; +#endif + case EC_NSFPLAY: + NES_APU_np_Destroy(info->chip_apu); + NES_DMC_np_Destroy(info->chip_dmc); + break; + } + if (info->chip_fds != NULL) + NES_FDS_Destroy(info->chip_fds); + + if (info->Memory != NULL) + { + free(info->Memory); + info->Memory = NULL; + } + info->chip_apu = NULL; + info->chip_dmc = NULL; + info->chip_fds = NULL; + + free(info); + + return; +} + +void device_reset_nes(void *_info) +{ + nes_state *info = (nes_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + device_reset_nesapu(info->chip_apu); + break; +#endif + case EC_NSFPLAY: + NES_APU_np_Reset(info->chip_apu); + NES_DMC_np_Reset(info->chip_dmc); + break; + } + if (info->chip_fds != NULL) + NES_FDS_Reset(info->chip_fds); +} + + +void nes_w(void *_info, offs_t offset, UINT8 data) +{ + nes_state *info = (nes_state *)_info; + + switch(offset & 0xE0) + { + case 0x00: // NES APU + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + nes_psg_w(info->chip_apu, offset, data); + break; +#endif + case EC_NSFPLAY: + // NES_APU handles the sqaure waves, NES_DMC the rest + NES_APU_np_Write(info->chip_apu, 0x4000 | offset, data); + NES_DMC_np_Write(info->chip_dmc, 0x4000 | offset, data); + break; + } + break; + case 0x20: // FDS register + if (info->chip_fds == NULL) + break; + if (offset == 0x3F) + NES_FDS_Write(info->chip_fds, 0x4023, data); + else + NES_FDS_Write(info->chip_fds, 0x4080 | (offset & 0x1F), data); + break; + case 0x40: // FDS wave RAM + case 0x60: + if (info->chip_fds == NULL) + break; + NES_FDS_Write(info->chip_fds, 0x4000 | offset, data); + break; + } +} + +void nes_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) +{ + nes_state* info = (nes_state *)_info; + UINT32 RemainBytes; + + if (DataStart >= 0x10000) + return; + + if (DataStart < 0x8000) + { + if (DataStart + DataLength <= 0x8000) + return; + + RemainBytes = 0x8000 - DataStart; + DataStart = 0x8000; + RAMData += RemainBytes; + DataLength -= RemainBytes; + } + + RemainBytes = 0x00; + if (DataStart + DataLength > 0x10000) + { + RemainBytes = DataLength; + DataLength = 0x10000 - DataStart; + RemainBytes -= DataLength; + } + memcpy(info->Memory + (DataStart - 0x8000), RAMData, DataLength); + if (RemainBytes) + { + if (RemainBytes > 0x8000) + RemainBytes = 0x8000; + memcpy(info->Memory, RAMData + DataLength, RemainBytes); + } + + return; +} + + +static void nes_set_chip_option(nes_state *info, int NesOptions) +{ + UINT8 CurOpt; + + if (NesOptions & 0x8000) + return; + + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + // no options for MAME's NES core + break; +#endif + case EC_NSFPLAY: + // shared APU/DMC options + for (CurOpt = 0; CurOpt < 2; CurOpt ++) + { + NES_APU_np_SetOption(info->chip_apu, CurOpt, (NesOptions >> CurOpt) & 0x01); + NES_DMC_np_SetOption(info->chip_dmc, CurOpt, (NesOptions >> CurOpt) & 0x01); + } + // APU-only options + for (; CurOpt < 4; CurOpt ++) + NES_APU_np_SetOption(info->chip_apu, CurOpt-2+2, (NesOptions >> CurOpt) & 0x01); + // DMC-only options + for (; CurOpt < 10; CurOpt ++) + NES_DMC_np_SetOption(info->chip_dmc, CurOpt-4+2, (NesOptions >> CurOpt) & 0x01); + break; + } + if (info->chip_fds != NULL) + { + // FDS options + // I skip the Cutoff frequency here, since it's not a boolean value. + for (CurOpt = 12; CurOpt < 14; CurOpt ++) + NES_FDS_SetOption(info->chip_fds, CurOpt-12+1, (NesOptions >> CurOpt) & 0x01); + } + + return; +} + +void nes_set_mute_mask(void *_info, UINT32 MuteMask) +{ + nes_state *info = (nes_state *)_info; + switch(info->EMU_CORE) + { +#ifdef ENABLE_ALL_CORES + case EC_MAME: + nesapu_set_mute_mask(info->chip_apu, MuteMask); + break; +#endif + case EC_NSFPLAY: + NES_APU_np_SetMask(info->chip_apu, (MuteMask & 0x03) >> 0); + NES_DMC_np_SetMask(info->chip_dmc, (MuteMask & 0x1C) >> 2); + break; + } + if (info->chip_fds != NULL) + NES_FDS_SetMask(info->chip_fds, (MuteMask & 0x20) >> 5); + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/nes_intf.h b/Frameworks/GME/vgmplay/chips/nes_intf.h new file mode 100644 index 000000000..285e7d8f5 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/nes_intf.h @@ -0,0 +1,13 @@ +#pragma once + +void nes_stream_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_nes(void **chip, int core, int clock, int options, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_nes(void *chip); +void device_reset_nes(void *chip); + +void nes_w(void *chip, offs_t offset, UINT8 data); + +void nes_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); + +void nes_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/np_nes_apu.c b/Frameworks/GME/vgmplay/chips/np_nes_apu.c new file mode 100644 index 000000000..4953a7cd1 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/np_nes_apu.c @@ -0,0 +1,511 @@ +// +// NES 2A03 +// +// Ported from NSFPlay 2.2 to VGMPlay (including C++ -> C conversion) +// by Valley Bell on 24 September 2013 +// Updated to NSFPlay 2.3 on 26 September 2013 +// (Note: Encoding is UTF-8) + +//#include +#include +#include // for memset() +#include // for NULL +#include "mamedef.h" +#include "../stdbool.h" +#include "np_nes_apu.h" + + +// Master Clock: 21477272 (NTSC) +// APU Clock = Master Clock / 12 +#define DEFAULT_CLOCK 1789772.0 // not sure if this shouldn't be 1789772,667 instead +#define DEFAULT_RATE 44100 + + +/** Upper half of APU **/ +enum +{ + OPT_UNMUTE_ON_RESET=0, + OPT_NONLINEAR_MIXER, + OPT_PHASE_REFRESH, + OPT_DUTY_SWAP, + OPT_END +}; + +enum +{ + SQR0_MASK = 1, + SQR1_MASK = 2, +}; + +// Note: For increased speed, I'll inline all of NSFPlay's Counter member functions. +#define COUNTER_SHIFT 24 + +typedef struct _Counter Counter; +struct _Counter +{ + double ratio; + UINT32 val, step; +}; +#define COUNTER_setcycle(cntr, s) (cntr).step = (UINT32)((cntr).ratio / (s + 1)) +#define COUNTER_iup(cntr) (cntr).val += (cntr).step +#define COUNTER_value(cntr) ((cntr).val >> COUNTER_SHIFT) +#define COUNTER_init(cntr, clk, rate) \ +{ \ + (cntr).ratio = (1 << COUNTER_SHIFT) * (1.0 * clk / rate); \ + (cntr).step = (UINT32)((cntr).ratio + 0.5); \ + (cntr).val = 0; \ +} + + +typedef struct _NES_APU NES_APU; +struct _NES_APU +{ + int option[OPT_END]; // å„種オプション + int mask; + INT32 sm[2][2]; + + UINT32 gclock; + UINT8 reg[0x20]; + INT32 out[2]; + double rate, clock; + + INT32 square_table[32]; // nonlinear mixer + + int scounter[2]; // frequency divider + int sphase[2]; // phase counter + + int duty[2]; + int volume[2]; + int freq[2]; + int sfreq[2]; + + bool sweep_enable[2]; + bool sweep_mode[2]; + bool sweep_write[2]; + int sweep_div_period[2]; + int sweep_div[2]; + int sweep_amount[2]; + + bool envelope_disable[2]; + bool envelope_loop[2]; + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + bool enable[2]; + + Counter tick_count; + UINT32 tick_last; +}; + +static void sweep_sqr(NES_APU* apu, int ch); // calculates target sweep frequency +static INT32 calc_sqr(NES_APU* apu, int ch, UINT32 clocks); +static void Tick(NES_APU* apu, UINT32 clocks); + + +static void sweep_sqr(NES_APU* apu, int i) +{ + int shifted = apu->freq[i] >> apu->sweep_amount[i]; + if (i == 0 && apu->sweep_mode[i]) shifted += 1; + apu->sfreq[i] = apu->freq[i] + (apu->sweep_mode[i] ? -shifted : shifted); + //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,apu->freq[i],apu->sweep_amount[i]); +} + +void NES_APU_np_FrameSequence(void* chip, int s) +{ + NES_APU* apu = (NES_APU*)chip; + int i; + + //DEBUG_OUT("FrameSequence(%d)\n",s); + + if (s > 3) return; // no operation in step 4 + + // 240hz clock + for (i=0; i < 2; ++i) + { + bool divider = false; + if (apu->envelope_write[i]) + { + apu->envelope_write[i] = false; + apu->envelope_counter[i] = 15; + apu->envelope_div[i] = 0; + } + else + { + ++apu->envelope_div[i]; + if (apu->envelope_div[i] > apu->envelope_div_period[i]) + { + divider = true; + apu->envelope_div[i] = 0; + } + } + if (divider) + { + if (apu->envelope_loop[i] && apu->envelope_counter[i] == 0) + apu->envelope_counter[i] = 15; + else if (apu->envelope_counter[i] > 0) + --apu->envelope_counter[i]; + } + } + + // 120hz clock + if ((s&1) == 0) + for (i=0; i < 2; ++i) + { + if (!apu->envelope_loop[i] && (apu->length_counter[i] > 0)) + --apu->length_counter[i]; + + if (apu->sweep_enable[i]) + { + //DEBUG_OUT("Clock sweep: %d\n", i); + + --apu->sweep_div[i]; + if (apu->sweep_div[i] <= 0) + { + sweep_sqr(apu, i); // calculate new sweep target + + //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,apu->sweep_div_period[i]); + //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,apu->freq[i],i,apu->sfreq[i]); + + if (apu->freq[i] >= 8 && apu->sfreq[i] < 0x800 && apu->sweep_amount[i] > 0) // update frequency if appropriate + { + apu->freq[i] = apu->sfreq[i] < 0 ? 0 : apu->sfreq[i]; + if (apu->scounter[i] > apu->freq[i]) apu->scounter[i] = apu->freq[i]; + } + apu->sweep_div[i] = apu->sweep_div_period[i] + 1; + + //DEBUG_OUT("freq[%d]=%d\n",i,apu->freq[i]); + } + + if (apu->sweep_write[i]) + { + apu->sweep_div[i] = apu->sweep_div_period[i] + 1; + apu->sweep_write[i] = false; + } + } + } + +} + +static INT32 calc_sqr(NES_APU* apu, int i, UINT32 clocks) +{ + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + INT32 ret = 0; + + apu->scounter[i] += clocks; + while (apu->scounter[i] > apu->freq[i]) + { + apu->sphase[i] = (apu->sphase[i] + 1) & 15; + apu->scounter[i] -= (apu->freq[i] + 1); + } + + //INT32 ret = 0; + if (apu->length_counter[i] > 0 && + apu->freq[i] >= 8 && + apu->sfreq[i] < 0x800 + ) + { + int v = apu->envelope_disable[i] ? apu->volume[i] : apu->envelope_counter[i]; + ret = sqrtbl[apu->duty[i]][apu->sphase[i]] ? v : 0; + } + + return ret; +} + +bool NES_APU_np_Read(void* chip, UINT32 adr, UINT32* val) +{ + NES_APU* apu = (NES_APU*)chip; + + if (0x4000 <= adr && adr < 0x4008) + { + *val |= apu->reg[adr&0x7]; + return true; + } + else if(adr==0x4015) + { + *val |= (apu->length_counter[1]?2:0)|(apu->length_counter[0]?1:0); + return true; + } + else + return false; +} + +static void Tick(NES_APU* apu, UINT32 clocks) +{ + apu->out[0] = calc_sqr(apu, 0, clocks); + apu->out[1] = calc_sqr(apu, 1, clocks); +} + +// 生æˆã•ã‚Œã‚‹æ³¢å½¢ã®æŒ¯å¹…ã¯0-8191 +UINT32 NES_APU_np_Render(void* chip, INT32 b[2]) +{ + NES_APU* apu = (NES_APU*)chip; + INT32 m[2]; + + COUNTER_iup(apu->tick_count); + Tick(apu, (COUNTER_value(apu->tick_count) - apu->tick_last) & 0xFF); + apu->tick_last = COUNTER_value(apu->tick_count); + + apu->out[0] = (apu->mask & 1) ? 0 : apu->out[0]; + apu->out[1] = (apu->mask & 2) ? 0 : apu->out[1]; + + if(apu->option[OPT_NONLINEAR_MIXER]) + { + INT32 voltage; + INT32 ref; + + voltage = apu->square_table[apu->out[0] + apu->out[1]]; + m[0] = apu->out[0] << 6; + m[1] = apu->out[1] << 6; + ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + } + else + { + m[0] = apu->out[0] << 6; + m[1] = apu->out[1] << 6; + } + + // Shifting is (x-2) to match the volume of MAME's NES APU sound core + b[0] = m[0] * apu->sm[0][0]; + b[0] += m[1] * apu->sm[0][1]; + b[0] >>= 7-2; // was 7, but is now 8 for bipolar square + + b[1] = m[0] * apu->sm[1][0]; + b[1] += m[1] * apu->sm[1][1]; + b[1] >>= 7-2; // see above + + return 2; +} + +void* NES_APU_np_Create(int clock, int rate) +{ + NES_APU* apu; + int i, c, t; + + apu = (NES_APU*)malloc(sizeof(NES_APU)); + if (apu == NULL) + return NULL; + memset(apu, 0x00, sizeof(NES_APU)); + + //NES_APU_np_SetClock(apu, DEFAULT_CLOCK); + //NES_APU_np_SetRate(apu, DEFAULT_RATE); + NES_APU_np_SetClock(apu, clock); + NES_APU_np_SetRate(apu, rate); + apu->option[OPT_UNMUTE_ON_RESET] = true; + apu->option[OPT_PHASE_REFRESH] = true; + apu->option[OPT_NONLINEAR_MIXER] = true; + apu->option[OPT_DUTY_SWAP] = false; + + apu->square_table[0] = 0; + for(i=1;i<32;i++) + apu->square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + for(c=0;c<2;++c) + for(t=0;t<2;++t) + apu->sm[c][t] = 128; + + return apu; +} + +void NES_APU_np_Destroy(void* chip) +{ + free(chip); +} + +void NES_APU_np_Reset(void* chip) +{ + NES_APU* apu = (NES_APU*)chip; + int i; + apu->gclock = 0; + apu->mask = 0; + + apu->scounter[0] = 0; + apu->scounter[1] = 0; + apu->sphase[0] = 0; + apu->sphase[0] = 0; + + apu->sweep_div[0] = 1; + apu->sweep_div[1] = 1; + apu->envelope_div[0] = 0; + apu->envelope_div[1] = 0; + apu->length_counter[0] = 0; + apu->length_counter[1] = 0; + apu->envelope_counter[0] = 0; + apu->envelope_counter[1] = 0; + + for (i = 0x4000; i < 0x4008; i++) + NES_APU_np_Write(apu, i, 0); + + NES_APU_np_Write(apu, 0x4015, 0); + if (apu->option[OPT_UNMUTE_ON_RESET]) + NES_APU_np_Write(apu, 0x4015, 0x0f); + + for (i = 0; i < 2; i++) + apu->out[i] = 0; + + NES_APU_np_SetRate(apu, apu->rate); +} + +void NES_APU_np_SetOption(void* chip, int id, int val) +{ + NES_APU* apu = (NES_APU*)chip; + + if(idoption[id] = val; +} + +void NES_APU_np_SetClock(void* chip, double c) +{ + NES_APU* apu = (NES_APU*)chip; + + apu->clock = c; +} + +void NES_APU_np_SetRate(void* chip, double r) +{ + NES_APU* apu = (NES_APU*)chip; + + apu->rate = r ? r : DEFAULT_RATE; + + COUNTER_init(apu->tick_count, apu->clock, apu->rate); + apu->tick_last = 0; +} + +void NES_APU_np_SetMask(void* chip, int m) +{ + NES_APU* apu = (NES_APU*)chip; + apu->mask = m; +} + +void NES_APU_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr) +{ + NES_APU* apu = (NES_APU*)chip; + + if (trk < 0) return; + if (trk > 1) return; + apu->sm[0][trk] = mixl; + apu->sm[1][trk] = mixr; +} + +bool NES_APU_np_Write(void* chip, UINT32 adr, UINT32 val) +{ + NES_APU* apu = (NES_APU*)chip; + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if (0x4000 <= adr && adr < 0x4008) + { + //DEBUG_OUT("$%04X = %02X\n",adr,val); + + adr &= 0xf; + ch = adr >> 2; + switch (adr) + { + case 0x0: + case 0x4: + apu->volume[ch] = val & 15; + apu->envelope_disable[ch] = (val >> 4) & 1; + apu->envelope_loop[ch] = (val >> 5) & 1; + apu->envelope_div_period[ch] = (val & 15); + apu->duty[ch] = (val >> 6) & 3; + if (apu->option[OPT_DUTY_SWAP]) + { + if (apu->duty[ch] == 1) apu->duty[ch] = 2; + else if (apu->duty[ch] == 2) apu->duty[ch] = 1; + } + break; + + case 0x1: + case 0x5: + apu->sweep_enable[ch] = (val >> 7) & 1; + apu->sweep_div_period[ch] = (((val >> 4) & 7)); + apu->sweep_mode[ch] = (val >> 3) & 1; + apu->sweep_amount[ch] = val & 7; + apu->sweep_write[ch] = true; + sweep_sqr(apu, ch); + break; + + case 0x2: + case 0x6: + apu->freq[ch] = val | (apu->freq[ch] & 0x700) ; + sweep_sqr(apu, ch); + if (apu->scounter[ch] > apu->freq[ch]) apu->scounter[ch] = apu->freq[ch]; + break; + + case 0x3: + case 0x7: + apu->freq[ch] = (apu->freq[ch] & 0xFF) | ((val & 0x7) << 8) ; + + if (apu->option[OPT_PHASE_REFRESH]) + apu->sphase[ch] = 0; + apu->envelope_write[ch] = true; + if (apu->enable[ch]) + { + apu->length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + sweep_sqr(apu, ch); + if (apu->scounter[ch] > apu->freq[ch]) apu->scounter[ch] = apu->freq[ch]; + break; + + default: + return false; + } + apu->reg[adr] = val; + return true; + } + else if (adr == 0x4015) + { + apu->enable[0] = (val & 1) ? true : false; + apu->enable[1] = (val & 2) ? true : false; + + if (!apu->enable[0]) + apu->length_counter[0] = 0; + if (!apu->enable[1]) + apu->length_counter[1] = 0; + + apu->reg[adr-0x4000] = val; + return true; + } + + // 4017 is handled in np_nes_dmc.c + //else if (adr == 0x4017) + //{ + //} + + return false; +} diff --git a/Frameworks/GME/vgmplay/chips/np_nes_apu.h b/Frameworks/GME/vgmplay/chips/np_nes_apu.h new file mode 100644 index 000000000..0ee285b20 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/np_nes_apu.h @@ -0,0 +1,12 @@ +void NES_APU_np_FrameSequence(void* chip, int s); +void* NES_APU_np_Create(int clock, int rate); +void NES_APU_np_Destroy(void* chip); +void NES_APU_np_Reset(void* chip); +bool NES_APU_np_Read(void* chip, UINT32 adr, UINT32* val); +bool NES_APU_np_Write(void* chip, UINT32 adr, UINT32 val); +UINT32 NES_APU_np_Render(void* chip, INT32 b[2]); +void NES_APU_np_SetRate(void* chip, double rate); +void NES_APU_np_SetClock(void* chip, double clock); +void NES_APU_np_SetOption(void* chip, int id, int b); +void NES_APU_np_SetMask(void* chip, int m); +void NES_APU_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr); diff --git a/Frameworks/GME/vgmplay/chips/np_nes_dmc.c b/Frameworks/GME/vgmplay/chips/np_nes_dmc.c new file mode 100644 index 000000000..a4f4aa1c0 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/np_nes_dmc.c @@ -0,0 +1,871 @@ +// Ported from NSFPlay to VGMPlay (including C++ -> C conversion) +// by Valley Bell on 25 September 2013 +// Updated to NSFPlay 2.3 on 26 September 2013 +// (Note: Encoding is UTF-8) + +#include // for rand() +#include // for memset() +#include // for NULL +#include "mamedef.h" +#include "../stdbool.h" +#include "np_nes_apu.h" // for NES_APU_np_FrameSequence +#include "np_nes_dmc.h" + + +// Master Clock: 21477272 (NTSC) +// APU Clock = Master Clock / 12 +#define DEFAULT_CLOCK 1789772.0 +#define DEFAULT_CLK_PAL 1662607 +#define DEFAULT_RATE 44100 + + +/** Bottom Half of APU **/ +enum +{ + OPT_UNMUTE_ON_RESET=0, + OPT_NONLINEAR_MIXER, + OPT_ENABLE_4011, + OPT_ENABLE_PNOISE, + OPT_DPCM_ANTI_CLICK, + OPT_RANDOMIZE_NOISE, + OPT_TRI_MUTE, + OPT_TRI_NULL, + OPT_END +}; + + +// Note: For increased speed, I'll inline all of NSFPlay's Counter member functions. +#define COUNTER_SHIFT 24 + +typedef struct _Counter Counter; +struct _Counter +{ + double ratio; + UINT32 val, step; +}; +#define COUNTER_setcycle(cntr, s) (cntr).step = (UINT32)((cntr).ratio / (s + 1)) +#define COUNTER_iup(cntr) (cntr).val += (cntr).step +#define COUNTER_value(cntr) ((cntr).val >> COUNTER_SHIFT) +#define COUNTER_init(cntr, clk, rate) \ +{ \ + (cntr).ratio = (1 << COUNTER_SHIFT) * (1.0 * clk / rate); \ + (cntr).step = (UINT32)((cntr).ratio + 0.5); \ + (cntr).val = 0; \ +} + + +typedef struct _NES_DMC NES_DMC; +struct _NES_DMC +{ + //const int GETA_BITS; + //static const UINT32 freq_table[2][16]; + //static const UINT32 wavlen_table[2][16]; + UINT32 tnd_table[2][16][16][128]; + + int option[OPT_END]; + int mask; + INT32 sm[2][3]; + UINT8 reg[0x10]; + UINT32 len_reg; + UINT32 adr_reg; + //IDevice *memory; + const UINT8* memory; + UINT32 out[3]; + UINT32 daddress; + UINT32 length; + UINT32 data; + INT16 damp; + int dac_lsb; + bool dmc_pop; + INT32 dmc_pop_offset; + INT32 dmc_pop_follow; + UINT32 clock; + UINT32 rate; + int pal; + int mode; + bool irq; + bool active; + + UINT32 counter[3]; // frequency dividers + int tphase; // triangle phase + UINT32 nfreq; // noise frequency + UINT32 dfreq; // DPCM frequency + + UINT32 tri_freq; + int linear_counter; + int linear_counter_reload; + bool linear_counter_halt; + bool linear_counter_control; + + int noise_volume; + UINT32 noise, noise_tap; + + // noise envelope + bool envelope_loop; + bool envelope_disable; + bool envelope_write; + int envelope_div_period; + int envelope_div; + int envelope_counter; + + bool enable[3]; + int length_counter[2]; // 0=tri, 1=noise + + // frame sequencer + void* apu; // apu is clocked by DMC's frame sequencer + int frame_sequence_count; // current cycle count + int frame_sequence_length; // CPU cycles per FrameSequence + int frame_sequence_step; // current step of frame sequence + int frame_sequence_steps; // 4/5 steps per frame + bool frame_irq; + bool frame_irq_enable; + + Counter tick_count; + UINT32 tick_last; +}; + +INLINE UINT32 calc_tri(NES_DMC* dmc, UINT32 clocks); +INLINE UINT32 calc_dmc(NES_DMC* dmc, UINT32 clocks); +INLINE UINT32 calc_noise(NES_DMC* dmc, UINT32 clocks); +static void FrameSequence(NES_DMC* dmc, int s); +static void TickFrameSequence(NES_DMC* dmc, UINT32 clocks); +static void Tick(NES_DMC* dmc, UINT32 clocks); + +#define GETA_BITS 20 +static const UINT32 wavlen_table[2][16] = { +{ // NTSC + 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 +}, +{ // PAL + 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 +}}; + +static const UINT32 freq_table[2][16] = { +{ // NTSC + 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 +}, +{ // PAL + 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 +}}; + +void* NES_DMC_np_Create(int clock, int rate) +{ + NES_DMC* dmc; + int c, t; + + dmc = (NES_DMC*)malloc(sizeof(NES_DMC)); + if (dmc == NULL) + return NULL; + memset(dmc, 0x00, sizeof(NES_DMC)); + + //NES_DMC_np_SetClock(dmc, DEFAULT_CLOCK); + //NES_DMC_np_SetRate(dmc, DEFAULT_RATE); + //NES_DMC_np_SetPal(dmc, false); + NES_DMC_np_SetClock(dmc, clock); // does SetPal, too + NES_DMC_np_SetRate(dmc, rate); + dmc->option[OPT_ENABLE_4011] = 1; + dmc->option[OPT_ENABLE_PNOISE] = 1; + dmc->option[OPT_UNMUTE_ON_RESET] = 1; + dmc->option[OPT_DPCM_ANTI_CLICK] = 0; + dmc->option[OPT_NONLINEAR_MIXER] = 1; + dmc->option[OPT_RANDOMIZE_NOISE] = 1; + dmc->option[OPT_TRI_MUTE] = 1; + dmc->tnd_table[0][0][0][0] = 0; + dmc->tnd_table[1][0][0][0] = 0; + + dmc->apu = NULL; + dmc->frame_sequence_count = 0; + dmc->frame_sequence_length = 7458; + dmc->frame_sequence_steps = 4; + + for(c=0;c<2;++c) + for(t=0;t<3;++t) + dmc->sm[c][t] = 128; + + return dmc; +} + + +void NES_DMC_np_Destroy(void* chip) +{ + free(chip); +} + +int NES_DMC_np_GetDamp(void* chip) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + return (dmc->damp<<1)|dmc->dac_lsb; +} + +void NES_DMC_np_SetMask(void* chip, int m) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + dmc->mask = m; +} + +void NES_DMC_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + if (trk < 0) return; + if (trk > 2) return; + dmc->sm[0][trk] = mixl; + dmc->sm[1][trk] = mixr; +} + +static void FrameSequence(NES_DMC* dmc, int s) +{ + //DEBUG_OUT("FrameSequence: %d\n",s); + + if (s > 3) return; // no operation in step 4 + + if (dmc->apu != NULL) + { + NES_APU_np_FrameSequence(dmc->apu, s); + } + + if (s == 0 && (dmc->frame_sequence_steps == 4)) + { + dmc->frame_irq = true; + } + + // 240hz clock + { + bool divider = false; + + // triangle linear counter + if (dmc->linear_counter_halt) + { + dmc->linear_counter = dmc->linear_counter_reload; + } + else + { + if (dmc->linear_counter > 0) --dmc->linear_counter; + } + if (!dmc->linear_counter_control) + { + dmc->linear_counter_halt = false; + } + + // noise envelope + //bool divider = false; + if (dmc->envelope_write) + { + dmc->envelope_write = false; + dmc->envelope_counter = 15; + dmc->envelope_div = 0; + } + else + { + ++dmc->envelope_div; + if (dmc->envelope_div > dmc->envelope_div_period) + { + divider = true; + dmc->envelope_div = 0; + } + } + if (divider) + { + if (dmc->envelope_loop && dmc->envelope_counter == 0) + dmc->envelope_counter = 15; + else if (dmc->envelope_counter > 0) + --dmc->envelope_counter; // TODO: Make this work. + } + } + + // 120hz clock + if ((s&1) == 0) + { + // triangle length counter + if (!dmc->linear_counter_control && (dmc->length_counter[0] > 0)) + --dmc->length_counter[0]; + + // noise length counter + if (!dmc->envelope_loop && (dmc->length_counter[1] > 0)) + --dmc->length_counter[1]; + } + +} + +// 三角波ãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-15 +UINT32 calc_tri(NES_DMC* dmc, UINT32 clocks) +{ + static UINT32 tritbl[32] = + { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0 + }; + + if (dmc->linear_counter > 0 && dmc->length_counter[0] > 0 + && (!dmc->option[OPT_TRI_MUTE] || dmc->tri_freq > 0)) + { + dmc->counter[0] += clocks; + while (dmc->counter[0] > dmc->tri_freq) + { + dmc->tphase = (dmc->tphase + 1) & 31; + dmc->counter[0] -= (dmc->tri_freq + 1); + } + } + // Note: else-block added by VB + else if (dmc->option[OPT_TRI_NULL]) + { + if (dmc->tphase && dmc->tphase < 31) + { + // Finish the Triangle wave to prevent clicks. + dmc->counter[0] += clocks; + while(dmc->counter[0] > dmc->tri_freq && dmc->tphase) + { + dmc->tphase = (dmc->tphase + 1) & 31; + dmc->counter[0] -= (dmc->tri_freq + 1); + } + } + } + + //UINT32 ret = tritbl[tphase]; + //return ret; + return tritbl[dmc->tphase]; +} + +// ノイズãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-127 +// 低サンプリングレートã§åˆæˆã™ã‚‹ã¨ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãƒŽã‚¤ã‚ºãŒæ¿€ã—ã„ã®ã§ +// ノイズã ã‘ã¯ã“ã®é–¢æ•°å†…ã§é«˜ã‚¯ãƒ­ãƒƒã‚¯åˆæˆã—ã€ç°¡æ˜“ãªã‚µãƒ³ãƒ—リングレート +// 変æ›ã‚’行ã£ã¦ã„る。 +UINT32 calc_noise(NES_DMC* dmc, UINT32 clocks) +{ + UINT32 env, last, count, accum, clocks_accum; + + env = dmc->envelope_disable ? dmc->noise_volume : dmc->envelope_counter; + if (dmc->length_counter[1] < 1) env = 0; + + last = (dmc->noise & 0x4000) ? env : 0; + if (clocks < 1) return last; + + // simple anti-aliasing (noise requires it, even when oversampling is off) + count = 0; + accum = 0; + + dmc->counter[1] += clocks; +// assert(dmc->nfreq > 0); // prevent infinite loop + if (dmc->nfreq <= 0) // prevent infinite loop -VB + return last; + while (dmc->counter[1] >= dmc->nfreq) + { + // tick the noise generator + UINT32 feedback = (dmc->noise&1) ^ ((dmc->noise&dmc->noise_tap)?1:0); + dmc->noise = (dmc->noise>>1) | (feedback<<14); + + ++count; + accum += last; + last = (dmc->noise & 0x4000) ? env : 0; + + dmc->counter[1] -= dmc->nfreq; + } + + if (count < 1) // no change over interval, don't anti-alias + { + return last; + } + + clocks_accum = clocks - dmc->counter[1]; + // count = number of samples in accum + // counter[1] = number of clocks since last sample + + accum = (accum * clocks_accum) + (last * dmc->counter[1] * count); + // note accum as an average is already premultiplied by count + + return accum / (clocks * count); +} + +// DMCãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-127 +UINT32 calc_dmc(NES_DMC* dmc, UINT32 clocks) +{ + dmc->counter[2] += clocks; +// assert(dmc->dfreq > 0); // prevent infinite loop + if (dmc->dfreq <= 0) // prevent infinite loop -VB + return (dmc->damp<<1) + dmc->dac_lsb; + while (dmc->counter[2] >= dmc->dfreq) + { + if ( dmc->data != 0x100 ) // data = 0x100 㯠EMPTY ã‚’æ„味ã™ã‚‹ã€‚ + { + if ((dmc->data & 1) && (dmc->damp < 63)) + dmc->damp++; + else if (!(dmc->data & 1) && (0 < dmc->damp)) + dmc->damp--; + dmc->data >>=1; + } + + if ( dmc->data == 0x100 && dmc->active ) + { + //dmc->memory->Read(dmc->daddress, dmc->data); + dmc->data = dmc->memory[dmc->daddress]; + dmc->data |= (dmc->data&0xFF)|0x10000; // 8bitシフト㧠0x100 ã«ãªã‚‹ + if ( dmc->length > 0 ) + { + dmc->daddress = ((dmc->daddress+1)&0xFFFF)|0x8000 ; + dmc->length --; + } + } + + if ( dmc->length == 0 ) // 最後ã®ãƒ•ェッãƒãŒçµ‚了ã—ãŸã‚‰(å†ç”Ÿå®Œäº†ã‚ˆã‚Šå‰ã«)å³åº§ã«çµ‚ç«¯å‡¦ç† + { + if (dmc->mode & 1) + { + dmc->daddress = ((dmc->adr_reg<<6)|0xC000); + dmc->length = (dmc->len_reg<<4)+1; + } + else + { + dmc->irq = (dmc->mode==2&&dmc->active)?1:0; // ç›´å‰ãŒactiveã ã£ãŸã¨ãã¯IRQ発行 + dmc->active = false; + } + } + + dmc->counter[2] -= dmc->dfreq; + } + + return (dmc->damp<<1) + dmc->dac_lsb; +} + +static void TickFrameSequence(NES_DMC* dmc, UINT32 clocks) +{ + dmc->frame_sequence_count += clocks; + while (dmc->frame_sequence_count > dmc->frame_sequence_length) + { + FrameSequence(dmc, dmc->frame_sequence_step); + dmc->frame_sequence_count -= dmc->frame_sequence_length; + ++dmc->frame_sequence_step; + if(dmc->frame_sequence_step >= dmc->frame_sequence_steps) + dmc->frame_sequence_step = 0; + } +} + +static void Tick(NES_DMC* dmc, UINT32 clocks) +{ + dmc->out[0] = calc_tri(dmc, clocks); + dmc->out[1] = calc_noise(dmc, clocks); + dmc->out[2] = calc_dmc(dmc, clocks); +} + +UINT32 NES_DMC_np_Render(void* chip, INT32 b[2]) +{ + NES_DMC* dmc = (NES_DMC*)chip; + UINT32 clocks; + INT32 m[3]; + + COUNTER_iup(dmc->tick_count); // increase counter (overflows after 255) + clocks = (COUNTER_value(dmc->tick_count) - dmc->tick_last) & 0xFF; + TickFrameSequence(dmc, clocks); + Tick(dmc, clocks); + dmc->tick_last = COUNTER_value(dmc->tick_count); + + dmc->out[0] = (dmc->mask & 1) ? 0 : dmc->out[0]; + dmc->out[1] = (dmc->mask & 2) ? 0 : dmc->out[1]; + dmc->out[2] = (dmc->mask & 4) ? 0 : dmc->out[2]; + + m[0] = dmc->tnd_table[0][dmc->out[0]][0][0]; + m[1] = dmc->tnd_table[0][0][dmc->out[1]][0]; + m[2] = dmc->tnd_table[0][0][0][dmc->out[2]]; + + if (dmc->option[OPT_NONLINEAR_MIXER]) + { + INT32 ref = m[0] + m[1] + m[2]; + INT32 voltage = dmc->tnd_table[1][dmc->out[0]][dmc->out[1]][dmc->out[2]]; + int i; + if (ref) + { + for (i=0; i < 3; ++i) + m[i] = (m[i] * voltage) / ref; + } + else + { + for (i=0; i < 3; ++i) + m[i] = voltage; + } + } + + // anti-click nullifies any 4011 write but preserves nonlinearity + if (dmc->option[OPT_DPCM_ANTI_CLICK]) + { + if (dmc->dmc_pop) // $4011 will cause pop this frame + { + // adjust offset to counteract pop + dmc->dmc_pop_offset += dmc->dmc_pop_follow - m[2]; + dmc->dmc_pop = false; + + // prevent overflow, keep headspace at edges + //const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); +#define OFFSET_MAX ((1 << 30) - (4 << 16)) + if (dmc->dmc_pop_offset > OFFSET_MAX) dmc->dmc_pop_offset = OFFSET_MAX; + if (dmc->dmc_pop_offset < -OFFSET_MAX) dmc->dmc_pop_offset = -OFFSET_MAX; + } + dmc->dmc_pop_follow = m[2]; // remember previous position + + m[2] += dmc->dmc_pop_offset; // apply offset + + // TODO implement this in a better way + // roll off offset (not ideal, but prevents overflow) + if (dmc->dmc_pop_offset > 0) --dmc->dmc_pop_offset; + else if (dmc->dmc_pop_offset < 0) ++dmc->dmc_pop_offset; + } + + b[0] = m[0] * dmc->sm[0][0]; + b[0] += m[1] * dmc->sm[0][1]; + b[0] +=-m[2] * dmc->sm[0][2]; + b[0] >>= 7-2; + + b[1] = m[0] * dmc->sm[1][0]; + b[1] += m[1] * dmc->sm[1][1]; + b[1] +=-m[2] * dmc->sm[1][2]; + b[1] >>= 7-2; + + return 2; +} + + +void NES_DMC_np_SetClock(void* chip, double c) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + dmc->clock = (UINT32)(c); + + if (abs(dmc->clock - DEFAULT_CLK_PAL) <= 1000) // check for approximately DEFAULT_CLK_PAL + NES_DMC_np_SetPal(dmc, true); + else + NES_DMC_np_SetPal(dmc, false); +} + +void NES_DMC_np_SetRate(void* chip, double r) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + dmc->rate = (UINT32)(r?r:DEFAULT_RATE); + + COUNTER_init(dmc->tick_count, dmc->clock, dmc->rate); + dmc->tick_last = 0; +} + +void NES_DMC_np_SetPal(void* chip, bool is_pal) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + dmc->pal = (is_pal ? 1 : 0); + // set CPU cycles in frame_sequence + dmc->frame_sequence_length = is_pal ? 8314 : 7458; +} + +void NES_DMC_np_SetAPU(void* chip, void* apu_) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + dmc->apu = apu_; +} + +// Initializing TRI, NOISE, DPCM mixing table +static void InitializeTNDTable(NES_DMC* dmc, double wt, double wn, double wd) +{ + // volume adjusted by 0.75 based on empirical measurements + const double MASTER = 8192.0 * 0.75; + // truthfully, the nonlinear curve does not appear to match well + // with my tests, triangle in particular seems too quiet relatively. + // do more testing of the APU/DMC DAC later + + int t, n, d; + + { // Linear Mixer + for(t=0; t<16 ; t++) { + for(n=0; n<16; n++) { + for(d=0; d<128; d++) { + dmc->tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); + } + } + } + } + { // Non-Linear Mixer + dmc->tnd_table[1][0][0][0] = 0; + for(t=0; t<16 ; t++) { + for(n=0; n<16; n++) { + for(d=0; d<128; d++) { + if(t!=0||n!=0||d!=0) + dmc->tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); + } + } + } + } + +} + +void NES_DMC_np_Reset(void* chip) +{ + NES_DMC* dmc = (NES_DMC*)chip; + int i; + dmc->mask = 0; + + InitializeTNDTable(dmc,8227,12241,22638); + + dmc->counter[0] = 0; + dmc->counter[1] = 0; + dmc->counter[2] = 0; + dmc->tphase = 0; + dmc->nfreq = wavlen_table[0][0]; + dmc->dfreq = freq_table[0][0]; + + dmc->envelope_div = 0; + dmc->length_counter[0] = 0; + dmc->length_counter[1] = 0; + dmc->linear_counter = 0; + dmc->envelope_counter = 0; + + dmc->frame_irq = false; + dmc->frame_irq_enable = false; + dmc->frame_sequence_count = 0; + dmc->frame_sequence_steps = 4; + dmc->frame_sequence_step = 0; + + for (i = 0; i < 0x10; i++) + NES_DMC_np_Write(dmc, 0x4008 + i, 0); + + dmc->irq = false; + NES_DMC_np_Write(dmc, 0x4015, 0x00); + if (dmc->option[OPT_UNMUTE_ON_RESET]) + NES_DMC_np_Write(dmc, 0x4015, 0x0f); + + dmc->out[0] = dmc->out[1] = dmc->out[2] = 0; + dmc->tri_freq = 0; + dmc->damp = 0; + dmc->dmc_pop = false; + dmc->dmc_pop_offset = 0; + dmc->dmc_pop_follow = 0; + dmc->dac_lsb = 0; + dmc->data = 0x100; + dmc->adr_reg = 0; + dmc->active = false; + dmc->length = 0; + dmc->len_reg = 0; + dmc->daddress = 0; + dmc->noise = 1; + dmc->noise_tap = (1<<1); + if (dmc->option[OPT_RANDOMIZE_NOISE]) + { + dmc->noise |= rand(); + } + + NES_DMC_np_SetRate(dmc, dmc->rate); +} + +void NES_DMC_np_SetMemory(void* chip, const UINT8* r) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + dmc->memory = r; +} + +void NES_DMC_np_SetOption(void* chip, int id, int val) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + if(idoption[id] = val; + if(id==OPT_NONLINEAR_MIXER) + InitializeTNDTable(dmc, 8227,12241,22638); + } +} + +bool NES_DMC_np_Write(void* chip, UINT32 adr, UINT32 val) +{ + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + NES_DMC* dmc = (NES_DMC*)chip; + + if (adr == 0x4015) + { + dmc->enable[0] = (val & 4) ? true : false; + dmc->enable[1] = (val & 8) ? true : false; + + if (!dmc->enable[0]) + { + dmc->length_counter[0] = 0; + } + if (!dmc->enable[1]) + { + dmc->length_counter[1] = 0; + } + + if ((val & 16)&&!dmc->active) + { + dmc->enable[2] = dmc->active = true; + dmc->daddress = (0xC000 | (dmc->adr_reg << 6)); + dmc->length = (dmc->len_reg << 4) + 1; + dmc->irq = 0; + } + else if (!(val & 16)) + { + dmc->enable[2] = dmc->active = false; + } + + dmc->reg[adr-0x4008] = val; + return true; + } + + if (adr == 0x4017) + { + //DEBUG_OUT("4017 = %02X\n", val); + dmc->frame_irq_enable = ((val & 0x40) == 0x40); + dmc->frame_irq = (dmc->frame_irq_enable ? dmc->frame_irq : 0); + dmc->frame_sequence_count = 0; + if (val & 0x80) + { + dmc->frame_sequence_steps = 5; + dmc->frame_sequence_step = 0; + FrameSequence(dmc, dmc->frame_sequence_step); + ++dmc->frame_sequence_step; + } + else + { + dmc->frame_sequence_steps = 4; + dmc->frame_sequence_step = 1; + } + } + + if (adr<0x4008||0x4013reg[adr-0x4008] = val&0xff; + + //DEBUG_OUT("$%04X %02X\n", adr, val); + + switch (adr) + { + + // tri + + case 0x4008: + dmc->linear_counter_control = (val >> 7) & 1; + dmc->linear_counter_reload = val & 0x7F; + break; + + case 0x4009: + break; + + case 0x400a: + dmc->tri_freq = val | (dmc->tri_freq & 0x700) ; + if (dmc->counter[0] > dmc->tri_freq) dmc->counter[0] = dmc->tri_freq; + break; + + case 0x400b: + dmc->tri_freq = (dmc->tri_freq & 0xff) | ((val & 0x7) << 8) ; + if (dmc->counter[0] > dmc->tri_freq) dmc->counter[0] = dmc->tri_freq; + dmc->linear_counter_halt = true; + if (dmc->enable[0]) + { + dmc->length_counter[0] = length_table[(val >> 3) & 0x1f]; + } + break; + + // noise + + case 0x400c: + dmc->noise_volume = val & 15; + dmc->envelope_div_period = val & 15; + dmc->envelope_disable = (val >> 4) & 1; + dmc->envelope_loop = (val >> 5) & 1; + break; + + case 0x400d: + break; + + case 0x400e: + if (dmc->option[OPT_ENABLE_PNOISE]) + dmc->noise_tap = (val & 0x80) ? (1<<6) : (1<<1); + else + dmc->noise_tap = (1<<1); + dmc->nfreq = wavlen_table[dmc->pal][val&15]; + if (dmc->counter[1] > dmc->nfreq) dmc->counter[1] = dmc->nfreq; + break; + + case 0x400f: + if (dmc->enable[1]) + { + dmc->length_counter[1] = length_table[(val >> 3) & 0x1f]; + } + dmc->envelope_write = true; + break; + + // dmc + + case 0x4010: + dmc->mode = (val >> 6) & 3; + dmc->dfreq = freq_table[dmc->pal][val&15]; + if (dmc->counter[2] > dmc->dfreq) dmc->counter[2] = dmc->dfreq; + break; + + case 0x4011: + if (dmc->option[OPT_ENABLE_4011]) + { + dmc->damp = (val >> 1) & 0x3f; + dmc->dac_lsb = val & 1; + dmc->dmc_pop = true; + } + break; + + case 0x4012: + dmc->adr_reg = val&0xff; + // ã“ã“ã§daddressã¯æ›´æ–°ã•れãªã„ + break; + + case 0x4013: + dmc->len_reg = val&0xff; + // ã“ã“ã§lengthã¯æ›´æ–°ã•れãªã„ + break; + + default: + return false; + } + + return true; +} + +bool NES_DMC_np_Read(void* chip, UINT32 adr, UINT32* val) +{ + NES_DMC* dmc = (NES_DMC*)chip; + + if (adr == 0x4015) + { + *val |= (dmc->irq?128:0) + | (dmc->frame_irq ? 0x40 : 0) + | (dmc->active?16:0) + | (dmc->length_counter[1]?8:0) + | (dmc->length_counter[0]?4:0) + ; + + dmc->frame_irq = false; + return true; + } + else if (0x4008<=adr&&adr<=0x4014) + { + *val |= dmc->reg[adr-0x4008]; + return true; + } + else + return false; +} diff --git a/Frameworks/GME/vgmplay/chips/np_nes_dmc.h b/Frameworks/GME/vgmplay/chips/np_nes_dmc.h new file mode 100644 index 000000000..b51b6a088 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/np_nes_dmc.h @@ -0,0 +1,20 @@ +#ifndef _NP_NES_DMC_H_ +#define _NP_NES_DMC_H_ + +void* NES_DMC_np_Create(int clock, int rate); +void NES_DMC_np_Destroy(void *chip); +void NES_DMC_np_Reset(void *chip); +void NES_DMC_np_SetRate(void* chip, double rate); +void NES_DMC_np_SetPal(void* chip, bool is_pal); +void NES_DMC_np_SetAPU(void* chip, void* apu_); +UINT32 NES_DMC_np_Render(void* chip, INT32 b[2]); +void NES_DMC_np_SetMemory(void* chip, const UINT8* r); +bool NES_DMC_np_Write(void* chip, UINT32 adr, UINT32 val); +bool NES_DMC_np_Read(void* chip, UINT32 adr, UINT32* val); +void NES_DMC_np_SetClock(void* chip, double rate); +void NES_DMC_np_SetOption(void* chip, int id, int val); +int NES_DMC_np_GetDamp(void* chip); +void NES_DMC_np_SetMask(void* chip, int m); +void NES_DMC_np_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr); + +#endif // _NP_NES_DMC_H_ diff --git a/Frameworks/GME/vgmplay/chips/np_nes_fds.c b/Frameworks/GME/vgmplay/chips/np_nes_fds.c new file mode 100644 index 000000000..18c8089dd --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/np_nes_fds.c @@ -0,0 +1,547 @@ +// Ported from NSFPlay 2.3 to VGMPlay (including C++ -> C conversion) +// by Valley Bell on 26 September 2013 + +#include // for rand() +#include // for memset() +#include // for NULL +#include // for exp() +#include "mamedef.h" +#include "../stdbool.h" +#include "np_nes_fds.h" + + +#define DEFAULT_CLOCK 1789772.0 +#define DEFAULT_RATE 44100 + + +enum +{ + OPT_CUTOFF=0, + OPT_4085_RESET, + OPT_WRITE_PROTECT, + OPT_END +}; + +enum +{ + EMOD=0, + EVOL=1 +}; + +//const int RC_BITS = 12; +#define RC_BITS 12 + +enum +{ + TMOD=0, + TWAV=1 +}; + + +// 8 bit approximation of master volume +#define MASTER_VOL (2.4 * 1223.0) // max FDS vol vs max APU square (arbitrarily 1223) +#define MAX_OUT (32.0 * 63.0) // value that should map to master vol +static const INT32 MASTER[4] = { + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; + + +// Although they were pretty much removed from any sound core in NSFPlay 2.3, +// I find this counter structure very useful. +#define COUNTER_SHIFT 24 + +typedef struct _Counter Counter; +struct _Counter +{ + double ratio; + UINT32 val, step; +}; +#define COUNTER_setcycle(cntr, s) (cntr).step = (UINT32)((cntr).ratio / (s + 1)) +#define COUNTER_iup(cntr) (cntr).val += (cntr).step +#define COUNTER_value(cntr) ((cntr).val >> COUNTER_SHIFT) +#define COUNTER_init(cntr, clk, rate) \ +{ \ + (cntr).ratio = (1 << COUNTER_SHIFT) * (1.0 * clk / rate); \ + (cntr).step = (UINT32)((cntr).ratio + 0.5); \ + (cntr).val = 0; \ +} + + +typedef struct _NES_FDS NES_FDS; +struct _NES_FDS +{ + double rate, clock; + int mask; + INT32 sm[2]; // stereo mix + INT32 fout; // current output + int option[OPT_END]; + + bool master_io; + UINT8 master_vol; + UINT32 last_freq; // for trackinfo + UINT32 last_vol; // for trackinfo + + // two wavetables + //const enum { TMOD=0, TWAV=1 }; + INT32 wave[2][64]; + UINT32 freq[2]; + UINT32 phase[2]; + bool wav_write; + bool wav_halt; + bool env_halt; + bool mod_halt; + UINT32 mod_pos; + UINT32 mod_write_pos; + + // two ramp envelopes + //const enum { EMOD=0, EVOL=1 }; + bool env_mode[2]; + bool env_disable[2]; + UINT32 env_timer[2]; + UINT32 env_speed[2]; + UINT32 env_out[2]; + UINT32 master_env_speed; + + // 1-pole RC lowpass filter + INT32 rc_accum; + INT32 rc_k; + INT32 rc_l; + + Counter tick_count; + UINT32 tick_last; +}; + +void* NES_FDS_Create(int clock, int rate) +{ + NES_FDS* fds; + + fds = (NES_FDS*)malloc(sizeof(NES_FDS)); + if (fds == NULL) + return NULL; + memset(fds, 0x00, sizeof(NES_FDS)); + + fds->option[OPT_CUTOFF] = 2000; + fds->option[OPT_4085_RESET] = 0; + fds->option[OPT_WRITE_PROTECT] = 0; // not used here, see nsfplay.cpp + + fds->rc_k = 0; + fds->rc_l = (1<sm[0] = 128; + fds->sm[1] = 128; + + NES_FDS_Reset(fds); + + return fds; +} + +void NES_FDS_Destroy(void* chip) +{ + free(chip); +} + +void NES_FDS_SetMask(void* chip, int m) +{ + NES_FDS* fds = (NES_FDS*)chip; + + fds->mask = m&1; +} + +void NES_FDS_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr) +{ + NES_FDS* fds = (NES_FDS*)chip; + + if (trk < 0) return; + if (trk > 1) return; + fds->sm[0] = mixl; + fds->sm[1] = mixr; +} + +void NES_FDS_SetClock(void* chip, double c) +{ + NES_FDS* fds = (NES_FDS*)chip; + + fds->clock = c; +} + +void NES_FDS_SetRate(void* chip, double r) +{ + NES_FDS* fds = (NES_FDS*)chip; + double cutoff, leak; + + fds->rate = r; + + COUNTER_init(fds->tick_count, fds->clock, fds->rate); + fds->tick_last = 0; + + // configure lowpass filter + cutoff = (double)fds->option[OPT_CUTOFF]; + leak = 0.0; + if (cutoff > 0) + leak = exp(-2.0 * 3.14159 * cutoff / fds->rate); + fds->rc_k = (INT32)(leak * (double)(1<rc_l = (1<rc_k; +} + +void NES_FDS_SetOption(void* chip, int id, int val) +{ + NES_FDS* fds = (NES_FDS*)chip; + + if(idoption[id] = val; + + // update cutoff immediately + if (id == OPT_CUTOFF) NES_FDS_SetRate(fds, fds->rate); +} + +void NES_FDS_Reset(void* chip) +{ + NES_FDS* fds = (NES_FDS*)chip; + int i; + + fds->master_io = true; + fds->master_vol = 0; + fds->last_freq = 0; + fds->last_vol = 0; + + fds->rc_accum = 0; + + for (i=0; i<2; ++i) + { + memset(fds->wave[i], 0, sizeof(fds->wave[i])); + fds->freq[i] = 0; + fds->phase[i] = 0; + } + fds->wav_write = false; + fds->wav_halt = true; + fds->env_halt = true; + fds->mod_halt = true; + fds->mod_pos = 0; + fds->mod_write_pos = 0; + + for (i=0; i<2; ++i) + { + fds->env_mode[i] = false; + fds->env_disable[i] = true; + fds->env_timer[i] = 0; + fds->env_speed[i] = 0; + fds->env_out[i] = 0; + } + fds->master_env_speed = 0xFF; + + // NOTE: the FDS BIOS reset only does the following related to audio: + // $4023 = $00 + // $4023 = $83 enables master_io + // $4080 = $80 output volume = 0, envelope disabled + // $408A = $FF master envelope speed set to slowest + NES_FDS_Write(fds, 0x4023, 0x00); + NES_FDS_Write(fds, 0x4023, 0x83); + NES_FDS_Write(fds, 0x4080, 0x80); + NES_FDS_Write(fds, 0x408A, 0xFF); + + // reset other stuff + NES_FDS_Write(fds, 0x4082, 0x00); // wav freq 0 + NES_FDS_Write(fds, 0x4083, 0x80); // wav disable + NES_FDS_Write(fds, 0x4084, 0x80); // mod strength 0 + NES_FDS_Write(fds, 0x4085, 0x00); // mod position 0 + NES_FDS_Write(fds, 0x4086, 0x00); // mod freq 0 + NES_FDS_Write(fds, 0x4087, 0x80); // mod disable + NES_FDS_Write(fds, 0x4089, 0x00); // wav write disable, max global volume} +} + +static void Tick(NES_FDS* fds, UINT32 clocks) +{ + INT32 vol_out; + + // clock envelopes + if (!fds->env_halt && !fds->wav_halt && (fds->master_env_speed != 0)) + { + int i; + + for (i=0; i<2; ++i) + { + if (!fds->env_disable[i]) + { + UINT32 period; + + fds->env_timer[i] += clocks; + period = ((fds->env_speed[i]+1) * fds->master_env_speed) << 3; + while (fds->env_timer[i] >= period) + { + // clock the envelope + if (fds->env_mode[i]) + { + if (fds->env_out[i] < 32) ++fds->env_out[i]; + } + else + { + if (fds->env_out[i] > 0 ) --fds->env_out[i]; + } + fds->env_timer[i] -= period; + } + } + } + } + + // clock the mod table + if (!fds->mod_halt) + { + UINT32 start_pos, end_pos, p; + + // advance phase, adjust for modulator + start_pos = fds->phase[TMOD] >> 16; + fds->phase[TMOD] += (clocks * fds->freq[TMOD]); + end_pos = fds->phase[TMOD] >> 16; + + // wrap the phase to the 64-step table (+ 16 bit accumulator) + fds->phase[TMOD] = fds->phase[TMOD] & 0x3FFFFF; + + // execute all clocked steps + for (p = start_pos; p < end_pos; ++p) + { + INT32 wv = fds->wave[TMOD][p & 0x3F]; + if (wv == 4) // 4 resets mod position + fds->mod_pos = 0; + else + { + static const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; + fds->mod_pos += BIAS[wv]; + fds->mod_pos &= 0x7F; // 7-bit clamp + } + } + } + + // clock the wav table + if (!fds->wav_halt) + { + INT32 mod, f; + + // complex mod calculation + mod = 0; + if (fds->env_out[EMOD] != 0) // skip if modulator off + { + // convert mod_pos to 7-bit signed + INT32 pos = (fds->mod_pos < 64) ? fds->mod_pos : (fds->mod_pos-128); + + // multiply pos by gain, + // shift off 4 bits but with odd "rounding" behaviour + INT32 temp = pos * fds->env_out[EMOD]; + INT32 rem = temp & 0x0F; + temp >>= 4; + if ((rem > 0) && ((temp & 0x80) == 0)) + { + if (pos < 0) temp -= 1; + else temp += 2; + } + + // wrap if range is exceeded + while (temp >= 192) temp -= 256; + while (temp < -64) temp += 256; + + // multiply result by pitch, + // shift off 6 bits, round to nearest + temp = fds->freq[TWAV] * temp; + rem = temp & 0x3F; + temp >>= 6; + if (rem >= 32) temp += 1; + + mod = temp; + } + + // advance wavetable position + f = fds->freq[TWAV] + mod; + fds->phase[TWAV] = fds->phase[TWAV] + (clocks * f); + fds->phase[TWAV] = fds->phase[TWAV] & 0x3FFFFF; // wrap + + // store for trackinfo + fds->last_freq = f; + } + + // output volume caps at 32 + vol_out = fds->env_out[EVOL]; + if (vol_out > 32) vol_out = 32; + + // final output + if (!fds->wav_write) + fds->fout = fds->wave[TWAV][(fds->phase[TWAV]>>16)&0x3F] * vol_out; + + // NOTE: during wav_halt, the unit still outputs (at phase 0) + // and volume can affect it if the first sample is nonzero. + // haven't worked out 100% of the conditions for volume to + // effect (vol envelope does not seem to run, but am unsure) + // but this implementation is very close to correct + + // store for trackinfo + fds->last_vol = vol_out; +} + +UINT32 NES_FDS_Render(void* chip, INT32 b[2]) +{ + NES_FDS* fds = (NES_FDS*)chip; + +/* // 8 bit approximation of master volume + static const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) + static const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol + static const INT32 MASTER[4] = { + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), + (INT32)((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };*/ + + UINT32 clocks; + INT32 v, rc_out, m; + + COUNTER_iup(fds->tick_count); + clocks = (COUNTER_value(fds->tick_count) - fds->tick_last) & 0xFF; + Tick(fds, clocks); + fds->tick_last = COUNTER_value(fds->tick_count); + + v = fds->fout * MASTER[fds->master_vol] >> 8; + + // lowpass RC filter + rc_out = ((fds->rc_accum * fds->rc_k) + (v * fds->rc_l)) >> RC_BITS; + fds->rc_accum = rc_out; + v = rc_out; + + // output mix + m = fds->mask ? 0 : v; + b[0] = (m * fds->sm[0]) >> (7-2); + b[1] = (m * fds->sm[1]) >> (7-2); + return 2; +} + +bool NES_FDS_Write(void* chip, UINT32 adr, UINT32 val) +{ + NES_FDS* fds = (NES_FDS*)chip; + + // $4023 master I/O enable/disable + if (adr == 0x4023) + { + fds->master_io = ((val & 2) != 0); + return true; + } + + if (!fds->master_io) + return false; + if (adr < 0x4040 || adr > 0x408A) + return false; + + if (adr < 0x4080) // $4040-407F wave table write + { + if (fds->wav_write) + fds->wave[TWAV][adr - 0x4040] = val & 0x3F; + return true; + } + + switch (adr & 0x00FF) + { + case 0x80: // $4080 volume envelope + fds->env_disable[EVOL] = ((val & 0x80) != 0); + fds->env_mode[EVOL] = ((val & 0x40) != 0); + fds->env_timer[EVOL] = 0; + fds->env_speed[EVOL] = val & 0x3F; + if (fds->env_disable[EVOL]) + fds->env_out[EVOL] = fds->env_speed[EVOL]; + return true; + case 0x81: // $4081 --- + return false; + case 0x82: // $4082 wave frequency low + fds->freq[TWAV] = (fds->freq[TWAV] & 0xF00) | val; + return true; + case 0x83: // $4083 wave frequency high / enables + fds->freq[TWAV] = (fds->freq[TWAV] & 0x0FF) | ((val & 0x0F) << 8); + fds->wav_halt = ((val & 0x80) != 0); + fds->env_halt = ((val & 0x40) != 0); + if (fds->wav_halt) + fds->phase[TWAV] = 0; + if (fds->env_halt) + { + fds->env_timer[EMOD] = 0; + fds->env_timer[EVOL] = 0; + } + return true; + case 0x84: // $4084 mod envelope + fds->env_disable[EMOD] = ((val & 0x80) != 0); + fds->env_mode[EMOD] = ((val & 0x40) != 0); + fds->env_timer[EMOD] = 0; + fds->env_speed[EMOD] = val & 0x3F; + if (fds->env_disable[EMOD]) + fds->env_out[EMOD] = fds->env_speed[EMOD]; + return true; + case 0x85: // $4085 mod position + fds->mod_pos = val & 0x7F; + // not hardware accurate., but prevents detune due to cycle inaccuracies + // (notably in Bio Miracle Bokutte Upa) + if (fds->option[OPT_4085_RESET]) + fds->phase[TMOD] = fds->mod_write_pos << 16; + return true; + case 0x86: // $4086 mod frequency low + fds->freq[TMOD] = (fds->freq[TMOD] & 0xF00) | val; + return true; + case 0x87: // $4087 mod frequency high / enable + fds->freq[TMOD] = (fds->freq[TMOD] & 0x0FF) | ((val & 0x0F) << 8); + fds->mod_halt = ((val & 0x80) != 0); + if (fds->mod_halt) + fds->phase[TMOD] = fds->phase[TMOD] & 0x3F0000; // reset accumulator phase + return true; + case 0x88: // $4088 mod table write + if (fds->mod_halt) + { + // writes to current playback position (there is no direct way to set phase) + fds->wave[TMOD][(fds->phase[TMOD] >> 16) & 0x3F] = val & 0x7F; + fds->phase[TMOD] = (fds->phase[TMOD] + 0x010000) & 0x3FFFFF; + fds->wave[TMOD][(fds->phase[TMOD] >> 16) & 0x3F] = val & 0x7F; + fds->phase[TMOD] = (fds->phase[TMOD] + 0x010000) & 0x3FFFFF; + fds->mod_write_pos = fds->phase[TMOD] >> 16; // used by OPT_4085_RESET + } + return true; + case 0x89: // $4089 wave write enable, master volume + fds->wav_write = ((val & 0x80) != 0); + fds->master_vol = val & 0x03; + return true; + case 0x8A: // $408A envelope speed + fds->master_env_speed = val; + // haven't tested whether this register resets phase on hardware, + // but this ensures my inplementation won't spam envelope clocks + // if this value suddenly goes low. + fds->env_timer[EMOD] = 0; + fds->env_timer[EVOL] = 0; + return true; + default: + return false; + } + return false; +} + +bool NES_FDS_Read(void* chip, UINT32 adr, UINT32* val) +{ + NES_FDS* fds = (NES_FDS*)chip; + + if (adr >= 0x4040 && adr < 0x407F) + { + // TODO: if wav_write is not enabled, the + // read address may not be reliable? need + // to test this on hardware. + *val = fds->wave[TWAV][adr - 0x4040]; + return true; + } + + if (adr == 0x4090) // $4090 read volume envelope + { + *val = fds->env_out[EVOL] | 0x40; + return true; + } + + if (adr == 0x4092) // $4092 read mod envelope + { + *val = fds->env_out[EMOD] | 0x40; + return true; + } + + return false; +} diff --git a/Frameworks/GME/vgmplay/chips/np_nes_fds.h b/Frameworks/GME/vgmplay/chips/np_nes_fds.h new file mode 100644 index 000000000..34906db9d --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/np_nes_fds.h @@ -0,0 +1,16 @@ +#ifndef _NP_NES_FDS_H_ +#define _NP_NES_FDS_H_ + +void* NES_FDS_Create(int clock, int rate); +void NES_FDS_Destroy(void* chip); +void NES_FDS_Reset(void* chip); +UINT32 NES_FDS_Render(void* chip, INT32 b[2]); +bool NES_FDS_Write(void* chip, UINT32 adr, UINT32 val); +bool NES_FDS_Read(void* chip, UINT32 adr, UINT32* val); +void NES_FDS_SetRate(void* chip, double r); +void NES_FDS_SetClock(void* chip, double c); +void NES_FDS_SetOption(void* chip, int id, int val); +void NES_FDS_SetMask(void* chip, int m); +void NES_FDS_SetStereoMix(void* chip, int trk, INT16 mixl, INT16 mixr); + +#endif // _NP_NES_FDS_H_ diff --git a/Frameworks/GME/gme/okim6258.c b/Frameworks/GME/vgmplay/chips/okim6258.c similarity index 67% rename from Frameworks/GME/gme/okim6258.c rename to Frameworks/GME/vgmplay/chips/okim6258.c index 63d3e4312..c6a27a4d8 100644 --- a/Frameworks/GME/gme/okim6258.c +++ b/Frameworks/GME/vgmplay/chips/okim6258.c @@ -15,10 +15,12 @@ #include #endif //#include "streams.h" -#include #include +#include #include "okim6258.h" +#define NULL ((void *)0) + #define COMMAND_STOP (1 << 0) #define COMMAND_PLAY (1 << 1) #define COMMAND_RECORD (1 << 2) @@ -28,6 +30,8 @@ static const int dividers[4] = { 1024, 768, 512, 512 }; +#define QUEUE_SIZE (1 << 1) +#define QUEUE_MASK (QUEUE_SIZE - 1) typedef struct _okim6258_state okim6258_state; struct _okim6258_state { @@ -41,9 +45,11 @@ struct _okim6258_state //sound_stream *stream; /* which stream are we playing on? */ UINT8 output_bits; + INT32 output_mask; // Valley Bell: Added a small queue to prevent race conditions. - UINT8 data_buf[2]; + UINT8 data_buf[4]; + UINT8 data_in_last; UINT8 data_buf_pos; // Data Empty Values: // 00 - data written, but not read yet @@ -53,13 +59,19 @@ struct _okim6258_state // Valley Bell: Added pan UINT8 pan; INT32 last_smpl; - + INT32 signal; INT32 step; UINT8 clock_buffer[0x04]; UINT32 initial_clock; UINT8 initial_div; + + SRATE_CALLBACK SmpRateFunc; + void* SmpRateData; + + UINT8 Iternal10Bit; + UINT8 DCRemoval; }; /* step size index shift table */ @@ -71,6 +83,7 @@ static int diff_lookup[49*16]; /* tables computed? */ static int tables_computed = 0; + /*INLINE okim6258_state *get_safe_token(running_device *device) { assert(device != NULL); @@ -123,8 +136,8 @@ static void compute_tables(void) static INT16 clock_adpcm(okim6258_state *chip, UINT8 nibble) { - INT32 max = (1 << (chip->output_bits - 1)) - 1; - INT32 min = -(1 << (chip->output_bits - 1)); + INT32 max = chip->output_mask - 1; + INT32 min = -chip->output_mask; chip->signal += diff_lookup[chip->step * 16 + (nibble & 15)]; @@ -152,19 +165,19 @@ static INT16 clock_adpcm(okim6258_state *chip, UINT8 nibble) ***********************************************************************************************/ //static STREAM_UPDATE( okim6258_update ) -void okim6258_update(void *_chip, stream_sample_t **outputs, int samples) +void okim6258_update(void *param, stream_sample_t **outputs, int samples) { - okim6258_state *chip = (okim6258_state *)_chip; + okim6258_state *chip = (okim6258_state *)param; //stream_sample_t *buffer = outputs[0]; stream_sample_t *bufL = outputs[0]; stream_sample_t *bufR = outputs[1]; - + //memset(outputs[0], 0, samples * sizeof(*outputs[0])); - + if (chip->status & STATUS_PLAYING) { int nibble_shift = chip->nibble_shift; - + while (samples) { /* Compute the new amplitude and update the current step */ @@ -178,19 +191,20 @@ void okim6258_update(void *_chip, stream_sample_t **outputs, int samples) if (! chip->data_empty) { chip->data_in = chip->data_buf[chip->data_buf_pos >> 4]; - chip->data_buf_pos ^= 0x10; + chip->data_buf_pos += 0x10; + chip->data_buf_pos &= 0x3F; if ((chip->data_buf_pos >> 4) == (chip->data_buf_pos & 0x0F)) chip->data_empty ++; } else { - chip->data_in = 0x80; + //chip->data_in = chip->data_in_last; if (chip->data_empty < 0x80) chip->data_empty ++; } } nibble = (chip->data_in >> nibble_shift) & 0xf; - + /* Output to the buffer */ //INT16 sample = clock_adpcm(chip, nibble); if (chip->data_empty < 0x02) @@ -200,26 +214,28 @@ void okim6258_update(void *_chip, stream_sample_t **outputs, int samples) } else { - sample = chip->last_smpl; // Valley Bell: data_empty behaviour (loosely) ported from XM6 - if (chip->data_empty >= 0x12) + if (chip->data_empty >= 0x02 + 0x01) { - chip->data_empty -= 0x10; - if (chip->signal < 0) + chip->data_empty -= 0x01; + /*if (chip->signal < 0) chip->signal ++; else if (chip->signal > 0) - chip->signal --; + chip->signal --;*/ + chip->signal = chip->signal * 15 / 16; + chip->last_smpl = chip->signal << 4; } + sample = chip->last_smpl; } - + nibble_shift ^= 4; - + //*buffer++ = sample; *bufL++ = (chip->pan & 0x02) ? 0x00 : sample; *bufR++ = (chip->pan & 0x01) ? 0x00 : sample; samples--; } - + /* Update the parameters */ chip->nibble_shift = nibble_shift; } @@ -261,29 +277,47 @@ void okim6258_update(void *_chip, stream_sample_t **outputs, int samples) ***********************************************************************************************/ +static int get_vclk(okim6258_state* info) +{ + int clk_rnd; + + clk_rnd = info->master_clock; + clk_rnd += info->divider / 2; // for better rounding - should help some of the streams + return clk_rnd / info->divider; +} + //static DEVICE_START( okim6258 ) -void * device_start_okim6258(int clock, int divider, int adpcm_type, int output_12bits) +int device_start_okim6258(void **_info, int clock, int Options, int divider, int adpcm_type, int output_12bits) { //const okim6258_interface *intf = (const okim6258_interface *)device->baseconfig().static_config(); //okim6258_state *info = get_safe_token(device); okim6258_state *info; info = (okim6258_state *) calloc(1, sizeof(okim6258_state)); + *_info = (void *) info; + + info->Iternal10Bit = (Options >> 0) & 0x01; + info->DCRemoval = (Options >> 1) & 0x01; compute_tables(); //info->master_clock = device->clock(); - info->initial_clock = clock; - info->initial_div = divider; + info->initial_clock = clock; + info->initial_div = divider; info->master_clock = clock; info->adpcm_type = /*intf->*/adpcm_type; info->clock_buffer[0x00] = (clock & 0x000000FF) >> 0; info->clock_buffer[0x01] = (clock & 0x0000FF00) >> 8; info->clock_buffer[0x02] = (clock & 0x00FF0000) >> 16; info->clock_buffer[0x03] = (clock & 0xFF000000) >> 24; + info->SmpRateFunc = NULL; /* D/A precision is 10-bits but 12-bit data can be output serially to an external DAC */ info->output_bits = /*intf->*/output_12bits ? 12 : 10; + if (info->Iternal10Bit) + info->output_mask = (1 << (info->output_bits - 1)); + else + info->output_mask = (1 << (12 - 1)); info->divider = dividers[/*intf->*/divider]; //info->stream = stream_create(device, 0, 1, device->clock()/info->divider, info, okim6258_update); @@ -292,8 +326,8 @@ void * device_start_okim6258(int clock, int divider, int adpcm_type, int output_ info->step = 0; //okim6258_state_save_register(info, device); - - return info; //return info->master_clock / info->divider; + + return get_vclk(info); } @@ -303,29 +337,31 @@ void * device_start_okim6258(int clock, int divider, int adpcm_type, int output_ ***********************************************************************************************/ -void device_stop_okim6258(void *chip) +void device_stop_okim6258(void *info) { - okim6258_state *info = (okim6258_state *) chip; - free(info); + + return; } //static DEVICE_RESET( okim6258 ) -void device_reset_okim6258(void *chip) +void device_reset_okim6258(void *_info) { //okim6258_state *info = get_safe_token(device); - okim6258_state *info = (okim6258_state *) chip; + okim6258_state *info = (okim6258_state *)_info; //stream_update(info->stream); - + info->master_clock = info->initial_clock; info->clock_buffer[0x00] = (info->initial_clock & 0x000000FF) >> 0; info->clock_buffer[0x01] = (info->initial_clock & 0x0000FF00) >> 8; info->clock_buffer[0x02] = (info->initial_clock & 0x00FF0000) >> 16; info->clock_buffer[0x03] = (info->initial_clock & 0xFF000000) >> 24; info->divider = dividers[info->initial_div]; - - + if (info->SmpRateFunc != NULL) + info->SmpRateFunc(info->SmpRateData, get_vclk(info)); + + info->signal = -2; info->step = 0; info->status = 0; @@ -346,12 +382,16 @@ void device_reset_okim6258(void *chip) ***********************************************************************************************/ //void okim6258_set_divider(running_device *device, int val) -void okim6258_set_divider(void *chip, int val) +void okim6258_set_divider(void *_info, int val) { - okim6258_state *info = (okim6258_state *) chip; + //okim6258_state *info = get_safe_token(device); + okim6258_state *info = (okim6258_state *)_info; + int divider = dividers[val]; info->divider = dividers[val]; //stream_set_sample_rate(info->stream, info->master_clock / divider); + if (info->SmpRateFunc != NULL) + info->SmpRateFunc(info->SmpRateData, get_vclk(info)); } @@ -362,9 +402,10 @@ void okim6258_set_divider(void *chip, int val) ***********************************************************************************************/ //void okim6258_set_clock(running_device *device, int val) -void okim6258_set_clock(void *chip, int val) +void okim6258_set_clock(void *_info, int val) { - okim6258_state *info = (okim6258_state *) chip; + //okim6258_state *info = get_safe_token(device); + okim6258_state *info = (okim6258_state *)_info; if (val) { @@ -377,6 +418,9 @@ void okim6258_set_clock(void *chip, int val) (info->clock_buffer[0x02] << 16) | (info->clock_buffer[0x03] << 24); } + //stream_set_sample_rate(info->stream, info->master_clock / info->divider); + if (info->SmpRateFunc != NULL) + info->SmpRateFunc(info->SmpRateData, get_vclk(info)); } @@ -387,11 +431,12 @@ void okim6258_set_clock(void *chip, int val) ***********************************************************************************************/ //int okim6258_get_vclk(running_device *device) -int okim6258_get_vclk(void *chip) +int okim6258_get_vclk(void *_info) { - okim6258_state *info = (okim6258_state *) chip; + //okim6258_state *info = get_safe_token(device); + okim6258_state *info = (okim6258_state *)_info; - return (info->master_clock / info->divider); + return get_vclk(info); } @@ -402,15 +447,15 @@ int okim6258_get_vclk(void *chip) ***********************************************************************************************/ //READ8_DEVICE_HANDLER( okim6258_status_r ) -UINT8 okim6258_status_r(void *chip, offs_t offset) +/*UINT8 okim6258_status_r(UINT8 ChipID, offs_t offset) { //okim6258_state *info = get_safe_token(device); - okim6258_state *info = (okim6258_state *) chip; + okim6258_state *info = &OKIM6258Data[ChipID]; //stream_update(info->stream); return (info->status & STATUS_PLAYING) ? 0x00 : 0x80; -} +}*/ /********************************************************************************************** @@ -419,24 +464,28 @@ UINT8 okim6258_status_r(void *chip, offs_t offset) ***********************************************************************************************/ //WRITE8_DEVICE_HANDLER( okim6258_data_w ) -void okim6258_data_w(void *chip, offs_t offset, UINT8 data) +static void okim6258_data_w(void *_info, /*offs_t offset, */UINT8 data) { //okim6258_state *info = get_safe_token(device); - okim6258_state *info = (okim6258_state *) chip; + okim6258_state *info = (okim6258_state *)_info; /* update the stream */ //stream_update(info->stream); //info->data_in = data; //info->nibble_shift = 0; - + if (info->data_empty >= 0x02) - { info->data_buf_pos = 0x00; - info->data_buf[info->data_buf_pos & 0x0F] = 0x80; - } + info->data_in_last = data; info->data_buf[info->data_buf_pos & 0x0F] = data; - info->data_buf_pos ^= 0x01; + info->data_buf_pos += 0x01; + info->data_buf_pos &= 0xF3; + if ((info->data_buf_pos >> 4) == (info->data_buf_pos & 0x0F)) + { + logerror("Warning: FIFO full!\n"); + info->data_buf_pos = (info->data_buf_pos & 0xF0) | ((info->data_buf_pos-1) & 0x03); + } info->data_empty = 0x00; } @@ -448,10 +497,10 @@ void okim6258_data_w(void *chip, offs_t offset, UINT8 data) ***********************************************************************************************/ //WRITE8_DEVICE_HANDLER( okim6258_ctrl_w ) -void okim6258_ctrl_w(void *chip, offs_t offset, UINT8 data) +static void okim6258_ctrl_w(void *_info, /*offs_t offset, */UINT8 data) { //okim6258_state *info = get_safe_token(device); - okim6258_state *info = (okim6258_state *) chip; + okim6258_state *info = (okim6258_state *)_info; //stream_update(info->stream); @@ -460,30 +509,37 @@ void okim6258_ctrl_w(void *chip, offs_t offset, UINT8 data) info->status &= ~(STATUS_PLAYING | STATUS_RECORDING); return; } - + if (data & COMMAND_PLAY) { - if (!(info->status & STATUS_PLAYING)) + if (!(info->status & STATUS_PLAYING) || info->DCRemoval) { info->status |= STATUS_PLAYING; - + /* Also reset the ADPCM parameters */ - //info->signal = -2; - //info->step = 0; - //info->nibble_shift = 0; + info->signal = -2; + info->step = 0; + info->nibble_shift = 0; + + info->data_buf[0x00] = data; + info->data_buf_pos = 0x01; // write pos 01, read pos 00 + info->data_empty = 0x00; } + // Resetting the ADPCM sample always seems to reduce the clicks and improves the waveform. + // For games that don't use the Multichannel ADPCM driver (whose waveform looks horrible anyway), + // this causes many additional (and loud) clicks though. //info->signal = -2; - info->step = 0; + info->step = 0; // this was verified with the source of XM6 info->nibble_shift = 0; } else { info->status &= ~STATUS_PLAYING; } - + if (data & COMMAND_RECORD) { - /*logerror("M6258: Record enabled\n");*/ + logerror("M6258: Record enabled\n"); info->status |= STATUS_RECORDING; } else @@ -492,49 +548,93 @@ void okim6258_ctrl_w(void *chip, offs_t offset, UINT8 data) } } -void okim6258_set_clock_byte(void *chip, UINT8 Byte, UINT8 val) +static void okim6258_set_clock_byte(void *_info, UINT8 Byte, UINT8 val) { - okim6258_state *info = (okim6258_state *) chip; + okim6258_state *info = (okim6258_state *)_info; info->clock_buffer[Byte] = val; return; } -static void okim6258_pan_w(void *chip, UINT8 data) +static void okim6258_pan_w(void *_info, UINT8 data) { - okim6258_state *info = (okim6258_state *) chip; - + okim6258_state *info = (okim6258_state *)_info; + info->pan = data; return; } -void okim6258_write(void *chip, UINT8 Port, UINT8 Data) +void okim6258_write(void *info, UINT8 Port, UINT8 Data) { switch(Port) { case 0x00: - okim6258_ctrl_w(chip, 0x00, Data); + okim6258_ctrl_w(info, /*0x00, */Data); break; case 0x01: - okim6258_data_w(chip, 0x00, Data); + okim6258_data_w(info, /*0x00, */Data); + break; + case 0x02: + okim6258_pan_w(info, Data); break; - case 0x02: - okim6258_pan_w(chip, Data); - break; case 0x08: case 0x09: case 0x0A: - okim6258_set_clock_byte(chip, Port & 0x03, Data); + okim6258_set_clock_byte(info, Port & 0x03, Data); break; case 0x0B: - okim6258_set_clock_byte(chip, Port & 0x03, Data); - okim6258_set_clock(chip, 0); + okim6258_set_clock_byte(info, Port & 0x03, Data); + okim6258_set_clock(info, 0); break; case 0x0C: - okim6258_set_divider(chip, Data); + okim6258_set_divider(info, Data); break; } + + return; } + + +void okim6258_set_srchg_cb(void *_info, SRATE_CALLBACK CallbackFunc, void* DataPtr) +{ + okim6258_state *info = (okim6258_state *)_info; + + // set Sample Rate Change Callback routine + info->SmpRateFunc = CallbackFunc; + info->SmpRateData = DataPtr; + + return; +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( okim6258 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(okim6258_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(okim6258); break; + case DEVINFO_FCT_STOP: // nothing // break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(okim6258); break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "OKI6258"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "OKI ADPCM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(OKIM6258, okim6258);*/ diff --git a/Frameworks/GME/gme/okim6258.h b/Frameworks/GME/vgmplay/chips/okim6258.h similarity index 52% rename from Frameworks/GME/gme/okim6258.h rename to Frameworks/GME/vgmplay/chips/okim6258.h index 1383a2b70..154ef4a23 100644 --- a/Frameworks/GME/gme/okim6258.h +++ b/Frameworks/GME/vgmplay/chips/okim6258.h @@ -1,7 +1,5 @@ #pragma once -#include "mamedef.h" - //#include "devlegcy.h" /* an interface for the OKIM6258 and similar chips */ @@ -14,9 +12,6 @@ struct _okim6258_interface int output_12bits; };*/ -#ifdef __cplusplus -extern "C" { -#endif #define FOSC_DIV_BY_1024 0 #define FOSC_DIV_BY_768 1 @@ -28,30 +23,28 @@ extern "C" { #define OUTPUT_10BITS 0 #define OUTPUT_12BITS 1 -void okim6258_update(void *, stream_sample_t **outputs, int samples); -void * device_start_okim6258(int clock, int divider, int adpcm_type, int output_12bits); -void device_stop_okim6258(void *); -void device_reset_okim6258(void *); +void okim6258_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_okim6258(void **chip, int clock, int options, int divider, int adpcm_type, int output_12bits); +void device_stop_okim6258(void *chip); +void device_reset_okim6258(void *chip); //void okim6258_set_divider(running_device *device, int val); //void okim6258_set_clock(running_device *device, int val); //int okim6258_get_vclk(running_device *device); -void okim6258_set_divider(void *, int val); -void okim6258_set_clock(void *, int val); -int okim6258_get_vclk(void *); +void okim6258_set_divider(void *chip, int val); +void okim6258_set_clock(void *chip, int val); +int okim6258_get_vclk(void *chip); //READ8_DEVICE_HANDLER( okim6258_status_r ); //WRITE8_DEVICE_HANDLER( okim6258_data_w ); //WRITE8_DEVICE_HANDLER( okim6258_ctrl_w ); -UINT8 okim6258_status_r(void *, offs_t offset); -void okim6258_data_w(void *, offs_t offset, UINT8 data); -void okim6258_ctrl_w(void *, offs_t offset, UINT8 data); -void okim6258_write(void *, UINT8 Port, UINT8 Data); +/*UINT8 okim6258_status_r(UINT8 ChipID, offs_t offset); +void okim6258_data_w(UINT8 ChipID, offs_t offset, UINT8 data); +void okim6258_ctrl_w(UINT8 ChipID, offs_t offset, UINT8 data);*/ +void okim6258_write(void *chip, UINT8 Port, UINT8 Data); + +void okim6258_set_srchg_cb(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr); //DECLARE_LEGACY_SOUND_DEVICE(OKIM6258, okim6258); - -#ifdef __cplusplus -} -#endif diff --git a/Frameworks/GME/gme/okim6295.c b/Frameworks/GME/vgmplay/chips/okim6295.c similarity index 77% rename from Frameworks/GME/gme/okim6295.c rename to Frameworks/GME/vgmplay/chips/okim6295.c index 2157380dd..60a77c309 100644 --- a/Frameworks/GME/gme/okim6295.c +++ b/Frameworks/GME/vgmplay/chips/okim6295.c @@ -27,7 +27,6 @@ #include #include #include -#define _USE_MATH_DEFINES #include #include "okim6295.h" @@ -58,15 +57,21 @@ struct _okim6295_state #define OKIM6295_VOICES 4 struct ADPCMVoice voice[OKIM6295_VOICES]; //running_device *device; - INT32 command; - UINT8 bank_installed; + INT16 command; + //UINT8 bank_installed; INT32 bank_offs; UINT8 pin7_state; + UINT8 nmk_mode; + UINT8 nmk_bank[4]; //sound_stream *stream; /* which stream are we playing on? */ UINT32 master_clock; /* master clock frequency */ + UINT32 initial_clock; - UINT32 ROMSize; - UINT8* ROM; + UINT32 ROMSize; + UINT8* ROM; + + SRATE_CALLBACK SmpRateFunc; + void* SmpRateData; }; /* step size index shift table */ @@ -216,13 +221,46 @@ INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble) ***********************************************************************************************/ -static UINT8 memory_raw_read_byte(okim6295_state *info, offs_t offset) +#define NMK_BNKTBLBITS 8 +#define NMK_BNKTBLSIZE (1 << NMK_BNKTBLBITS) // 0x100 +#define NMK_TABLESIZE (4 * NMK_BNKTBLSIZE) // 0x400 +#define NMK_TABLEMASK (NMK_TABLESIZE - 1) // 0x3FF + +#define NMK_BANKBITS 16 +#define NMK_BANKSIZE (1 << NMK_BANKBITS) // 0x10000 +#define NMK_BANKMASK (NMK_BANKSIZE - 1) // 0xFFFF +#define NMK_ROMBASE (4 * NMK_BANKSIZE) // 0x40000 + +static UINT8 memory_raw_read_byte(okim6295_state *chip, offs_t offset) { offs_t CurOfs; - CurOfs = info->bank_offs | offset; - if (CurOfs < info->ROMSize) - return info->ROM[CurOfs]; + if (! chip->nmk_mode) + { + CurOfs = chip->bank_offs | offset; + } + else + { + UINT8 BankID; + + if (offset < NMK_TABLESIZE && (chip->nmk_mode & 0x80)) + { + // pages sample table + BankID = offset >> NMK_BNKTBLBITS; + CurOfs = offset & NMK_TABLEMASK; // 0x3FF, not 0xFF + } + else + { + BankID = offset >> NMK_BANKBITS; + CurOfs = offset & NMK_BANKMASK; + } + CurOfs |= (chip->nmk_bank[BankID & 0x03] << NMK_BANKBITS); + // I modified MAME to write a clean sample ROM. + // (Usually it moves the data by NMK_ROMBASE.) + //CurOfs += NMK_ROMBASE; + } + if (CurOfs < chip->ROMSize) + return chip->ROM[CurOfs]; else return 0x00; } @@ -293,9 +331,9 @@ static void generate_adpcm(okim6295_state *chip, struct ADPCMVoice *voice, INT16 ***********************************************************************************************/ //static STREAM_UPDATE( okim6295_update ) -void okim6295_update(void *_chip, stream_sample_t **outputs, int samples) +void okim6295_update(void *param, stream_sample_t **outputs, int samples) { - okim6295_state *chip = (okim6295_state *)_chip; + okim6295_state *chip = (okim6295_state *)param; int i; memset(outputs[0], 0, samples * sizeof(*outputs[0])); @@ -374,7 +412,7 @@ static void okim6295_state_save_register(okim6295_state *info, running_device *d ***********************************************************************************************/ //static DEVICE_START( okim6295 ) -void * device_start_okim6295(int clock) +int device_start_okim6295(void **_info, int clock) { //const okim6295_interface *intf = (const okim6295_interface *)device->baseconfig().static_config; //okim6295_state *info = get_safe_token(device); @@ -384,17 +422,22 @@ void * device_start_okim6295(int clock) //int voice; info = (okim6295_state *) calloc(1, sizeof(okim6295_state)); + *_info = (void *) info; compute_tables(); info->command = -1; - info->bank_installed = FALSE; + //info->bank_installed = FALSE; info->bank_offs = 0; + info->nmk_mode = 0x00; + memset(info->nmk_bank, 0x00, 4 * sizeof(UINT8)); //info->device = device; //info->master_clock = device->clock; + info->initial_clock = clock; info->master_clock = clock & 0x7FFFFFFF; info->pin7_state = (clock & 0x80000000) >> 31; + info->SmpRateFunc = NULL; /* generate the name and create the stream */ divisor = info->pin7_state ? 132 : 165; @@ -410,20 +453,22 @@ void * device_start_okim6295(int clock) }*/ //okim6295_state_save_register(info, device); - - return info; //return info->master_clock / divisor; + + return info->master_clock / divisor; } -void device_stop_okim6295(void *_chip) +void device_stop_okim6295(void *_info) { - okim6295_state* chip = (okim6295_state *) _chip; + okim6295_state* chip = (okim6295_state *)_info; free(chip->ROM); chip->ROM = NULL; chip->ROMSize = 0x00; - + free(chip); + + return; } /********************************************************************************************** @@ -433,11 +478,21 @@ void device_stop_okim6295(void *_chip) ***********************************************************************************************/ //static DEVICE_RESET( okim6295 ) -void device_reset_okim6295(void *chip) +void device_reset_okim6295(void *_info) { - okim6295_state *info = (okim6295_state *) chip; + //okim6295_state *info = get_safe_token(device); + okim6295_state *info = (okim6295_state *)_info; int voice; + //stream_update(info->stream); + + info->command = -1; + info->bank_offs = 0; + info->nmk_mode = 0x00; + memset(info->nmk_bank, 0x00, 4 * sizeof(UINT8)); + info->master_clock = info->initial_clock & 0x7FFFFFFF; + info->pin7_state = (info->initial_clock & 0x80000000) >> 31; + for (voice = 0; voice < OKIM6295_VOICES; voice++) { info->voice[voice].volume = 0; @@ -461,20 +516,21 @@ void okim6295_set_bank_base(okim6295_state *info, int base) //okim6295_state *info = get_safe_token(device); //stream_update(info->stream); - /* if we are setting a non-zero base, and we have no bank, allocate one */ - if (!info->bank_installed && base != 0) + // if we are setting a non-zero base, and we have no bank, allocate one + /*if (!info->bank_installed && base != 0) { - /* override our memory map with a bank */ + // override our memory map with a bank //memory_install_read_bank(device->space(), 0x00000, 0x3ffff, 0, 0, device->tag()); info->bank_installed = TRUE; } - /* if we have a bank number, set the base pointer */ + // if we have a bank number, set the base pointer if (info->bank_installed) { info->bank_offs = base; //memory_set_bankptr(device->machine, device->tag(), device->region->base.u8 + base); - } + }*/ + info->bank_offs = base; } @@ -490,16 +546,16 @@ static void okim6295_clock_changed(okim6295_state *info) int divisor; divisor = info->pin7_state ? 132 : 165; //stream_set_sample_rate(info->stream, info->master_clock/divisor); + if (info->SmpRateFunc != NULL) + info->SmpRateFunc(info->SmpRateData, info->master_clock / divisor); } //void okim6295_set_pin7(running_device *device, int pin7) -static void okim6295_set_pin7(okim6295_state *info, int pin7) +INLINE void okim6295_set_pin7(okim6295_state *info, int pin7) { //okim6295_state *info = get_safe_token(device); - //int divisor = pin7 ? 132 : 165; info->pin7_state = pin7; - //stream_set_sample_rate(info->stream, info->master_clock/divisor); okim6295_clock_changed(info); } @@ -511,10 +567,10 @@ static void okim6295_set_pin7(okim6295_state *info, int pin7) ***********************************************************************************************/ //READ8_DEVICE_HANDLER( okim6295_r ) -UINT8 okim6295_r(void *chip, offs_t offset) +UINT8 okim6295_r(void *_info, offs_t offset) { //okim6295_state *info = get_safe_token(device); - okim6295_state *info = (okim6295_state *) chip; + okim6295_state *info = (okim6295_state *)_info; int i, result; result = 0xf0; /* naname expects bits 4-7 to be 1 */ @@ -605,7 +661,7 @@ void okim6295_write_command(okim6295_state *info, UINT8 data) else { //logerror("OKIM6295:'%s' requested to play invalid sample %02x\n",device->tag(),info->command); - /*logerror("OKIM6295: Voice %u requested to play invalid sample %02x\n",i,info->command);*/ + logerror("OKIM6295: Voice %u requested to play invalid sample %02x\n",i,info->command); voice->playing = 0; } } @@ -642,9 +698,9 @@ void okim6295_write_command(okim6295_state *info, UINT8 data) } } -void okim6295_w(void *_chip, offs_t offset, UINT8 data) +void okim6295_w(void *_info, offs_t offset, UINT8 data) { - okim6295_state* chip = (okim6295_state *) _chip; + okim6295_state* chip = (okim6295_state *)_info; switch(offset) { @@ -664,6 +720,9 @@ void okim6295_w(void *_chip, offs_t offset, UINT8 data) chip->master_clock |= data << 16; break; case 0x0B: + if ((data >> 7) != chip->pin7_state) + printf("Pin 7 changed!\n"); + data &= 0x7F; // fix a bug in MAME VGM logs chip->master_clock &= ~0xFF000000; chip->master_clock |= data << 24; okim6295_clock_changed(chip); @@ -671,23 +730,33 @@ void okim6295_w(void *_chip, offs_t offset, UINT8 data) case 0x0C: okim6295_set_pin7(chip, data); break; + case 0x0E: // NMK112 bank switch enable + chip->nmk_mode = data; + break; case 0x0F: okim6295_set_bank_base(chip, data << 18); break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + chip->nmk_bank[offset & 0x03] = data; + break; } return; } -void okim6295_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void okim6295_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { - okim6295_state *chip = (okim6295_state *) _chip; + okim6295_state *chip = (okim6295_state *)_info; if (chip->ROMSize != ROMSize) { chip->ROM = (UINT8*)realloc(chip->ROM, ROMSize); chip->ROMSize = ROMSize; + //printf("OKIM6295: New ROM Size: 0x%05X\n", ROMSize); memset(chip->ROM, 0xFF, ROMSize); } if (DataStart > ROMSize) @@ -696,14 +765,61 @@ void okim6295_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t Da DataLength = ROMSize - DataStart; memcpy(chip->ROM + DataStart, ROMData, DataLength); + + return; } -void okim6295_set_mute_mask(void *_chip, UINT32 MuteMask) +void okim6295_set_mute_mask(void *_info, UINT32 MuteMask) { - okim6295_state *chip = (okim6295_state *) _chip; + okim6295_state *chip = (okim6295_state *)_info; UINT8 CurChn; for (CurChn = 0; CurChn < OKIM6295_VOICES; CurChn ++) chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; } + +void okim6295_set_srchg_cb(void *_info, SRATE_CALLBACK CallbackFunc, void* DataPtr) +{ + okim6295_state *info = (okim6295_state *)_info; + + // set Sample Rate Change Callback routine + info->SmpRateFunc = CallbackFunc; + info->SmpRateData = DataPtr; + + return; +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( okim6295 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(okim6295_state); break; + case DEVINFO_INT_DATABUS_WIDTH_0: info->i = 8; break; + case DEVINFO_INT_ADDRBUS_WIDTH_0: info->i = 18; break; + case DEVINFO_INT_ADDRBUS_SHIFT_0: info->i = 0; break; + + // --- the following bits of info are returned as pointers to data --- // + case DEVINFO_PTR_DEFAULT_MEMORY_MAP_0: info->default_map8 = ADDRESS_MAP_NAME(okim6295);break; + + // --- the following bits of info are returned as pointers to functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( okim6295 ); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( okim6295 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "OKI6295"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "OKI ADPCM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/gme/okim6295.h b/Frameworks/GME/vgmplay/chips/okim6295.h similarity index 67% rename from Frameworks/GME/gme/okim6295.h rename to Frameworks/GME/vgmplay/chips/okim6295.h index 0ca8fc510..a48f88ddf 100644 --- a/Frameworks/GME/gme/okim6295.h +++ b/Frameworks/GME/vgmplay/chips/okim6295.h @@ -1,11 +1,5 @@ #pragma once -#include "mamedef.h" - -#ifdef __cplusplus -extern "C" { -#endif - /* an interface for the OKIM6295 and similar chips */ /* @@ -26,18 +20,19 @@ extern const okim6295_interface okim6295_interface_pin7low;*/ //void okim6295_set_bank_base(running_device *device, int base); //void okim6295_set_pin7(running_device *device, int pin7); -void okim6295_update(void *, stream_sample_t **outputs, int samples); -void * device_start_okim6295(int clock); -void device_stop_okim6295(void *); -void device_reset_okim6295(void *); +void okim6295_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_okim6295(void **chip, int clock); +void device_stop_okim6295(void *chip); +void device_reset_okim6295(void *chip); //READ8_DEVICE_HANDLER( okim6295_r ); //WRITE8_DEVICE_HANDLER( okim6295_w ); -void okim6295_w(void *, offs_t offset, UINT8 data); +void okim6295_w(void *chip, offs_t offset, UINT8 data); -void okim6295_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void okim6295_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); -void okim6295_set_mute_mask(void *, UINT32 MuteMask); +void okim6295_set_mute_mask(void *chip, UINT32 MuteMask); +void okim6295_set_srchg_cb(void *chip, SRATE_CALLBACK CallbackFunc, void* DataPtr); /* @@ -54,7 +49,3 @@ INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble); //DEVICE_GET_INFO( okim6295 ); //#define SOUND_OKIM6295 DEVICE_GET_INFO_NAME( okim6295 ) - -#ifdef __cplusplus -} -#endif diff --git a/Frameworks/GME/vgmplay/chips/opl.c b/Frameworks/GME/vgmplay/chips/opl.c new file mode 100644 index 000000000..a053feb84 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/opl.c @@ -0,0 +1,2027 @@ +// IMPORTANT: This file is not meant to be compiled. It's included in adlibemu_opl?.c. + +/* + * Copyright (C) 2002-2010 The DOSBox Team + * OPL2/OPL3 emulation library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + + +#include +#include // rand() +#include // for memset() +//#include "dosbox.h" +#include "../stdbool.h" +#include "opl.h" + + +//static fltype recipsamp; // inverse of sampling rate // moved to OPL_DATA +static Bit16s wavtable[WAVEPREC*3]; // wave form table + +// vibrato/tremolo tables +static Bit32s vib_table[VIBTAB_SIZE]; +static Bit32s trem_table[TREMTAB_SIZE*2]; + +static Bit32s vibval_const[BLOCKBUF_SIZE]; +static Bit32s tremval_const[BLOCKBUF_SIZE]; + +// vibrato value tables (used per-operator) +static Bit32s vibval_var1[BLOCKBUF_SIZE]; +static Bit32s vibval_var2[BLOCKBUF_SIZE]; +//static Bit32s vibval_var3[BLOCKBUF_SIZE]; +//static Bit32s vibval_var4[BLOCKBUF_SIZE]; + +// vibrato/trmolo value table pointers +//static Bit32s *vibval1, *vibval2, *vibval3, *vibval4; +//static Bit32s *tremval1, *tremval2, *tremval3, *tremval4; +// moved to adlib_getsample + + +// key scale level lookup table +static const fltype kslmul[4] = { + 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct +}; + +// frequency multiplicator lookup table +static const fltype frqmul_tab[16] = { + 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 +}; +// calculated frequency multiplication values (depend on sampling rate) +//static fltype frqmul[16]; // moved to OPL_DATA + +// key scale levels +static Bit8u kslev[8][16]; + +// map a channel number to the register offset of the modulator (=register base) +static const Bit8u modulatorbase[9] = { + 0,1,2, + 8,9,10, + 16,17,18 +}; + +// map a register base to a modulator operator number or operator number +#if defined(OPLTYPE_IS_OPL3) +static const Bit8u regbase2modop[44] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set + 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set +}; +static const Bit8u regbase2op[44] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set + 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set +}; +#else +static const Bit8u regbase2modop[22] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 +}; +static const Bit8u regbase2op[22] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 +}; +#endif + + +// start of the waveform +static Bit32u waveform[8] = { + WAVEPREC, + WAVEPREC>>1, + WAVEPREC, + (WAVEPREC*3)>>2, + 0, + 0, + (WAVEPREC*5)>>2, + WAVEPREC<<1 +}; + +// length of the waveform as mask +static Bit32u wavemask[8] = { + WAVEPREC-1, + WAVEPREC-1, + (WAVEPREC>>1)-1, + (WAVEPREC>>1)-1, + WAVEPREC-1, + ((WAVEPREC*3)>>2)-1, + WAVEPREC>>1, + WAVEPREC-1 +}; + +// where the first entry resides +static Bit32u wavestart[8] = { + 0, + WAVEPREC>>1, + 0, + WAVEPREC>>2, + 0, + 0, + 0, + WAVEPREC>>3 +}; + +// envelope generator function constants +static fltype attackconst[4] = { + (fltype)(1/2.82624), + (fltype)(1/2.25280), + (fltype)(1/1.88416), + (fltype)(1/1.59744) +}; +static fltype decrelconst[4] = { + (fltype)(1/39.28064), + (fltype)(1/31.41608), + (fltype)(1/26.17344), + (fltype)(1/22.44608) +}; + + +INLINE void operator_advance(OPL_DATA* chip, op_type* op_pt, Bit32s vib) { + op_pt->wfpos = op_pt->tcount; // waveform position + + // advance waveform time + op_pt->tcount += op_pt->tinc; + op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT; + + op_pt->generator_pos += chip->generator_add; +} + +INLINE void operator_advance_drums(OPL_DATA* chip, op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) +{ + Bit32u c1 = op_pt1->tcount/FIXEDPT; + Bit32u c3 = op_pt3->tcount/FIXEDPT; + Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; + + Bit32u noisebit = rand()&1; + + Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); + + //Hihat + Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); + op_pt1->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt1->tcount += op_pt1->tinc; + op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; + op_pt1->generator_pos += chip->generator_add; + + //Snare + inttm = ((1+snare_phase_bit) ^ noisebit)<<8; + op_pt2->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt2->tcount += op_pt2->tinc; + op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; + op_pt2->generator_pos += chip->generator_add; + + //Cymbal + inttm = (1+phasebit)<<8; + op_pt3->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt3->tcount += op_pt3->tinc; + op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; + op_pt3->generator_pos += chip->generator_add; +} + + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +INLINE void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) +{ + if (op_pt->op_state != OF_TYPE_OFF) + { + Bit32u i; + op_pt->lastcval = op_pt->cval; + i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); + + // wform: -16384 to 16383 (0x4000) + // trem : 32768 to 65535 (0x10000) + // step_amp: 0.0 to 1.0 + // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) + + op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); + } +} + + +// no action, operator is off +static void operator_off(op_type* op_pt) { +} + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +static void operator_sustain(op_type* op_pt) +{ + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + Bit32u ct; + for (ct=0; ctcur_env_step++; + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in release mode, if output level reaches zero the operator is turned off +static void operator_release(op_type* op_pt) +{ + Bit32u num_steps_add; + Bit32u ct; + + // ??? boundary? + if (op_pt->amp > 0.00000001) + { + // release phase + op_pt->amp *= op_pt->releasemul; + } + + num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (ct=0; ctcur_env_step++; // sample counter + if ((op_pt->cur_env_step & op_pt->env_step_r)==0) + { + if (op_pt->amp <= 0.00000001) + { + // release phase finished, turn off this operator + op_pt->amp = 0.0; + if (op_pt->op_state == OF_TYPE_REL) + { + op_pt->op_state = OF_TYPE_OFF; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in decay mode, if sustain level is reached the output level is either +// kept (sustain level keep enabled) or the operator is switched into release mode +static void operator_decay(op_type* op_pt) +{ + Bit32u num_steps_add; + Bit32u ct; + + if (op_pt->amp > op_pt->sustain_level) + { + // decay phase + op_pt->amp *= op_pt->decaymul; + } + + num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (ct=0; ctcur_env_step++; + if ((op_pt->cur_env_step & op_pt->env_step_d)==0) + { + if (op_pt->amp <= op_pt->sustain_level) + { + // decay phase finished, sustain level reached + if (op_pt->sus_keep) + { + // keep sustain level (until turned off) + op_pt->op_state = OF_TYPE_SUS; + op_pt->amp = op_pt->sustain_level; + } + else + { + // next: release phase + op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in attack mode, if full output level is reached, +// the operator is switched into decay mode +static void operator_attack(op_type* op_pt) +{ + Bit32u num_steps_add; + Bit32u ct; + + op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; + + num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (ct=0; ctcur_env_step++; // next sample + if ((op_pt->cur_env_step & op_pt->env_step_a)==0) + { // check if next step already reached + if (op_pt->amp > 1.0) + { + // attack phase finished, next: decay + op_pt->op_state = OF_TYPE_DEC; + op_pt->amp = 1.0; + op_pt->step_amp = 1.0; + } + op_pt->step_skip_pos_a <<= 1; + if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1; + if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) + { // check if required to skip next step + op_pt->step_amp = op_pt->amp; + } + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +static void operator_eg_attack_check(op_type* op_pt) +{ + if (((op_pt->cur_env_step + 1) & op_pt->env_step_a)==0) + { + // check if next step already reached + if (op_pt->a0 >= 1.0) + { + // attack phase finished, next: decay + op_pt->op_state = OF_TYPE_DEC; + op_pt->amp = 1.0; + op_pt->step_amp = 1.0; + } + } +} + + +typedef void (*optype_fptr)(op_type*); + +static optype_fptr opfuncs[6] = { + operator_attack, + operator_decay, + operator_release, + operator_sustain, // sustain phase (keeping level) + operator_release, // sustain_nokeep phase (release-style) + operator_off +}; + +static void change_attackrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ + Bits attackrate = chip->adlibreg[ARC_ATTR_DECR+regbase]>>4; + if (attackrate) + { + static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa}; + Bits step_skip; + Bits steps; + Bits step_num; + + fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*chip->recipsamp); + // attack rate coefficients + op_pt->a0 = (fltype)(0.0377*f); + op_pt->a1 = (fltype)(10.73*f+1); + op_pt->a2 = (fltype)(-17.57*f); + op_pt->a3 = (fltype)(7.42*f); + + step_skip = attackrate*4 + op_pt->toff; + steps = step_skip >> 2; + op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; + + step_num = (step_skip<=48)?(4-(step_skip&3)):0; + op_pt->env_step_skip_a = step_skip_mask[step_num]; + +#if defined(OPLTYPE_IS_OPL3) + if (step_skip>=60) +#else + if (step_skip>=62) +#endif + { + op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0 + op_pt->a1 = (fltype)(0.0); + op_pt->a2 = (fltype)(0.0); + op_pt->a3 = (fltype)(0.0); + } + } + else + { + // attack disabled + op_pt->a0 = 0.0; + op_pt->a1 = 1.0; + op_pt->a2 = 0.0; + op_pt->a3 = 0.0; + op_pt->env_step_a = 0; + op_pt->env_step_skip_a = 0; + } +} + +static void change_decayrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ + Bits decayrate = chip->adlibreg[ARC_ATTR_DECR+regbase]&15; + // decaymul should be 1.0 when decayrate==0 + if (decayrate) { + Bits steps; + + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*chip->recipsamp); + op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); + steps = (decayrate*4 + op_pt->toff) >> 2; + op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; + } + else + { + op_pt->decaymul = 1.0; + op_pt->env_step_d = 0; + } +} + +static void change_releaserate(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ + Bits releaserate = chip->adlibreg[ARC_SUSL_RELR+regbase]&15; + // releasemul should be 1.0 when releaserate==0 + if (releaserate) + { + Bits steps; + + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*chip->recipsamp); + op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); + steps = (releaserate*4 + op_pt->toff) >> 2; + op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; + } + else + { + op_pt->releasemul = 1.0; + op_pt->env_step_r = 0; + } +} + +static void change_sustainlevel(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ + Bits sustainlevel = chip->adlibreg[ARC_SUSL_RELR+regbase]>>4; + // sustainlevel should be 0.0 when sustainlevel==15 (max) + if (sustainlevel<15) + { + op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); + } + else + { + op_pt->sustain_level = 0.0; + } +} + +static void change_waveform(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ +#if defined(OPLTYPE_IS_OPL3) + if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22 +#endif + // waveform selection + op_pt->cur_wmask = wavemask[chip->wave_sel[regbase]]; + op_pt->cur_wform = &wavtable[waveform[chip->wave_sel[regbase]]]; + // (might need to be adapted to waveform type here...) +} + +static void change_keepsustain(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ + op_pt->sus_keep = (chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; + if (op_pt->op_state==OF_TYPE_SUS) + { + if (!op_pt->sus_keep) + op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } + else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) + { + if (op_pt->sus_keep) + op_pt->op_state = OF_TYPE_SUS; + } +} + +// enable/disable vibrato/tremolo LFO effects +static void change_vibrato(OPL_DATA* chip, Bitu regbase, op_type* op_pt) +{ + op_pt->vibrato = (chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; + op_pt->tremolo = (chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; +} + +// change amount of self-feedback +static void change_feedback(OPL_DATA* chip, Bitu chanbase, op_type* op_pt) +{ + Bits feedback = chip->adlibreg[ARC_FEEDBACK+chanbase]&14; + if (feedback) + op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); + else + op_pt->mfbi = 0; +} + +static void change_frequency(OPL_DATA* chip, Bitu chanbase, Bitu regbase, op_type* op_pt) +{ + Bit32u frn; + Bit32u oct; + Bit32u note_sel; + fltype vol_in; + + // frequency + frn = ((((Bit32u)chip->adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)chip->adlibreg[ARC_FREQ_NUM+chanbase]; + // block number/octave + oct = ((((Bit32u)chip->adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); + op_pt->freq_high = (Bit32s)((frn>>7)&7); + + // keysplit + note_sel = (chip->adlibreg[8]>>6)&1; + op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); + op_pt->toff += (oct<<1); + + // envelope scaling (KSR) + if (!(chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; + + // 20+a0+b0: + op_pt->tinc = (Bit32u)((((fltype)(frn<frqmul[chip->adlibreg[ARC_TVS_KSR_MUL+regbase]&15])); + // 40+a0+b0: + vol_in = (fltype)((fltype)(chip->adlibreg[ARC_KSL_OUTLEV+regbase]&63) + + kslmul[chip->adlibreg[ARC_KSL_OUTLEV+regbase]>>6]*kslev[oct][frn>>6]); + op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); + + // operator frequency changed, care about features that depend on it + change_attackrate(chip, regbase,op_pt); + change_decayrate(chip, regbase,op_pt); + change_releaserate(chip, regbase,op_pt); +} + +static void enable_operator(OPL_DATA* chip, Bitu regbase, op_type* op_pt, Bit32u act_type) +{ + // check if this is really an off-on transition + if (op_pt->act_state == OP_ACT_OFF) + { + Bits wselbase = regbase; + if (wselbase>=ARC_SECONDSET) + wselbase -= (ARC_SECONDSET-22); // second set starts at 22 + + op_pt->tcount = wavestart[chip->wave_sel[wselbase]]*FIXEDPT; + + // start with attack mode + op_pt->op_state = OF_TYPE_ATT; + op_pt->act_state |= act_type; + } +} + +static void disable_operator(op_type* op_pt, Bit32u act_type) +{ + // check if this is really an on-off transition + if (op_pt->act_state != OP_ACT_OFF) + { + op_pt->act_state &= (~act_type); + if (op_pt->act_state == OP_ACT_OFF) + { + if (op_pt->op_state != OF_TYPE_OFF) + op_pt->op_state = OF_TYPE_REL; + } + } +} + +//void adlib_init(Bit32u samplerate) +void* ADLIBEMU(init)(UINT32 clock, UINT32 samplerate, + ADL_UPDATEHANDLER UpdateHandler, void* param) +{ + OPL_DATA* OPL; + //op_type* op; + + Bits i, j, oct; + //Bit32s trem_table_int[TREMTAB_SIZE]; + static Bitu initfirstime = 0; + + OPL = (OPL_DATA*)malloc(sizeof(OPL_DATA)); + OPL->chip_clock = clock; + OPL->int_samplerate = samplerate; + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; + + OPL->generator_add = (Bit32u)(INTFREQU*FIXEDPT/OPL->int_samplerate); + + + /*memset(OPL->adlibreg,0,sizeof(OPL->adlibreg)); + memset(OPL->op,0,sizeof(op_type)*MAXOPERATORS); + memset(OPL->wave_sel,0,sizeof(OPL->wave_sel)); + + for (i=0;iop[i]; + + op->op_state = OF_TYPE_OFF; + op->act_state = OP_ACT_OFF; + op->amp = 0.0; + op->step_amp = 0.0; + op->vol = 0.0; + op->tcount = 0; + op->tinc = 0; + op->toff = 0; + op->cur_wmask = wavemask[0]; + op->cur_wform = &wavtable[waveform[0]]; + op->freq_high = 0; + + op->generator_pos = 0; + op->cur_env_step = 0; + op->env_step_a = 0; + op->env_step_d = 0; + op->env_step_r = 0; + op->step_skip_pos_a = 0; + op->env_step_skip_a = 0; + +#if defined(OPLTYPE_IS_OPL3) + op->is_4op = false; + op->is_4op_attached = false; + op->left_pan = 1; + op->right_pan = 1; +#endif + }*/ + + OPL->recipsamp = 1.0 / (fltype)OPL->int_samplerate; + for (i=15;i>=0;i--) + { + OPL->frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*OPL->recipsamp); + } + + //OPL->status = 0; + //OPL->opl_index = 0; + + + if (!initfirstime) + { + // create vibrato table + vib_table[0] = 8; + vib_table[1] = 4; + vib_table[2] = 0; + vib_table[3] = -4; + for (i=4; ivibtab_add = (Bit32u)(VIBTAB_SIZE*FIXEDPT_LFO/8192*INTFREQU/OPL->int_samplerate); + OPL->vibtab_pos = 0; + + if (!initfirstime) + { + Bit32s trem_table_int[TREMTAB_SIZE]; + + for (i=0; i -0.5/6 to 0) + for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6) + for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6) + + for (i=0; itremtab_add = (Bit32u)((fltype)TREMTAB_SIZE * TREM_FREQ * FIXEDPT_LFO / (fltype)OPL->int_samplerate); + OPL->tremtab_pos = 0; + + if (!initfirstime) + { + initfirstime = 1; + + for (i=0; i>1);i++) + { + wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); + wavtable[i] = wavtable[(i<<1) +WAVEPREC]; + // alternative: (zero-less) +/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC)); + wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */ + } + for (i=0;i<(WAVEPREC>>3);i++) + { + wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384; + wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384; + } + + // key scale level table verified ([table in book]*8/3) + kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37; + kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47; + kslev[7][8] = 48; + for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); + for (j=6;j>=0;j--) + { + for (i=0;i<16;i++) + { + oct = (Bits)kslev[j+1][i]-8; + if (oct < 0) oct = 0; + kslev[j][i] = (Bit8u)oct; + } + } + } + + return OPL; +} + +void ADLIBEMU(stop)(void *chip) +{ + free(chip); + + return; +} + +void ADLIBEMU(reset)(void *chip) +{ + OPL_DATA* OPL = (OPL_DATA*)chip; + Bits i; + op_type* op; + + memset(OPL->adlibreg, 0x00, sizeof(OPL->adlibreg)); + memset(OPL->op, 0x00, sizeof(op_type) * MAXOPERATORS); + memset(OPL->wave_sel, 0x00, sizeof(OPL->wave_sel)); + + for (i=0;iop[i]; + + op->op_state = OF_TYPE_OFF; + op->act_state = OP_ACT_OFF; + op->amp = 0.0; + op->step_amp = 0.0; + op->vol = 0.0; + op->tcount = 0; + op->tinc = 0; + op->toff = 0; + op->cur_wmask = wavemask[0]; + op->cur_wform = &wavtable[waveform[0]]; + op->freq_high = 0; + + op->generator_pos = 0; + op->cur_env_step = 0; + op->env_step_a = 0; + op->env_step_d = 0; + op->env_step_r = 0; + op->step_skip_pos_a = 0; + op->env_step_skip_a = 0; + +#if defined(OPLTYPE_IS_OPL3) + op->is_4op = false; + op->is_4op_attached = false; + op->left_pan = 1; + op->right_pan = 1; +#endif + } + + OPL->status = 0; + OPL->opl_index = 0; + OPL->opl_addr = 0; + + return; +} + + + +void ADLIBEMU(writeIO)(void *chip, UINT32 addr, UINT8 val) +{ + OPL_DATA* OPL = (OPL_DATA*)chip; + + if (addr & 1) + adlib_write(OPL, OPL->opl_addr, val); + else +#if defined(OPLTYPE_IS_OPL3) + OPL->opl_addr = val | ((addr & 2) << 7); +#else + OPL->opl_addr = val; +#endif +} + +static void adlib_write(void *chip, Bitu idx, Bit8u val) +{ + OPL_DATA* OPL = (OPL_DATA*)chip; + + Bit32u second_set = idx&0x100; + OPL->adlibreg[idx] = val; + + switch (idx&0xf0) + { + case ARC_CONTROL: + // here we check for the second set registers, too: + switch (idx) + { + case 0x02: // timer1 counter + case 0x03: // timer2 counter + break; + case 0x04: + // IRQ reset, timer mask/start + if (val&0x80) + { + // clear IRQ bits in status register + OPL->status &= ~0x60; + } + else + { + OPL->status = 0; + } + break; +#if defined(OPLTYPE_IS_OPL3) + case 0x04|ARC_SECONDSET: + // 4op enable/disable switches for each possible channel + OPL->op[0].is_4op = (val&1)>0; + OPL->op[3].is_4op_attached = OPL->op[0].is_4op; + OPL->op[1].is_4op = (val&2)>0; + OPL->op[4].is_4op_attached = OPL->op[1].is_4op; + OPL->op[2].is_4op = (val&4)>0; + OPL->op[5].is_4op_attached = OPL->op[2].is_4op; + OPL->op[18].is_4op = (val&8)>0; + OPL->op[21].is_4op_attached = OPL->op[18].is_4op; + OPL->op[19].is_4op = (val&16)>0; + OPL->op[22].is_4op_attached = OPL->op[19].is_4op; + OPL->op[20].is_4op = (val&32)>0; + OPL->op[23].is_4op_attached = OPL->op[20].is_4op; + break; + case 0x05|ARC_SECONDSET: + break; +#endif + case 0x08: + // CSW, note select + break; + default: + break; + } + break; + case ARC_TVS_KSR_MUL: + case ARC_TVS_KSR_MUL+0x10: + { + // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication + int num = idx&7; + Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu regbase = base+second_set; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change tremolo/vibrato and sustain keeping of this operator + op_type* op_ptr = &OPL->op[modop+((num<3) ? 0 : 9)]; + change_keepsustain(chip, regbase,op_ptr); + change_vibrato(chip, regbase,op_ptr); + + // change frequency calculations of this operator as + // key scale rate and frequency multiplicator can be changed +#if defined(OPLTYPE_IS_OPL3) + if ((OPL->adlibreg[0x105]&1) && (OPL->op[modop].is_4op_attached)) + { + // operator uses frequency of channel + change_frequency(chip, chanbase-3,regbase,op_ptr); + } + else + { + change_frequency(chip, chanbase,regbase,op_ptr); + } +#else + change_frequency(chip, chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_KSL_OUTLEV: + case ARC_KSL_OUTLEV+0x10: + { + // key scale level; output rate + int num = idx&7; + Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; + if ((num<6) && (base<22)) + { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change frequency calculations of this operator as + // key scale level and output rate can be changed + op_type* op_ptr = &OPL->op[modop+((num<3) ? 0 : 9)]; +#if defined(OPLTYPE_IS_OPL3) + Bitu regbase = base+second_set; + if ((OPL->adlibreg[0x105]&1) && (OPL->op[modop].is_4op_attached)) + { + // operator uses frequency of channel + change_frequency(chip, chanbase-3,regbase,op_ptr); + } + else + { + change_frequency(chip, chanbase,regbase,op_ptr); + } +#else + change_frequency(chip, chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_ATTR_DECR: + case ARC_ATTR_DECR+0x10: + { + // attack/decay rates + int num = idx&7; + Bitu base = (idx-ARC_ATTR_DECR)&0xff; + if ((num<6) && (base<22)) + { + Bitu regbase = base+second_set; + + // change attack rate and decay rate of this operator + op_type* op_ptr = &OPL->op[regbase2op[second_set?(base+22):base]]; + change_attackrate(chip, regbase,op_ptr); + change_decayrate(chip, regbase,op_ptr); + } + } + break; + case ARC_SUSL_RELR: + case ARC_SUSL_RELR+0x10: + { + // sustain level; release rate + int num = idx&7; + Bitu base = (idx-ARC_SUSL_RELR)&0xff; + if ((num<6) && (base<22)) + { + Bitu regbase = base+second_set; + + // change sustain level and release rate of this operator + op_type* op_ptr = &OPL->op[regbase2op[second_set?(base+22):base]]; + change_releaserate(chip, regbase,op_ptr); + change_sustainlevel(chip, regbase,op_ptr); + } + } + break; + case ARC_FREQ_NUM: + { + // 0xa0-0xa8 low8 frequency + Bitu base = (idx-ARC_FREQ_NUM)&0xff; + if (base<9) + { + Bits opbase = second_set?(base+18):base; + Bits modbase; + Bitu chanbase; +#if defined(OPLTYPE_IS_OPL3) + if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + modbase = modulatorbase[base]+second_set; + + chanbase = base+second_set; + + change_frequency(chip, chanbase,modbase,&OPL->op[opbase]); + change_frequency(chip, chanbase,modbase+3,&OPL->op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((OPL->adlibreg[0x105]&1) && OPL->op[second_set?(base+18):base].is_4op) + { + change_frequency(chip, chanbase,modbase+8,&OPL->op[opbase+3]); + change_frequency(chip, chanbase,modbase+3+8,&OPL->op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_KON_BNUM: + { + Bitu base; + if (OPL->UpdateHandler != NULL) // hack for DOSBox logs + OPL->UpdateHandler(OPL->UpdateParam); + if (idx == ARC_PERC_MODE) + { +#if defined(OPLTYPE_IS_OPL3) + if (second_set) return; +#endif + + if ((val&0x30) == 0x30) + { // BassDrum active + enable_operator(chip, 16,&OPL->op[6],OP_ACT_PERC); + change_frequency(chip, 6,16,&OPL->op[6]); + enable_operator(chip, 16+3,&OPL->op[6+9],OP_ACT_PERC); + change_frequency(chip, 6,16+3,&OPL->op[6+9]); + } + else + { + disable_operator(&OPL->op[6],OP_ACT_PERC); + disable_operator(&OPL->op[6+9],OP_ACT_PERC); + } + if ((val&0x28) == 0x28) + { // Snare active + enable_operator(chip, 17+3,&OPL->op[16],OP_ACT_PERC); + change_frequency(chip, 7,17+3,&OPL->op[16]); + } + else + { + disable_operator(&OPL->op[16],OP_ACT_PERC); + } + if ((val&0x24) == 0x24) + { // TomTom active + enable_operator(chip, 18,&OPL->op[8],OP_ACT_PERC); + change_frequency(chip, 8,18,&OPL->op[8]); + } + else + { + disable_operator(&OPL->op[8],OP_ACT_PERC); + } + if ((val&0x22) == 0x22) + { // Cymbal active + enable_operator(chip, 18+3,&OPL->op[8+9],OP_ACT_PERC); + change_frequency(chip, 8,18+3,&OPL->op[8+9]); + } + else + { + disable_operator(&OPL->op[8+9],OP_ACT_PERC); + } + if ((val&0x21) == 0x21) + { // Hihat active + enable_operator(chip, 17,&OPL->op[7],OP_ACT_PERC); + change_frequency(chip, 7,17,&OPL->op[7]); + } + else + { + disable_operator(&OPL->op[7],OP_ACT_PERC); + } + + break; + } + // regular 0xb0-0xb8 + base = (idx-ARC_KON_BNUM)&0xff; + if (base<9) + { + Bits opbase = second_set?(base+18):base; + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + Bitu chanbase; + +#if defined(OPLTYPE_IS_OPL3) + if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op_attached) break; +#endif + if (val&32) + { + // operator switched on + enable_operator(chip, modbase,&OPL->op[opbase],OP_ACT_NORMAL); // modulator (if 2op) + enable_operator(chip, modbase+3,&OPL->op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op) +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched on + if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op) + { + // turn on chan+3 operators as well + enable_operator(chip, modbase+8,&OPL->op[opbase+3],OP_ACT_NORMAL); + enable_operator(chip, modbase+3+8,&OPL->op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } + else + { + // operator switched off + disable_operator(&OPL->op[opbase],OP_ACT_NORMAL); + disable_operator(&OPL->op[opbase+9],OP_ACT_NORMAL); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched off + if ((OPL->adlibreg[0x105]&1) && OPL->op[opbase].is_4op) + { + // turn off chan+3 operators as well + disable_operator(&OPL->op[opbase+3],OP_ACT_NORMAL); + disable_operator(&OPL->op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } + + chanbase = base+second_set; + + // change frequency calculations of modulator and carrier (2op) as + // the frequency of the channel has changed + change_frequency(chip, chanbase,modbase,&OPL->op[opbase]); + change_frequency(chip, chanbase,modbase+3,&OPL->op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((OPL->adlibreg[0x105]&1) && OPL->op[second_set?(base+18):base].is_4op) + { + // change frequency calculations of chan+3 operators as well + change_frequency(chip, chanbase,modbase+8,&OPL->op[opbase+3]); + change_frequency(chip, chanbase,modbase+3+8,&OPL->op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_FEEDBACK: + { + // 0xc0-0xc8 feedback/modulation type (AM/FM) + Bitu base = (idx-ARC_FEEDBACK)&0xff; + if (base<9) + { + Bits opbase = second_set?(base+18):base; + Bitu chanbase = base+second_set; + change_feedback(chip, chanbase,&OPL->op[opbase]); +#if defined(OPLTYPE_IS_OPL3) + // OPL3 panning + OPL->op[opbase].left_pan = ((val&0x10)>>4); + OPL->op[opbase].right_pan = ((val&0x20)>>5); + OPL->op[opbase].left_pan += ((val&0x40)>>6); + OPL->op[opbase].right_pan += ((val&0x80)>>7); +#endif + } + } + break; + case ARC_WAVE_SEL: + case ARC_WAVE_SEL+0x10: + { + int num = idx&7; + Bitu base = (idx-ARC_WAVE_SEL)&0xff; + if ((num<6) && (base<22)) + { +#if defined(OPLTYPE_IS_OPL3) + Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[] + op_type* op_ptr; + // change waveform + if (OPL->adlibreg[0x105]&1) OPL->wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible + else OPL->wave_sel[wselbase] = val&3; + op_ptr = &OPL->op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; + change_waveform(chip, wselbase,op_ptr); +#else + if (OPL->adlibreg[0x01]&0x20) + { + op_type* op_ptr; + + // wave selection enabled, change waveform + OPL->wave_sel[base] = val&3; + op_ptr = &OPL->op[regbase2modop[base]+((num<3) ? 0 : 9)]; + change_waveform(chip, base,op_ptr); + } +#endif + } + } + break; + default: + break; + } +} + + +UINT32 ADLIBEMU(reg_read)(void *chip, UINT32 port) +{ + OPL_DATA* OPL = (OPL_DATA*)chip; + +#if defined(OPLTYPE_IS_OPL3) + // opl3-detection routines require ret&6 to be zero + if ((port&1)==0) + { + return OPL->status; + } + return 0x00; +#else + // opl2-detection routines require ret&6 to be 6 + if ((port&1)==0) + { + return OPL->status|6; + } + return 0xff; +#endif +} + +void ADLIBEMU(write_index)(void *chip, UINT32 port, UINT8 val) +{ + OPL_DATA* OPL = (OPL_DATA*)chip; + + OPL->opl_index = val; +#if defined(OPLTYPE_IS_OPL3) + if ((port&3)!=0) + { + // possibly second set + if (((OPL->adlibreg[0x105]&1)!=0) || (OPL->opl_index==5)) OPL->opl_index |= ARC_SECONDSET; + } +#endif +} + +/*static void OPL_INLINE clipit16(Bit32s ival, Bit16s* outval) +{ + if (ival<32768) + { + if (ival>-32769) + { + *outval=(Bit16s)ival; + } + else + { + *outval = -32768; + } + } + else + { + *outval = 32767; + } +}*/ + + + +// be careful with this +// uses cptr and chanval, outputs into outbufl(/outbufr) +// for opl3 check if opl3-mode is enabled (which uses stereo panning) +// +// Changes by Valley Bell: +// - Changed to always output to both channels +// - added parameter "chn" to fix panning for 4-op channels and the Rhythm Cymbal +#undef CHANVAL_OUT +#if defined(OPLTYPE_IS_OPL3) +#define CHANVAL_OUT(chn) \ + if (OPL->adlibreg[0x105]&1) { \ + outbufl[i] += chanval*cptr[chn].left_pan; \ + outbufr[i] += chanval*cptr[chn].right_pan; \ + } else { \ + outbufl[i] += chanval; \ + outbufr[i] += chanval; \ + } +#else +#define CHANVAL_OUT(chn) \ + outbufl[i] += chanval; \ + outbufr[i] += chanval; +#endif + +//void adlib_getsample(Bit16s* sndptr, Bits numsamples) +void ADLIBEMU(getsample)(void *chip, INT32** sndptr, INT32 numsamples) +{ + OPL_DATA* OPL = (OPL_DATA*)chip; + + Bits i, endsamples; + op_type* cptr; + + //Bit32s outbufl[BLOCKBUF_SIZE]; +#if defined(OPLTYPE_IS_OPL3) + // second output buffer (right channel for opl3 stereo) + //Bit32s outbufr[BLOCKBUF_SIZE]; +#endif + Bit32s* outbufl = sndptr[0]; + Bit32s* outbufr = sndptr[1]; + + // vibrato/tremolo lookup tables (global, to possibly be used by all operators) + Bit32s vib_lut[BLOCKBUF_SIZE]; + Bit32s trem_lut[BLOCKBUF_SIZE]; + + Bits samples_to_process = numsamples; + + Bits cursmp; + Bit32s vib_tshift; + Bitu max_channel = NUM_CHANNELS; + Bits cur_ch; + + Bit32s *vibval1, *vibval2, *vibval3, *vibval4; + Bit32s *tremval1, *tremval2, *tremval3, *tremval4; + +#if defined(OPLTYPE_IS_OPL3) + if ((OPL->adlibreg[0x105]&1)==0) max_channel = NUM_CHANNELS/2; +#endif + + if (! samples_to_process) + { + for (cur_ch = 0; cur_ch < max_channel; cur_ch ++) + { + if ((OPL->adlibreg[ARC_PERC_MODE] & 0x20) && (cur_ch >= 6 && cur_ch < 9)) + continue; + +#if defined(OPLTYPE_IS_OPL3) + if (cur_ch < 9) + cptr = &OPL->op[cur_ch]; + else + cptr = &OPL->op[cur_ch+9]; // second set is operator18-operator35 + if (cptr->is_4op_attached) + continue; +#else + cptr = &OPL->op[cur_ch]; +#endif + + if (cptr[0].op_state == OF_TYPE_ATT) + operator_eg_attack_check(&cptr[0]); + if (cptr[9].op_state == OF_TYPE_ATT) + operator_eg_attack_check(&cptr[9]); + } + + return; + } + + for (cursmp=0; cursmpBLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; + + memset(outbufl,0,endsamples*sizeof(Bit32s)); +//#if defined(OPLTYPE_IS_OPL3) +// // clear second output buffer (opl3 stereo) +// //if (adlibreg[0x105]&1) + memset(outbufr,0,endsamples*sizeof(Bit32s)); +//#endif + + // calculate vibrato/tremolo lookup tables + vib_tshift = ((OPL->adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching + for (i=0;ivibtab_pos += OPL->vibtab_add; + if (OPL->vibtab_pos/FIXEDPT_LFO>=VIBTAB_SIZE) + OPL->vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; + vib_lut[i] = vib_table[OPL->vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents + + // cycle through tremolo table + OPL->tremtab_pos += OPL->tremtab_add; + if (OPL->tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) + OPL->tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; + if (OPL->adlibreg[ARC_PERC_MODE]&0x80) + trem_lut[i] = trem_table[OPL->tremtab_pos/FIXEDPT_LFO]; + else + trem_lut[i] = trem_table[TREMTAB_SIZE+OPL->tremtab_pos/FIXEDPT_LFO]; + } + + if (OPL->adlibreg[ARC_PERC_MODE]&0x20) + { + if (! (OPL->MuteChn[NUM_CHANNELS + 0])) + { + //BassDrum + cptr = &OPL->op[6]; + if (OPL->adlibreg[ARC_FEEDBACK+6]&1) + { + // additive synthesis + if (cptr[9].op_state != OF_TYPE_OFF) + { + if (cptr[9].vibrato) + { + vibval1 = vibval_var1; + for (i=0;iMuteChn[NUM_CHANNELS + 2]) && OPL->op[8].op_state != OF_TYPE_OFF) + { + cptr = &OPL->op[8]; + if (cptr[0].vibrato) + { + vibval3 = vibval_var1; + for (i=0;iop[7].op_state != OF_TYPE_OFF) || (OPL->op[16].op_state != OF_TYPE_OFF) || + (OPL->op[17].op_state != OF_TYPE_OFF)) + { + cptr = &OPL->op[7]; + if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) + { + vibval1 = vibval_var1; + for (i=0;iop[8]; + if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) + { + vibval4 = vibval_var2; + for (i=0;iop[0]; // set cptr to something useful (else it stays at op[8]) + for (i=0;iop[7],vibval1[i],&OPL->op[7+9],vibval2[i],&OPL->op[8+9],vibval4[i]); + + if (! (OPL->MuteChn[NUM_CHANNELS + 4])) + { + opfuncs[OPL->op[7].op_state](&OPL->op[7]); //Hihat + operator_output(&OPL->op[7],0,tremval1[i]); + } + else + OPL->op[7].cval = 0; + + if (! (OPL->MuteChn[NUM_CHANNELS + 1])) + { + opfuncs[OPL->op[7+9].op_state](&OPL->op[7+9]); //Snare + operator_output(&OPL->op[7+9],0,tremval2[i]); + } + else + OPL->op[7+9].cval = 0; + + if (! (OPL->MuteChn[NUM_CHANNELS + 3])) + { + opfuncs[OPL->op[8+9].op_state](&OPL->op[8+9]); //Cymbal + operator_output(&OPL->op[8+9],0,tremval4[i]); + } + else + OPL->op[8+9].cval = 0; + + //chanval = (OPL->op[7].cval + OPL->op[7+9].cval + OPL->op[8+9].cval)*2; + //CHANVAL_OUT(0) + // fix panning of the snare -Valley Bell + chanval = (OPL->op[7].cval + OPL->op[7+9].cval)*2; + CHANVAL_OUT(7) + chanval = OPL->op[8+9].cval*2; + CHANVAL_OUT(8) + } + } + } + + for (cur_ch=max_channel-1; cur_ch>=0; cur_ch--) + { + Bitu k; + + if (OPL->MuteChn[cur_ch]) + continue; + + // skip drum/percussion operators + if ((OPL->adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; + + k = cur_ch; +#if defined(OPLTYPE_IS_OPL3) + if (cur_ch < 9) + { + cptr = &OPL->op[cur_ch]; + } + else + { + cptr = &OPL->op[cur_ch+9]; // second set is operator18-operator35 + k += (-9+256); // second set uses registers 0x100 onwards + } + // check if this operator is part of a 4-op + //if ((OPL->adlibreg[0x105]&1) && cptr->is_4op_attached) continue; + if (cptr->is_4op_attached) continue; // this is more correct +#else + cptr = &OPL->op[cur_ch]; +#endif + + // check for FM/AM + if (OPL->adlibreg[ARC_FEEDBACK+k]&1) + { +#if defined(OPLTYPE_IS_OPL3) + //if ((OPL->adlibreg[0x105]&1) && cptr->is_4op) + if (cptr->is_4op) // this is more correct + { + if (OPL->adlibreg[ARC_FEEDBACK+k+3]&1) + { + // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) + if (cptr[0].op_state != OF_TYPE_OFF) + { + if (cptr[0].vibrato) + { + vibval1 = vibval_var1; + for (i=0;iadlibreg[0x105]&1) && cptr->is_4op) + if (cptr->is_4op) // this is more correct + { + if (OPL->adlibreg[ARC_FEEDBACK+k+3]&1) + { + // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) + if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) + { + if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) + { + vibval1 = vibval_var1; + for (i=0;iMuteChn[CurChn] = (MuteMask >> CurChn) & 0x01; + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/opl.h b/Frameworks/GME/vgmplay/chips/opl.h new file mode 100644 index 000000000..6dd303345 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/opl.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2002-2010 The DOSBox Team + * OPL2/OPL3 emulation library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + + +#define fltype double + +/* + define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here +*/ +#include +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +/* + define attribution that inlines/forces inlining of a function (optional) +*/ +#define OPL_INLINE INLINE + + +#undef NUM_CHANNELS +#if defined(OPLTYPE_IS_OPL3) +#define NUM_CHANNELS 18 +#else +#define NUM_CHANNELS 9 +#endif + +#define MAXOPERATORS (NUM_CHANNELS*2) + + +#define FL05 ((fltype)0.5) +#define FL2 ((fltype)2.0) +#define PI ((fltype)3.1415926535897932384626433832795) + + +#define FIXEDPT 0x10000 // fixed-point calculations using 16+16 +#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24 + +#define WAVEPREC 1024 // waveform precision (10 bits) + +//#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip +#if defined(OPLTYPE_IS_OPL3) +#define INTFREQU ((fltype)(OPL->chip_clock / 288.0)) // clocking of the chip +#else +#define INTFREQU ((fltype)(OPL->chip_clock / 72.0)) // clocking of the chip +#endif + + +#define OF_TYPE_ATT 0 +#define OF_TYPE_DEC 1 +#define OF_TYPE_REL 2 +#define OF_TYPE_SUS 3 +#define OF_TYPE_SUS_NOKEEP 4 +#define OF_TYPE_OFF 5 + +#define ARC_CONTROL 0x00 +#define ARC_TVS_KSR_MUL 0x20 +#define ARC_KSL_OUTLEV 0x40 +#define ARC_ATTR_DECR 0x60 +#define ARC_SUSL_RELR 0x80 +#define ARC_FREQ_NUM 0xa0 +#define ARC_KON_BNUM 0xb0 +#define ARC_PERC_MODE 0xbd +#define ARC_FEEDBACK 0xc0 +#define ARC_WAVE_SEL 0xe0 + +#define ARC_SECONDSET 0x100 // second operator set for OPL3 + + +#define OP_ACT_OFF 0x00 +#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked) +#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked) + +#define BLOCKBUF_SIZE 512 + + +// vibrato constants +#define VIBTAB_SIZE 8 +#define VIBFAC 70/50000 // no braces, integer mul/div + +// tremolo constants and table +#define TREMTAB_SIZE 53 +#define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz + + +/* operator struct definition + For OPL2 all 9 channels consist of two operators each, carrier and modulator. + Channel x has operators x as modulator and operators (9+x) as carrier. + For OPL3 all 18 channels consist either of two operators (2op mode) or four + operators (4op mode) which is determined through register4 of the second + adlib register set. + Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as + 4op channels. The two additional operators for a channel y come from the + 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op + channel. +*/ +typedef struct operator_struct { + Bit32s cval, lastcval; // current output/last output (used for feedback) + Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment + fltype amp, step_amp; // and amplification (envelope) + fltype vol; // volume + fltype sustain_level; // sustain level + Bit32s mfbi; // feedback amount + fltype a0, a1, a2, a3; // attack rate function coefficients + fltype decaymul, releasemul; // decay/release rate functions + Bit32u op_state; // current state of operator (attack/decay/sustain/release/off) + Bit32u toff; + Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations + Bit16s* cur_wform; // start of selected waveform + Bit32u cur_wmask; // mask for selected waveform + Bit32u act_state; // activity state (regular, percussion) + bool sus_keep; // keep sustain level when decay finished + bool vibrato,tremolo; // vibrato/tremolo enable bits + + // variables used to provide non-continuous envelopes + Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed + Bits cur_env_step; // current (standardized) sample position + Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode) + Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask) + Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then) + +#if defined(OPLTYPE_IS_OPL3) + bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel + Bit32s left_pan,right_pan; // opl3 stereo panning amount +#endif +} op_type; + +typedef struct opl_chip +{ + // per-chip variables + //Bitu chip_num; + op_type op[MAXOPERATORS]; + Bit8u MuteChn[NUM_CHANNELS + 5]; + Bitu chip_clock; + + Bits int_samplerate; + + Bit8u status; + Bit32u opl_index; + Bits opl_addr; +#if defined(OPLTYPE_IS_OPL3) + Bit8u adlibreg[512]; // adlib register set (including second set) + Bit8u wave_sel[44]; // waveform selection +#else + Bit8u adlibreg[256]; // adlib register set + Bit8u wave_sel[22]; // waveform selection +#endif + + + // vibrato/tremolo increment/counter + Bit32u vibtab_pos; + Bit32u vibtab_add; + Bit32u tremtab_pos; + Bit32u tremtab_add; + + Bit32u generator_add; // should be a chip parameter + + fltype recipsamp; // inverse of sampling rate + fltype frqmul[16]; + + ADL_UPDATEHANDLER UpdateHandler; // stream update handler + void* UpdateParam; // stream update parameter +} OPL_DATA; + + +// enable an operator +static void enable_operator(OPL_DATA* chip, Bitu regbase, op_type* op_pt, Bit32u act_type); + +// functions to change parameters of an operator +static void change_frequency(OPL_DATA* chip, Bitu chanbase, Bitu regbase, op_type* op_pt); + +static void change_attackrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_decayrate(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_releaserate(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_sustainlevel(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_waveform(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_keepsustain(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_vibrato(OPL_DATA* chip, Bitu regbase, op_type* op_pt); +static void change_feedback(OPL_DATA* chip, Bitu chanbase, op_type* op_pt); + +// general functions +/*void* adlib_init(Bitu clock, Bit32u samplerate); +void adlib_writeIO(void *chip, Bitu addr, Bit8u val); +void adlib_write(void *chip, Bitu idx, Bit8u val); +//void adlib_getsample(Bit16s* sndptr, Bits numsamples); +void adlib_getsample(void *chip, Bit32s** sndptr, Bits numsamples); + +Bitu adlib_reg_read(void *chip, Bitu port); +void adlib_write_index(void *chip, Bitu port, Bit8u val);*/ +static void adlib_write(void *chip, Bitu idx, Bit8u val); diff --git a/Frameworks/GME/vgmplay/chips/panning.c b/Frameworks/GME/vgmplay/chips/panning.c new file mode 100644 index 000000000..f438cbeef --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/panning.c @@ -0,0 +1,82 @@ +#include +#include +#include "panning.h" + +#ifndef PI +#define PI 3.14159265359 +#endif +#ifndef SQRT2 +#define SQRT2 1.414213562 +#endif +#define RANGE 512 + +//----------------------------------------------------------------- +// Set the panning values for the two stereo channels (L,R) +// for a position -256..0..256 L..C..R +//----------------------------------------------------------------- +void calc_panning(float channels[2], int position) +{ + if ( position > RANGE / 2 ) + position = RANGE / 2; + else if ( position < -RANGE / 2 ) + position = -RANGE / 2; + position += RANGE / 2; // make -256..0..256 -> 0..256..512 + + // Equal power law: equation is + // right = sin( position / range * pi / 2) * sqrt( 2 ) + // left is equivalent to right with position = range - position + // position is in the range 0 .. RANGE + // RANGE / 2 = centre, result = 1.0f + channels[1] = (float)( sin( (double)position / RANGE * PI / 2 ) * SQRT2 ); + position = RANGE - position; + channels[0] = (float)( sin( (double)position / RANGE * PI / 2 ) * SQRT2 ); +} + +//----------------------------------------------------------------- +// Reset the panning values to the centre position +//----------------------------------------------------------------- +void centre_panning(float channels[2]) +{ + channels[0] = channels[1] = 1.0f; +} + +/*//----------------------------------------------------------------- +// Generate a stereo position in the range 0..RANGE +// with Gaussian distribution, mean RANGE/2, S.D. RANGE/5 +//----------------------------------------------------------------- +int random_stereo() +{ + int n = (int)(RANGE/2 + gauss_rand() * (RANGE * 0.2) ); + if ( n > RANGE ) n = RANGE; + if ( n < 0 ) n = 0; + return n; +} + +//----------------------------------------------------------------- +// Generate a Gaussian random number with mean 0, variance 1 +// Copied from an ancient C newsgroup FAQ +//----------------------------------------------------------------- +double gauss_rand() +{ + static double V1, V2, S; + static int phase = 0; + double X; + + if(phase == 0) { + do { + double U1 = (double)rand() / RAND_MAX; + double U2 = (double)rand() / RAND_MAX; + + V1 = 2 * U1 - 1; + V2 = 2 * U2 - 1; + S = V1 * V1 + V2 * V2; + } while(S >= 1 || S == 0); + + X = V1 * sqrt(-2 * log(S) / S); + } else + X = V2 * sqrt(-2 * log(S) / S); + + phase = 1 - phase; + + return X; +}*/ diff --git a/Frameworks/GME/vgmplay/chips/panning.h b/Frameworks/GME/vgmplay/chips/panning.h new file mode 100644 index 000000000..b6bab25ba --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/panning.h @@ -0,0 +1,13 @@ +/* + panning.h by Maxim in 2006 + Implements "simple equal power" panning using sine distribution + I am not an expert on this stuff, but this is the best sounding of the methods I've tried +*/ + +#ifndef PANNING_H +#define PANNING_H + +void calc_panning(float channels[2], int position); +void centre_panning(float channels[2]); + +#endif \ No newline at end of file diff --git a/Frameworks/GME/vgmplay/chips/pokey.c b/Frameworks/GME/vgmplay/chips/pokey.c new file mode 100644 index 000000000..a8b5ef05a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/pokey.c @@ -0,0 +1,1490 @@ +/***************************************************************************** + * + * POKEY chip emulator 4.51 + * Copyright Nicola Salmoria and the MAME Team + * + * Based on original info found in Ron Fries' Pokey emulator, + * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller, + * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual. + * Polynome algorithms according to info supplied by Perry McFarlane. + * + * This code is subject to the MAME license, which besides other + * things means it is distributed as is, no warranties whatsoever. + * For more details read mame.txt that comes with MAME. + * + * 4.51: + * - changed to use the attotime datatype + * 4.5: + * - changed the 9/17 bit polynomial formulas such that the values + * required for the Tempest Pokey protection will be found. + * Tempest expects the upper 4 bits of the RNG to appear in the + * lower 4 bits after four cycles, so there has to be a shift + * of 1 per cycle (which was not the case before). Bits #6-#13 of the + * new RNG give this expected result now, bits #0-7 of the 9 bit poly. + * - reading the RNG returns the shift register contents ^ 0xff. + * That way resetting the Pokey with SKCTL (which resets the + * polynome shifters to 0) returns the expected 0xff value. + * 4.4: + * - reversed sample values to make OFF channels produce a zero signal. + * actually de-reversed them; don't remember that I reversed them ;-/ + * 4.3: + * - for POT inputs returning zero, immediately assert the ALLPOT + * bit after POTGO is written, otherwise start trigger timer + * depending on SK_PADDLE mode, either 1-228 scanlines or 1-2 + * scanlines, depending on the SK_PADDLE bit of SKCTL. + * 4.2: + * - half volume for channels which are inaudible (this should be + * close to the real thing). + * 4.1: + * - default gain increased to closely match the old code. + * - random numbers repeat rate depends on POLY9 flag too! + * - verified sound output with many, many Atari 800 games, + * including the SUPPRESS_INAUDIBLE optimizations. + * 4.0: + * - rewritten from scratch. + * - 16bit stream interface. + * - serout ready/complete delayed interrupts. + * - reworked pot analog/digital conversion timing. + * - optional non-indexing pokey update functions. + * + *****************************************************************************/ + +#include "mamedef.h" +//#include "emu.h" +#include +#ifdef _DEBUG +#include +#endif +#include "pokey.h" + +/* + * Defining this produces much more (about twice as much) + * but also more efficient code. Ideally this should be set + * for processors with big code cache and for healthy compilers :) + */ +#ifndef BIG_SWITCH +#ifndef HEAVY_MACRO_USAGE +#define HEAVY_MACRO_USAGE 1 +#endif +#else +#define HEAVY_MACRO_USAGE BIG_SWITCH +#endif + +#define SUPPRESS_INAUDIBLE 1 + +/* Four channels with a range of 0..32767 and volume 0..15 */ +//#define POKEY_DEFAULT_GAIN (32767/15/4) + +/* + * But we raise the gain and risk clipping, the old Pokey did + * this too. It defined POKEY_DEFAULT_GAIN 6 and this was + * 6 * 15 * 4 = 360, 360/256 = 1.40625 + * I use 15/11 = 1.3636, so this is a little lower. + */ +#define POKEY_DEFAULT_GAIN (32767/11/4) + +#define VERBOSE 0 +#define VERBOSE_SOUND 0 +#define VERBOSE_TIMER 0 +#define VERBOSE_POLY 0 +#define VERBOSE_RAND 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0) + +#define LOG_TIMER(x) do { if (VERBOSE_TIMER) logerror x; } while (0) + +#define LOG_POLY(x) do { if (VERBOSE_POLY) logerror x; } while (0) + +#define LOG_RAND(x) do { if (VERBOSE_RAND) logerror x; } while (0) + +#define CHAN1 0 +#define CHAN2 1 +#define CHAN3 2 +#define CHAN4 3 + +#define TIMER1 0 +#define TIMER2 1 +#define TIMER4 2 + +/* values to add to the divisors for the different modes */ +#define DIVADD_LOCLK 1 +#define DIVADD_HICLK 4 +#define DIVADD_HICLK_JOINED 7 + +/* AUDCx */ +#define NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */ +#define POLY4 0x40 /* selects POLY4 or POLY17 */ +#define PURE 0x20 /* selects POLY4/17 or PURE tone */ +#define VOLUME_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */ +#define VOLUME_MASK 0x0f /* volume mask */ + +/* AUDCTL */ +#define POLY9 0x80 /* selects POLY9 or POLY17 */ +#define CH1_HICLK 0x40 /* selects 1.78979 MHz for Ch 1 */ +#define CH3_HICLK 0x20 /* selects 1.78979 MHz for Ch 3 */ +#define CH12_JOINED 0x10 /* clocks channel 1 w/channel 2 */ +#define CH34_JOINED 0x08 /* clocks channel 3 w/channel 4 */ +#define CH1_FILTER 0x04 /* selects channel 1 high pass filter */ +#define CH2_FILTER 0x02 /* selects channel 2 high pass filter */ +#define CLK_15KHZ 0x01 /* selects 15.6999 kHz or 63.9211 kHz */ + +/* IRQEN (D20E) */ +#define IRQ_BREAK 0x80 /* BREAK key pressed interrupt */ +#define IRQ_KEYBD 0x40 /* keyboard data ready interrupt */ +#define IRQ_SERIN 0x20 /* serial input data ready interrupt */ +#define IRQ_SEROR 0x10 /* serial output register ready interrupt */ +#define IRQ_SEROC 0x08 /* serial output complete interrupt */ +#define IRQ_TIMR4 0x04 /* timer channel #4 interrupt */ +#define IRQ_TIMR2 0x02 /* timer channel #2 interrupt */ +#define IRQ_TIMR1 0x01 /* timer channel #1 interrupt */ + +/* SKSTAT (R/D20F) */ +#define SK_FRAME 0x80 /* serial framing error */ +#define SK_OVERRUN 0x40 /* serial overrun error */ +#define SK_KBERR 0x20 /* keyboard overrun error */ +#define SK_SERIN 0x10 /* serial input high */ +#define SK_SHIFT 0x08 /* shift key pressed */ +#define SK_KEYBD 0x04 /* keyboard key pressed */ +#define SK_SEROUT 0x02 /* serial output active */ + +/* SKCTL (W/D20F) */ +#define SK_BREAK 0x80 /* serial out break signal */ +#define SK_BPS 0x70 /* bits per second */ +#define SK_FM 0x08 /* FM mode */ +#define SK_PADDLE 0x04 /* fast paddle a/d conversion */ +#define SK_RESET 0x03 /* reset serial/keyboard interface */ + +#define DIV_64 28 /* divisor for 1.78979 MHz clock to 63.9211 kHz */ +#define DIV_15 114 /* divisor for 1.78979 MHz clock to 15.6999 kHz */ + +typedef struct _pokey_state pokey_state; +struct _pokey_state +{ + INT32 counter[4]; /* channel counter */ + INT32 divisor[4]; /* channel divisor (modulo value) */ + UINT32 volume[4]; /* channel volume - derived */ + UINT8 output[4]; /* channel output signal (1 active, 0 inactive) */ + UINT8 audible[4]; /* channel plays an audible tone/effect */ + UINT8 Muted[4]; + UINT32 samplerate_24_8; /* sample rate in 24.8 format */ + UINT32 samplepos_fract; /* sample position fractional part */ + UINT32 samplepos_whole; /* sample position whole part */ + UINT32 polyadjust; /* polynome adjustment */ + UINT32 p4; /* poly4 index */ + UINT32 p5; /* poly5 index */ + UINT32 p9; /* poly9 index */ + UINT32 p17; /* poly17 index */ + UINT32 r9; /* rand9 index */ + UINT32 r17; /* rand17 index */ + UINT32 clockmult; /* clock multiplier */ + //device_t *device; + //sound_stream * channel; /* streams channel */ + //emu_timer *timer[3]; /* timers for channel 1,2 and 4 events */ + //attotime timer_period[3]; /* computed periods for these timers */ + //int timer_param[3]; /* computed parameters for these timers */ + //emu_timer *rtimer; /* timer for calculating the random offset */ + //emu_timer *ptimer[8]; /* pot timers */ + //devcb_resolved_read8 pot_r[8]; + //devcb_resolved_read8 allpot_r; + //devcb_resolved_read8 serin_r; + //devcb_resolved_write8 serout_w; + //void (*interrupt_cb)(device_t *device, int mask); + UINT8 AUDF[4]; /* AUDFx (D200, D202, D204, D206) */ + UINT8 AUDC[4]; /* AUDCx (D201, D203, D205, D207) */ + UINT8 POTx[8]; /* POTx (R/D200-D207) */ + UINT8 AUDCTL; /* AUDCTL (W/D208) */ + UINT8 ALLPOT; /* ALLPOT (R/D208) */ + UINT8 KBCODE; /* KBCODE (R/D209) */ + UINT8 RANDOM; /* RANDOM (R/D20A) */ + UINT8 SERIN; /* SERIN (R/D20D) */ + UINT8 SEROUT; /* SEROUT (W/D20D) */ + UINT8 IRQST; /* IRQST (R/D20E) */ + UINT8 IRQEN; /* IRQEN (W/D20E) */ + UINT8 SKSTAT; /* SKSTAT (R/D20F) */ + UINT8 SKCTL; /* SKCTL (W/D20F) */ + //pokey_interface intf; + //attotime clock_period; + double clock_period; + //attotime ad_time_fast; + //attotime ad_time_slow; + + UINT8 poly4[0x0f]; + UINT8 poly5[0x1f]; + UINT8 poly9[0x1ff]; + UINT8 poly17[0x1ffff]; + + UINT8 rand9[0x1ff]; + UINT8 rand17[0x1ffff]; +}; + + +#define P4(chip) chip->poly4[chip->p4] +#define P5(chip) chip->poly5[chip->p5] +#define P9(chip) chip->poly9[chip->p9] +#define P17(chip) chip->poly17[chip->p17] + +//static TIMER_CALLBACK( pokey_timer_expire ); +//static TIMER_CALLBACK( pokey_pot_trigger ); + + +#define SAMPLE -1 + +#define ADJUST_EVENT(chip) \ + chip->counter[CHAN1] -= event; \ + chip->counter[CHAN2] -= event; \ + chip->counter[CHAN3] -= event; \ + chip->counter[CHAN4] -= event; \ + chip->samplepos_whole -= event; \ + chip->polyadjust += event + +#if SUPPRESS_INAUDIBLE + +#define PROCESS_CHANNEL(chip,ch) \ + int toggle = 0; \ + ADJUST_EVENT(chip); \ + /* reset the channel counter */ \ + if( chip->audible[ch] ) \ + chip->counter[ch] = chip->divisor[ch]; \ + else \ + chip->counter[ch] = 0x7fffffff; \ + chip->p4 = (chip->p4+chip->polyadjust)%0x0000f; \ + chip->p5 = (chip->p5+chip->polyadjust)%0x0001f; \ + chip->p9 = (chip->p9+chip->polyadjust)%0x001ff; \ + chip->p17 = (chip->p17+chip->polyadjust)%0x1ffff; \ + chip->polyadjust = 0; \ + if( (chip->AUDC[ch] & NOTPOLY5) || P5(chip) ) \ + { \ + if( chip->AUDC[ch] & PURE ) \ + toggle = 1; \ + else \ + if( chip->AUDC[ch] & POLY4 ) \ + toggle = chip->output[ch] == !P4(chip); \ + else \ + if( chip->AUDCTL & POLY9 ) \ + toggle = chip->output[ch] == !P9(chip); \ + else \ + toggle = chip->output[ch] == !P17(chip); \ + } \ + if( toggle ) \ + { \ + if( chip->audible[ch] && ! chip->Muted[ch] ) \ + { \ + if( chip->output[ch] ) \ + sum -= chip->volume[ch]; \ + else \ + sum += chip->volume[ch]; \ + } \ + chip->output[ch] ^= 1; \ + } \ + /* is this a filtering channel (3/4) and is the filter active? */ \ + if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) \ + { \ + if( chip->output[ch-2] ) \ + { \ + chip->output[ch-2] = 0; \ + if( chip->audible[ch] && ! chip->Muted[ch] ) \ + sum -= chip->volume[ch-2]; \ + } \ + } \ + +#else + +#define PROCESS_CHANNEL(chip,ch) \ + int toggle = 0; \ + ADJUST_EVENT(chip); \ + /* reset the channel counter */ \ + chip->counter[ch] = p[chip].divisor[ch]; \ + chip->p4 = (chip->p4+chip->polyadjust)%0x0000f; \ + chip->p5 = (chip->p5+chip->polyadjust)%0x0001f; \ + chip->p9 = (chip->p9+chip->polyadjust)%0x001ff; \ + chip->p17 = (chip->p17+chip->polyadjust)%0x1ffff; \ + chip->polyadjust = 0; \ + if( (chip->AUDC[ch] & NOTPOLY5) || P5(chip) ) \ + { \ + if( chip->AUDC[ch] & PURE ) \ + toggle = 1; \ + else \ + if( chip->AUDC[ch] & POLY4 ) \ + toggle = chip->output[ch] == !P4(chip); \ + else \ + if( chip->AUDCTL & POLY9 ) \ + toggle = chip->output[ch] == !P9(chip); \ + else \ + toggle = chip->output[ch] == !P17(chip); \ + } \ + if( toggle && ! chip->Muted[ch] ) \ + { \ + if( chip->output[ch] ) \ + sum -= chip->volume[ch]; \ + else \ + sum += chip->volume[ch]; \ + chip->output[ch] ^= 1; \ + } \ + /* is this a filtering channel (3/4) and is the filter active? */ \ + if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) \ + { \ + if( chip->output[ch-2] && ! chip->Muted[ch] ) \ + { \ + chip->output[ch-2] = 0; \ + sum -= chip->volume[ch-2]; \ + } \ + } \ + +#endif + +#define PROCESS_SAMPLE(chip) \ + ADJUST_EVENT(chip); \ + /* adjust the sample position */ \ + chip->samplepos_whole++; \ + /* store sum of output signals into the buffer */ \ +/* *buffer++ = (sum > 0x7fff) ? 0x7fff : sum; */ \ + *bufL++ = *bufR++ = sum; \ + samples-- + +#if HEAVY_MACRO_USAGE + +/* + * This version of PROCESS_POKEY repeats the search for the minimum + * event value without using an index to the channel. That way the + * PROCESS_CHANNEL macros can be called with fixed values and expand + * to much more efficient code + */ + +#define PROCESS_POKEY(chip) \ + UINT32 sum = 0; \ + if( chip->output[CHAN1] && ! chip->Muted[CHAN1] ) \ + sum += chip->volume[CHAN1]; \ + if( chip->output[CHAN2] && ! chip->Muted[CHAN2] ) \ + sum += chip->volume[CHAN2]; \ + if( chip->output[CHAN3] && ! chip->Muted[CHAN3] ) \ + sum += chip->volume[CHAN3]; \ + if( chip->output[CHAN4] && ! chip->Muted[CHAN4] ) \ + sum += chip->volume[CHAN4]; \ + while( samples > 0 ) \ + { \ + if( chip->counter[CHAN1] < chip->samplepos_whole ) \ + { \ + if( chip->counter[CHAN2] < chip->counter[CHAN1] ) \ + { \ + if( chip->counter[CHAN3] < chip->counter[CHAN2] ) \ + { \ + if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN3]; \ + PROCESS_CHANNEL(chip,CHAN3); \ + } \ + } \ + else \ + if( chip->counter[CHAN4] < chip->counter[CHAN2] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN2]; \ + PROCESS_CHANNEL(chip,CHAN2); \ + } \ + } \ + else \ + if( chip->counter[CHAN3] < chip->counter[CHAN1] ) \ + { \ + if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN3]; \ + PROCESS_CHANNEL(chip,CHAN3); \ + } \ + } \ + else \ + if( chip->counter[CHAN4] < chip->counter[CHAN1] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN1]; \ + PROCESS_CHANNEL(chip,CHAN1); \ + } \ + } \ + else \ + if( chip->counter[CHAN2] < chip->samplepos_whole ) \ + { \ + if( chip->counter[CHAN3] < chip->counter[CHAN2] ) \ + { \ + if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN3]; \ + PROCESS_CHANNEL(chip,CHAN3); \ + } \ + } \ + else \ + if( chip->counter[CHAN4] < chip->counter[CHAN2] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN2]; \ + PROCESS_CHANNEL(chip,CHAN2); \ + } \ + } \ + else \ + if( chip->counter[CHAN3] < chip->samplepos_whole ) \ + { \ + if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->counter[CHAN3]; \ + PROCESS_CHANNEL(chip,CHAN3); \ + } \ + } \ + else \ + if( chip->counter[CHAN4] < chip->samplepos_whole ) \ + { \ + UINT32 event = chip->counter[CHAN4]; \ + PROCESS_CHANNEL(chip,CHAN4); \ + } \ + else \ + { \ + UINT32 event = chip->samplepos_whole; \ + PROCESS_SAMPLE(chip); \ + } \ + }/* \ + chip->rtimer->adjust(attotime::never)*/ + +#else /* no HEAVY_MACRO_USAGE */ +/* + * And this version of PROCESS_POKEY uses event and channel variables + * so that the PROCESS_CHANNEL macro needs to index memory at runtime. + */ + +#define PROCESS_POKEY(chip) \ + UINT32 sum = 0; \ + if( chip->output[CHAN1] && ! chip->Muted[CHAN1] ) \ + sum += chip->volume[CHAN1]; \ + if( chip->output[CHAN2] && ! chip->Muted[CHAN2] ) \ + sum += chip->volume[CHAN2]; \ + if( chip->output[CHAN3] && ! chip->Muted[CHAN3] ) \ + sum += chip->volume[CHAN3]; \ + if( chip->output[CHAN4] && ! chip->Muted[CHAN4] ) \ + sum += chip->volume[CHAN4]; \ + while( samples > 0 ) \ + { \ + UINT32 event = chip->samplepos_whole; \ + UINT32 channel = SAMPLE; \ + if( chip->counter[CHAN1] < event ) \ + { \ + event = chip->counter[CHAN1]; \ + channel = CHAN1; \ + } \ + if( chip->counter[CHAN2] < event ) \ + { \ + event = chip->counter[CHAN2]; \ + channel = CHAN2; \ + } \ + if( chip->counter[CHAN3] < event ) \ + { \ + event = chip->counter[CHAN3]; \ + channel = CHAN3; \ + } \ + if( chip->counter[CHAN4] < event ) \ + { \ + event = chip->counter[CHAN4]; \ + channel = CHAN4; \ + } \ + if( channel == SAMPLE ) \ + { \ + PROCESS_SAMPLE(chip); \ + } \ + else \ + { \ + PROCESS_CHANNEL(chip,channel); \ + } \ + }/* \ + chip->rtimer->adjust(attotime::never)*/ + +#endif + + +/*INLINE pokey_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == POKEY); + return (pokey_state *)downcast(device)->token(); +}*/ + + +//static STREAM_UPDATE( pokey_update ) +void pokey_update(void *param, stream_sample_t **outputs, int samples) +{ + pokey_state *chip = (pokey_state *)param; + //stream_sample_t *buffer = outputs[0]; + stream_sample_t *bufL = outputs[0]; + stream_sample_t *bufR = outputs[1]; + PROCESS_POKEY(chip); +} + + +static void poly_init(UINT8 *poly, int size, int left, int right, int add) +{ + int mask = (1 << size) - 1; + int i, x = 0; + + LOG_POLY(("poly %d\n", size)); + for( i = 0; i < mask; i++ ) + { + *poly++ = x & 1; + LOG_POLY(("%05x: %d\n", x, x&1)); + /* calculate next bit */ + x = ((x << left) + (x >> right) + add) & mask; + } +} + +static void rand_init(UINT8 *rng, int size, int left, int right, int add) +{ + int mask = (1 << size) - 1; + int i, x = 0; + + LOG_RAND(("rand %d\n", size)); + for( i = 0; i < mask; i++ ) + { + if (size == 17) + *rng = x >> 6; /* use bits 6..13 */ + else + *rng = x; /* use bits 0..7 */ + LOG_RAND(("%05x: %02x\n", x, *rng)); + rng++; + /* calculate next bit */ + x = ((x << left) + (x >> right) + add) & mask; + } +} + + +/*static void register_for_save(pokey_state *chip, device_t *device) +{ + device->save_item(NAME(chip->counter)); + device->save_item(NAME(chip->divisor)); + device->save_item(NAME(chip->volume)); + device->save_item(NAME(chip->output)); + device->save_item(NAME(chip->audible)); + device->save_item(NAME(chip->samplepos_fract)); + device->save_item(NAME(chip->samplepos_whole)); + device->save_item(NAME(chip->polyadjust)); + device->save_item(NAME(chip->p4)); + device->save_item(NAME(chip->p5)); + device->save_item(NAME(chip->p9)); + device->save_item(NAME(chip->p17)); + device->save_item(NAME(chip->r9)); + device->save_item(NAME(chip->r17)); + device->save_item(NAME(chip->clockmult)); + device->save_item(NAME(chip->timer_period[0])); + device->save_item(NAME(chip->timer_period[1])); + device->save_item(NAME(chip->timer_period[2])); + device->save_item(NAME(chip->timer_param)); + device->save_item(NAME(chip->AUDF)); + device->save_item(NAME(chip->AUDC)); + device->save_item(NAME(chip->POTx)); + device->save_item(NAME(chip->AUDCTL)); + device->save_item(NAME(chip->ALLPOT)); + device->save_item(NAME(chip->KBCODE)); + device->save_item(NAME(chip->RANDOM)); + device->save_item(NAME(chip->SERIN)); + device->save_item(NAME(chip->SEROUT)); + device->save_item(NAME(chip->IRQST)); + device->save_item(NAME(chip->IRQEN)); + device->save_item(NAME(chip->SKSTAT)); + device->save_item(NAME(chip->SKCTL)); +}*/ + + +//static DEVICE_START( pokey ) +int device_start_pokey(void **_info, int clock) +{ + //pokey_state *chip = get_safe_token(device); + pokey_state *chip; + //int sample_rate = device->clock(); + int sample_rate = clock; + //int i; + + chip = (pokey_state *) calloc(1, sizeof(pokey_state)); + *_info = (void *) chip; + + //if (device->static_config()) + // memcpy(&chip->intf, device->static_config(), sizeof(pokey_interface)); + //chip->device = device; + //chip->clock_period = attotime::from_hz(device->clock()); + chip->clock_period = 1.0 / clock; + + /* calculate the A/D times + * In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion + * takes N scanlines, where N is the paddle value. A single scanline + * takes approximately 64us to finish (1.78979MHz clock). + * In quick mode (SK_PADDLE set) the conversion is done very fast + * (takes two scanlines) but the result is not as accurate. + */ + //chip->ad_time_fast = (attotime::from_nsec(64000*2/228) * FREQ_17_EXACT) / device->clock(); + //chip->ad_time_slow = (attotime::from_nsec(64000 ) * FREQ_17_EXACT) / device->clock(); + + /* initialize the poly counters */ + poly_init(chip->poly4, 4, 3, 1, 0x00004); + poly_init(chip->poly5, 5, 3, 2, 0x00008); + poly_init(chip->poly9, 9, 8, 1, 0x00180); + poly_init(chip->poly17, 17,16, 1, 0x1c000); + + /* initialize the random arrays */ + rand_init(chip->rand9, 9, 8, 1, 0x00180); + rand_init(chip->rand17, 17,16, 1, 0x1c000); + + //chip->samplerate_24_8 = (device->clock() << 8) / sample_rate; + chip->samplerate_24_8 = (clock << 8) / sample_rate; + chip->divisor[CHAN1] = 4; + chip->divisor[CHAN2] = 4; + chip->divisor[CHAN3] = 4; + chip->divisor[CHAN4] = 4; + chip->clockmult = DIV_64; + chip->KBCODE = 0x09; /* Atari 800 'no key' */ + chip->SKCTL = SK_RESET; /* let the RNG run after reset */ + //chip->rtimer = device->machine().scheduler().timer_alloc(FUNC_NULL); + + //chip->timer[0] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip); + //chip->timer[1] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip); + //chip->timer[2] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip); + + /*for (i=0; i<8; i++) + { + chip->ptimer[i] = device->machine().scheduler().timer_alloc(FUNC(pokey_pot_trigger), chip); + chip->pot_r[i].resolve(chip->intf.pot_r[i], *device); + } + chip->allpot_r.resolve(chip->intf.allpot_r, *device); + chip->serin_r.resolve(chip->intf.serin_r, *device); + chip->serout_w.resolve(chip->intf.serout_w, *device); + chip->interrupt_cb = chip->intf.interrupt_cb;*/ + + //chip->channel = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, chip, pokey_update); + + //register_for_save(chip, device); + + return sample_rate; +} + +void device_stop_pokey(void *chip) +{ + free(chip); + + return; +} + +void device_reset_pokey(void *_info) +{ + pokey_state *chip = (pokey_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 4; CurChn ++) + { + chip->counter[CurChn] = 0; + chip->divisor[CurChn] = 4; + chip->volume[CurChn] = 0; + chip->output[CurChn] = 0; + chip->audible[CurChn] = 0; + } + chip->samplepos_fract = 0; + chip->samplepos_whole = 0; + chip->polyadjust = 0; + chip->p4 = 0; + chip->p5 = 0; + chip->p9 = 0; + chip->p17 = 0; + chip->r9 = 0; + chip->r17 = 0; + chip->clockmult = DIV_64; + + return; +} + +/*static TIMER_CALLBACK( pokey_timer_expire ) +{ + pokey_state *p = (pokey_state *)ptr; + int timers = param; + + LOG_TIMER(("POKEY #%p timer %d with IRQEN $%02x\n", p, timers, p->IRQEN)); + + // check if some of the requested timer interrupts are enabled // + timers &= p->IRQEN; + + if( timers ) + { + // set the enabled timer irq status bits // + p->IRQST |= timers; + // call back an application supplied function to handle the interrupt // + if( p->interrupt_cb ) + (*p->interrupt_cb)(p->device, timers); + } +}*/ + +/*static char *audc2str(int val) +{ + static char buff[80]; + if( val & NOTPOLY5 ) + { + if( val & PURE ) + strcpy(buff,"pure"); + else + if( val & POLY4 ) + strcpy(buff,"poly4"); + else + strcpy(buff,"poly9/17"); + } + else + { + if( val & PURE ) + strcpy(buff,"poly5"); + else + if( val & POLY4 ) + strcpy(buff,"poly4+poly5"); + else + strcpy(buff,"poly9/17+poly5"); + } + return buff; +} + +static char *audctl2str(int val) +{ + static char buff[80]; + if( val & POLY9 ) + strcpy(buff,"poly9"); + else + strcpy(buff,"poly17"); + if( val & CH1_HICLK ) + strcat(buff,"+ch1hi"); + if( val & CH3_HICLK ) + strcat(buff,"+ch3hi"); + if( val & CH12_JOINED ) + strcat(buff,"+ch1/2"); + if( val & CH34_JOINED ) + strcat(buff,"+ch3/4"); + if( val & CH1_FILTER ) + strcat(buff,"+ch1filter"); + if( val & CH2_FILTER ) + strcat(buff,"+ch2filter"); + if( val & CLK_15KHZ ) + strcat(buff,"+clk15"); + return buff; +}*/ + +/*static TIMER_CALLBACK( pokey_serin_ready_cb ) +{ + pokey_state *p = (pokey_state *)ptr; + if( p->IRQEN & IRQ_SERIN ) + { + // set the enabled timer irq status bits // + p->IRQST |= IRQ_SERIN; + // call back an application supplied function to handle the interrupt // + if( p->interrupt_cb ) + (*p->interrupt_cb)(p->device, IRQ_SERIN); + } +} + +static TIMER_CALLBACK( pokey_serout_ready_cb ) +{ + pokey_state *p = (pokey_state *)ptr; + if( p->IRQEN & IRQ_SEROR ) + { + p->IRQST |= IRQ_SEROR; + if( p->interrupt_cb ) + (*p->interrupt_cb)(p->device, IRQ_SEROR); + } +} + +static TIMER_CALLBACK( pokey_serout_complete ) +{ + pokey_state *p = (pokey_state *)ptr; + if( p->IRQEN & IRQ_SEROC ) + { + p->IRQST |= IRQ_SEROC; + if( p->interrupt_cb ) + (*p->interrupt_cb)(p->device, IRQ_SEROC); + } +} + +static TIMER_CALLBACK( pokey_pot_trigger ) +{ + pokey_state *p = (pokey_state *)ptr; + int pot = param; + LOG(("POKEY #%p POT%d triggers after %dus\n", p, pot, (int)(1000000 * p->ptimer[pot]->elapsed().as_double()))); + p->ALLPOT &= ~(1 << pot); // set the enabled timer irq status bits // +}*/ + +/*#define AD_TIME ((p->SKCTL & SK_PADDLE) ? p->ad_time_fast : p->ad_time_slow) + +static void pokey_potgo(pokey_state *p) +{ + int pot; + + LOG(("POKEY #%p pokey_potgo\n", p)); + + p->ALLPOT = 0xff; + + for( pot = 0; pot < 8; pot++ ) + { + p->POTx[pot] = 0xff; + if( !p->pot_r[pot].isnull() ) + { + int r = p->pot_r[pot](pot); + + LOG(("POKEY %s pot_r(%d) returned $%02x\n", p->device->tag(), pot, r)); + if( r != -1 ) + { + if (r > 228) + r = 228; + + // final value // + p->POTx[pot] = r; + p->ptimer[pot]->adjust(AD_TIME * r, pot); + } + } + } +}*/ + +//READ8_DEVICE_HANDLER( pokey_r ) +UINT8 pokey_r(void *_info, offs_t offset) +{ + //pokey_state *p = get_safe_token(device); + pokey_state *p = (pokey_state *)_info; + int data = 0, pot; + UINT32 adjust = 0; + + switch (offset & 15) + { + case POT0_C: case POT1_C: case POT2_C: case POT3_C: + case POT4_C: case POT5_C: case POT6_C: case POT7_C: + pot = offset & 7; + /*if( !p->pot_r[pot].isnull() ) + { + // + * If the conversion is not yet finished (ptimer running), + * get the current value by the linear interpolation of + * the final value using the elapsed time. + // + if( p->ALLPOT & (1 << pot) ) + { + //data = p->ptimer[pot]->elapsed().attoseconds / AD_TIME.attoseconds; + data = p->POTx[pot]; + LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", p->device->tag(), pot, data)); + } + else + { + data = p->POTx[pot]; + LOG(("POKEY '%s' read POT%d (final value) $%02x\n", p->device->tag(), pot, data)); + } + } + else + logerror("%s: warning - read '%s' POT%d\n", p->device->machine().describe_context(), p->device->tag(), pot);*/ + break; + + case ALLPOT_C: + /**************************************************************** + * If the 2 least significant bits of SKCTL are 0, the ALLPOTs + * are disabled (SKRESET). Thanks to MikeJ for pointing this out. + ****************************************************************/ + /*if( (p->SKCTL & SK_RESET) == 0) + { + data = 0; + LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", p->device->tag(), data)); + } + else if( !p->allpot_r.isnull() ) + { + data = p->allpot_r(offset); + LOG(("POKEY '%s' ALLPOT callback $%02x\n", p->device->tag(), data)); + } + else + { + data = p->ALLPOT; + LOG(("POKEY '%s' ALLPOT internal $%02x\n", p->device->tag(), data)); + }*/ + break; + + case KBCODE_C: + data = p->KBCODE; + break; + + case RANDOM_C: + /**************************************************************** + * If the 2 least significant bits of SKCTL are 0, the random + * number generator is disabled (SKRESET). Thanks to Eric Smith + * for pointing out this critical bit of info! If the random + * number generator is enabled, get a new random number. Take + * the time gone since the last read into account and read the + * new value from an appropriate offset in the rand17 table. + ****************************************************************/ + if( p->SKCTL & SK_RESET ) + { + //adjust = p->rtimer->elapsed().as_double() / p->clock_period.as_double(); + adjust = 0; + p->r9 = (p->r9 + adjust) % 0x001ff; + p->r17 = (p->r17 + adjust) % 0x1ffff; + } + else + { + adjust = 1; + p->r9 = 0; + p->r17 = 0; + //LOG_RAND(("POKEY '%s' rand17 frozen (SKCTL): $%02x\n", p->device->tag(), p->RANDOM)); + } + if( p->AUDCTL & POLY9 ) + { + p->RANDOM = p->rand9[p->r9]; + //LOG_RAND(("POKEY '%s' adjust %u rand9[$%05x]: $%02x\n", p->device->tag(), adjust, p->r9, p->RANDOM)); + } + else + { + p->RANDOM = p->rand17[p->r17]; + //LOG_RAND(("POKEY '%s' adjust %u rand17[$%05x]: $%02x\n", p->device->tag(), adjust, p->r17, p->RANDOM)); + } + //if (adjust > 0) + // p->rtimer->adjust(attotime::never); + data = p->RANDOM ^ 0xff; + break; + + case SERIN_C: + //if( !p->serin_r.isnull() ) + // p->SERIN = p->serin_r(offset); + data = p->SERIN; + //LOG(("POKEY '%s' SERIN $%02x\n", p->device->tag(), data)); + break; + + case IRQST_C: + /* IRQST is an active low input port; we keep it active high */ + /* internally to ease the (un-)masking of bits */ + data = p->IRQST ^ 0xff; + //LOG(("POKEY '%s' IRQST $%02x\n", p->device->tag(), data)); + break; + + case SKSTAT_C: + /* SKSTAT is also an active low input port */ + data = p->SKSTAT ^ 0xff; + //LOG(("POKEY '%s' SKSTAT $%02x\n", p->device->tag(), data)); + break; + + default: + //LOG(("POKEY '%s' register $%02x\n", p->device->tag(), offset)); + break; + } + return data; +} + +/*READ8_HANDLER( quad_pokey_r ) +{ + static const char *const devname[4] = { "pokey1", "pokey2", "pokey3", "pokey4" }; + int pokey_num = (offset >> 3) & ~0x04; + int control = (offset & 0x20) >> 2; + int pokey_reg = (offset % 8) | control; + + return pokey_r(space->machine().device(devname[pokey_num]), pokey_reg); +}*/ + + +//WRITE8_DEVICE_HANDLER( pokey_w ) +void pokey_w(void *_info, offs_t offset, UINT8 data) +{ + //pokey_state *p = get_safe_token(device); + pokey_state *p = (pokey_state *)_info; + int ch_mask = 0, new_val; + + //p->channel->update(); + + /* determine which address was changed */ + switch (offset & 15) + { + case AUDF1_C: + if( data == p->AUDF[CHAN1] ) + return; + //LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", p->device->tag(), data)); + p->AUDF[CHAN1] = data; + ch_mask = 1 << CHAN1; + if( p->AUDCTL & CH12_JOINED ) /* if ch 1&2 tied together */ + ch_mask |= 1 << CHAN2; /* then also change on ch2 */ + break; + + case AUDC1_C: + if( data == p->AUDC[CHAN1] ) + return; + //LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); + p->AUDC[CHAN1] = data; + ch_mask = 1 << CHAN1; + break; + + case AUDF2_C: + if( data == p->AUDF[CHAN2] ) + return; + //LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", p->device->tag(), data)); + p->AUDF[CHAN2] = data; + ch_mask = 1 << CHAN2; + break; + + case AUDC2_C: + if( data == p->AUDC[CHAN2] ) + return; + //LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); + p->AUDC[CHAN2] = data; + ch_mask = 1 << CHAN2; + break; + + case AUDF3_C: + if( data == p->AUDF[CHAN3] ) + return; + //LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", p->device->tag(), data)); + p->AUDF[CHAN3] = data; + ch_mask = 1 << CHAN3; + + if( p->AUDCTL & CH34_JOINED ) /* if ch 3&4 tied together */ + ch_mask |= 1 << CHAN4; /* then also change on ch4 */ + break; + + case AUDC3_C: + if( data == p->AUDC[CHAN3] ) + return; + //LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); + p->AUDC[CHAN3] = data; + ch_mask = 1 << CHAN3; + break; + + case AUDF4_C: + if( data == p->AUDF[CHAN4] ) + return; + //LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", p->device->tag(), data)); + p->AUDF[CHAN4] = data; + ch_mask = 1 << CHAN4; + break; + + case AUDC4_C: + if( data == p->AUDC[CHAN4] ) + return; + //LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", p->device->tag(), data, audc2str(data))); + p->AUDC[CHAN4] = data; + ch_mask = 1 << CHAN4; + break; + + case AUDCTL_C: + if( data == p->AUDCTL ) + return; + //LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", p->device->tag(), data, audctl2str(data))); + p->AUDCTL = data; + ch_mask = 15; /* all channels */ + /* determine the base multiplier for the 'div by n' calculations */ + p->clockmult = (p->AUDCTL & CLK_15KHZ) ? DIV_15 : DIV_64; + break; + + case STIMER_C: + /*// first remove any existing timers // + LOG_TIMER(("POKEY '%s' STIMER $%02x\n", p->device->tag(), data)); + + p->timer[TIMER1]->adjust(attotime::never, p->timer_param[TIMER1]); + p->timer[TIMER2]->adjust(attotime::never, p->timer_param[TIMER2]); + p->timer[TIMER4]->adjust(attotime::never, p->timer_param[TIMER4]); + + // reset all counters to zero (side effect) // + p->polyadjust = 0; + p->counter[CHAN1] = 0; + p->counter[CHAN2] = 0; + p->counter[CHAN3] = 0; + p->counter[CHAN4] = 0; + + // joined chan#1 and chan#2 ? // + if( p->AUDCTL & CH12_JOINED ) + { + if( p->divisor[CHAN2] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer1+2 after %d clocks\n", p->device->tag(), p->divisor[CHAN2])); + // set timer #1 _and_ #2 event after timer_div clocks of joined CHAN1+CHAN2 // + p->timer_period[TIMER2] = p->clock_period * p->divisor[CHAN2]; + p->timer_param[TIMER2] = IRQ_TIMR2|IRQ_TIMR1; + p->timer[TIMER2]->adjust(p->timer_period[TIMER2], p->timer_param[TIMER2], p->timer_period[TIMER2]); + } + } + else + { + if( p->divisor[CHAN1] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer1 after %d clocks\n", p->device->tag(), p->divisor[CHAN1])); + // set timer #1 event after timer_div clocks of CHAN1 // + p->timer_period[TIMER1] = p->clock_period * p->divisor[CHAN1]; + p->timer_param[TIMER1] = IRQ_TIMR1; + p->timer[TIMER1]->adjust(p->timer_period[TIMER1], p->timer_param[TIMER1], p->timer_period[TIMER1]); + } + + if( p->divisor[CHAN2] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer2 after %d clocks\n", p->device->tag(), p->divisor[CHAN2])); + // set timer #2 event after timer_div clocks of CHAN2 // + p->timer_period[TIMER2] = p->clock_period * p->divisor[CHAN2]; + p->timer_param[TIMER2] = IRQ_TIMR2; + p->timer[TIMER2]->adjust(p->timer_period[TIMER2], p->timer_param[TIMER2], p->timer_period[TIMER2]); + } + } + + // Note: p[chip] does not have a timer #3 // + + if( p->AUDCTL & CH34_JOINED ) + { + // not sure about this: if audc4 == 0000xxxx don't start timer 4 ? // + if( p->AUDC[CHAN4] & 0xf0 ) + { + if( p->divisor[CHAN4] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", p->device->tag(), p->divisor[CHAN4])); + // set timer #4 event after timer_div clocks of CHAN4 // + p->timer_period[TIMER4] = p->clock_period * p->divisor[CHAN4]; + p->timer_param[TIMER4] = IRQ_TIMR4; + p->timer[TIMER4]->adjust(p->timer_period[TIMER4], p->timer_param[TIMER4], p->timer_period[TIMER4]); + } + } + } + else + { + if( p->divisor[CHAN4] > 4 ) + { + LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", p->device->tag(), p->divisor[CHAN4])); + // set timer #4 event after timer_div clocks of CHAN4 // + p->timer_period[TIMER4] = p->clock_period * p->divisor[CHAN4]; + p->timer_param[TIMER4] = IRQ_TIMR4; + p->timer[TIMER4]->adjust(p->timer_period[TIMER4], p->timer_param[TIMER4], p->timer_period[TIMER4]); + } + } + + p->timer[TIMER1]->enable(p->IRQEN & IRQ_TIMR1); + p->timer[TIMER2]->enable(p->IRQEN & IRQ_TIMR2); + p->timer[TIMER4]->enable(p->IRQEN & IRQ_TIMR4);*/ + break; + + case SKREST_C: + /* reset SKSTAT */ + //LOG(("POKEY '%s' SKREST $%02x\n", p->device->tag(), data)); + p->SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR); + break; + + case POTGO_C: + //LOG(("POKEY '%s' POTGO $%02x\n", p->device->tag(), data)); + //pokey_potgo(p); + break; + + case SEROUT_C: + //LOG(("POKEY '%s' SEROUT $%02x\n", p->device->tag(), data)); + //p->serout_w(offset, data); + //p->SKSTAT |= SK_SEROUT; + /* + * These are arbitrary values, tested with some custom boot + * loaders from Ballblazer and Escape from Fractalus + * The real times are unknown + */ + //device->machine().scheduler().timer_set(attotime::from_usec(200), FUNC(pokey_serout_ready_cb), 0, p); + /* 10 bits (assumption 1 start, 8 data and 1 stop bit) take how long? */ + //device->machine().scheduler().timer_set(attotime::from_usec(2000), FUNC(pokey_serout_complete), 0, p); + break; + + case IRQEN_C: + //LOG(("POKEY '%s' IRQEN $%02x\n", p->device->tag(), data)); + + /* acknowledge one or more IRQST bits ? */ + if( p->IRQST & ~data ) + { + /* reset IRQST bits that are masked now */ + p->IRQST &= data; + } + else + { + /* enable/disable timers now to avoid unneeded + breaking of the CPU cores for masked timers */ + /*if( p->timer[TIMER1] && ((p->IRQEN^data) & IRQ_TIMR1) ) + p->timer[TIMER1]->enable(data & IRQ_TIMR1); + if( p->timer[TIMER2] && ((p->IRQEN^data) & IRQ_TIMR2) ) + p->timer[TIMER2]->enable(data & IRQ_TIMR2); + if( p->timer[TIMER4] && ((p->IRQEN^data) & IRQ_TIMR4) ) + p->timer[TIMER4]->enable(data & IRQ_TIMR4);*/ + } + /* store irq enable */ + p->IRQEN = data; + break; + + case SKCTL_C: + if( data == p->SKCTL ) + return; + //LOG(("POKEY '%s' SKCTL $%02x\n", p->device->tag(), data)); + p->SKCTL = data; + if( !(data & SK_RESET) ) + { + pokey_w(p, IRQEN_C, 0); + pokey_w(p, SKREST_C, 0); + } + break; + } + + /************************************************************ + * As defined in the manual, the exact counter values are + * different depending on the frequency and resolution: + * 64 kHz or 15 kHz - AUDF + 1 + * 1.79 MHz, 8-bit - AUDF + 4 + * 1.79 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 + ************************************************************/ + + /* only reset the channels that have changed */ + + if( ch_mask & (1 << CHAN1) ) + { + /* process channel 1 frequency */ + if( p->AUDCTL & CH1_HICLK ) + new_val = p->AUDF[CHAN1] + DIVADD_HICLK; + else + new_val = (p->AUDF[CHAN1] + DIVADD_LOCLK) * p->clockmult; + + //LOG_SOUND(("POKEY '%s' chan1 %d\n", p->device->tag(), new_val)); + + p->volume[CHAN1] = (p->AUDC[CHAN1] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + p->divisor[CHAN1] = new_val; + if( new_val < p->counter[CHAN1] ) + p->counter[CHAN1] = new_val; + //if( p->interrupt_cb && p->timer[TIMER1] ) + // p->timer[TIMER1]->adjust(p->clock_period * new_val, p->timer_param[TIMER1], p->timer_period[TIMER1]); + p->audible[CHAN1] = !( + (p->AUDC[CHAN1] & VOLUME_ONLY) || + (p->AUDC[CHAN1] & VOLUME_MASK) == 0 || + ((p->AUDC[CHAN1] & PURE) && new_val < (p->samplerate_24_8 >> 8))); + if( !p->audible[CHAN1] ) + { + p->output[CHAN1] = 1; + p->counter[CHAN1] = 0x7fffffff; + /* 50% duty cycle should result in half volume */ + p->volume[CHAN1] >>= 1; + } + } + + if( ch_mask & (1 << CHAN2) ) + { + /* process channel 2 frequency */ + if( p->AUDCTL & CH12_JOINED ) + { + if( p->AUDCTL & CH1_HICLK ) + new_val = p->AUDF[CHAN2] * 256 + p->AUDF[CHAN1] + DIVADD_HICLK_JOINED; + else + new_val = (p->AUDF[CHAN2] * 256 + p->AUDF[CHAN1] + DIVADD_LOCLK) * p->clockmult; + //LOG_SOUND(("POKEY '%s' chan1+2 %d\n", p->device->tag(), new_val)); + } + else + { + new_val = (p->AUDF[CHAN2] + DIVADD_LOCLK) * p->clockmult; + //LOG_SOUND(("POKEY '%s' chan2 %d\n", p->device->tag(), new_val)); + } + + p->volume[CHAN2] = (p->AUDC[CHAN2] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + p->divisor[CHAN2] = new_val; + if( new_val < p->counter[CHAN2] ) + p->counter[CHAN2] = new_val; + //if( p->interrupt_cb && p->timer[TIMER2] ) + // p->timer[TIMER2]->adjust(p->clock_period * new_val, p->timer_param[TIMER2], p->timer_period[TIMER2]); + p->audible[CHAN2] = !( + (p->AUDC[CHAN2] & VOLUME_ONLY) || + (p->AUDC[CHAN2] & VOLUME_MASK) == 0 || + ((p->AUDC[CHAN2] & PURE) && new_val < (p->samplerate_24_8 >> 8))); + if( !p->audible[CHAN2] ) + { + p->output[CHAN2] = 1; + p->counter[CHAN2] = 0x7fffffff; + /* 50% duty cycle should result in half volume */ + p->volume[CHAN2] >>= 1; + } + } + + if( ch_mask & (1 << CHAN3) ) + { + /* process channel 3 frequency */ + if( p->AUDCTL & CH3_HICLK ) + new_val = p->AUDF[CHAN3] + DIVADD_HICLK; + else + new_val = (p->AUDF[CHAN3] + DIVADD_LOCLK) * p->clockmult; + + //LOG_SOUND(("POKEY '%s' chan3 %d\n", p->device->tag(), new_val)); + + p->volume[CHAN3] = (p->AUDC[CHAN3] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + p->divisor[CHAN3] = new_val; + if( new_val < p->counter[CHAN3] ) + p->counter[CHAN3] = new_val; + /* channel 3 does not have a timer associated */ + p->audible[CHAN3] = !( + (p->AUDC[CHAN3] & VOLUME_ONLY) || + (p->AUDC[CHAN3] & VOLUME_MASK) == 0 || + ((p->AUDC[CHAN3] & PURE) && new_val < (p->samplerate_24_8 >> 8))) || + (p->AUDCTL & CH1_FILTER); + if( !p->audible[CHAN3] ) + { + p->output[CHAN3] = 1; + p->counter[CHAN3] = 0x7fffffff; + /* 50% duty cycle should result in half volume */ + p->volume[CHAN3] >>= 1; + } + } + + if( ch_mask & (1 << CHAN4) ) + { + /* process channel 4 frequency */ + if( p->AUDCTL & CH34_JOINED ) + { + if( p->AUDCTL & CH3_HICLK ) + new_val = p->AUDF[CHAN4] * 256 + p->AUDF[CHAN3] + DIVADD_HICLK_JOINED; + else + new_val = (p->AUDF[CHAN4] * 256 + p->AUDF[CHAN3] + DIVADD_LOCLK) * p->clockmult; + //LOG_SOUND(("POKEY '%s' chan3+4 %d\n", p->device->tag(), new_val)); + } + else + { + new_val = (p->AUDF[CHAN4] + DIVADD_LOCLK) * p->clockmult; + //LOG_SOUND(("POKEY '%s' chan4 %d\n", p->device->tag(), new_val)); + } + + p->volume[CHAN4] = (p->AUDC[CHAN4] & VOLUME_MASK) * POKEY_DEFAULT_GAIN; + p->divisor[CHAN4] = new_val; + if( new_val < p->counter[CHAN4] ) + p->counter[CHAN4] = new_val; + //if( p->interrupt_cb && p->timer[TIMER4] ) + // p->timer[TIMER4]->adjust(p->clock_period * new_val, p->timer_param[TIMER4], p->timer_period[TIMER4]); + p->audible[CHAN4] = !( + (p->AUDC[CHAN4] & VOLUME_ONLY) || + (p->AUDC[CHAN4] & VOLUME_MASK) == 0 || + ((p->AUDC[CHAN4] & PURE) && new_val < (p->samplerate_24_8 >> 8))) || + (p->AUDCTL & CH2_FILTER); + if( !p->audible[CHAN4] ) + { + p->output[CHAN4] = 1; + p->counter[CHAN4] = 0x7fffffff; + /* 50% duty cycle should result in half volume */ + p->volume[CHAN4] >>= 1; + } + } +} + +/*WRITE8_HANDLER( quad_pokey_w ) +{ + static const char *const devname[4] = { "pokey1", "pokey2", "pokey3", "pokey4" }; + int pokey_num = (offset >> 3) & ~0x04; + int control = (offset & 0x20) >> 2; + int pokey_reg = (offset % 8) | control; + + pokey_w(space->machine().device(devname[pokey_num]), pokey_reg, data); +} + +void pokey_serin_ready(device_t *device, int after) +{ + pokey_state *p = get_safe_token(device); + device->machine().scheduler().timer_set(p->clock_period * after, FUNC(pokey_serin_ready_cb), 0, p); +} + +void pokey_break_w(device_t *device, int shift) +{ + //pokey_state *p = get_safe_token(device); + if( shift ) // shift code ? // + p->SKSTAT |= SK_SHIFT; + else + p->SKSTAT &= ~SK_SHIFT; + // check if the break IRQ is enabled // + if( p->IRQEN & IRQ_BREAK ) + { + // set break IRQ status and call back the interrupt handler // + p->IRQST |= IRQ_BREAK; + if( p->interrupt_cb ) + (*p->interrupt_cb)(device, IRQ_BREAK); + } +} + +void pokey_kbcode_w(device_t *device, int kbcode, int make) +{ + pokey_state *p = get_safe_token(device); + // make code ? // + if( make ) + { + p->KBCODE = kbcode; + p->SKSTAT |= SK_KEYBD; + if( kbcode & 0x40 ) // shift code ? // + p->SKSTAT |= SK_SHIFT; + else + p->SKSTAT &= ~SK_SHIFT; + + if( p->IRQEN & IRQ_KEYBD ) + { + // last interrupt not acknowledged ? // + if( p->IRQST & IRQ_KEYBD ) + p->SKSTAT |= SK_KBERR; + p->IRQST |= IRQ_KEYBD; + if( p->interrupt_cb ) + (*p->interrupt_cb)(device, IRQ_KEYBD); + } + } + else + { + p->KBCODE = kbcode; + p->SKSTAT &= ~SK_KEYBD; + } +}*/ + + +void pokey_set_mute_mask(void *_info, UINT32 MuteMask) +{ + pokey_state *chip = (pokey_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 4; CurChn ++) + chip->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; + + return; +} + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( pokey ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pokey_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( pokey ); break; + case DEVINFO_FCT_STOP: // Nothing // break; + case DEVINFO_FCT_RESET: // Nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "POKEY"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Atari custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "4.51"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(POKEY, pokey);*/ diff --git a/Frameworks/GME/vgmplay/chips/pokey.h b/Frameworks/GME/vgmplay/chips/pokey.h new file mode 100644 index 000000000..0b50425da --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/pokey.h @@ -0,0 +1,99 @@ +/***************************************************************************** + * + * POKEY chip emulator 4.3 + * Copyright Nicola Salmoria and the MAME Team + * + * Based on original info found in Ron Fries' Pokey emulator, + * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller. + * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual. + * Polynome algorithms according to info supplied by Perry McFarlane. + * + * This code is subject to the MAME license, which besides other + * things means it is distributed as is, no warranties whatsoever. + * For more details read mame.txt that comes with MAME. + * + *****************************************************************************/ + +#pragma once + +//#include "devlegcy.h" + +/* CONSTANT DEFINITIONS */ + +/* POKEY WRITE LOGICALS */ +#define AUDF1_C 0x00 +#define AUDC1_C 0x01 +#define AUDF2_C 0x02 +#define AUDC2_C 0x03 +#define AUDF3_C 0x04 +#define AUDC3_C 0x05 +#define AUDF4_C 0x06 +#define AUDC4_C 0x07 +#define AUDCTL_C 0x08 +#define STIMER_C 0x09 +#define SKREST_C 0x0A +#define POTGO_C 0x0B +#define SEROUT_C 0x0D +#define IRQEN_C 0x0E +#define SKCTL_C 0x0F + +/* POKEY READ LOGICALS */ +#define POT0_C 0x00 +#define POT1_C 0x01 +#define POT2_C 0x02 +#define POT3_C 0x03 +#define POT4_C 0x04 +#define POT5_C 0x05 +#define POT6_C 0x06 +#define POT7_C 0x07 +#define ALLPOT_C 0x08 +#define KBCODE_C 0x09 +#define RANDOM_C 0x0A +#define SERIN_C 0x0D +#define IRQST_C 0x0E +#define SKSTAT_C 0x0F + +/* exact 1.79 MHz clock freq (of the Atari 800 that is) */ +#define FREQ_17_EXACT 1789790 + + +/***************************************************************************** + * pot0_r to pot7_r: + * Handlers for reading the pot values. Some Atari games use + * ALLPOT to return dipswitch settings and other things. + * serin_r, serout_w, interrupt_cb: + * New function pointers for serial input/output and a interrupt callback. + *****************************************************************************/ + +/*typedef struct _pokey_interface pokey_interface; +struct _pokey_interface +{ + devcb_read8 pot_r[8]; + devcb_read8 allpot_r; + devcb_read8 serin_r; + devcb_write8 serout_w; + void (*interrupt_cb)(device_t *device, int mask); +};*/ + + +void pokey_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_pokey(void **chip, int clock); +void device_stop_pokey(void *chip); +void device_reset_pokey(void *chip); + +//READ8_DEVICE_HANDLER( pokey_r ); +//WRITE8_DEVICE_HANDLER( pokey_w ); +UINT8 pokey_r(void *chip, offs_t offset); +void pokey_w(void *chip, offs_t offset, UINT8 data); + +/* fix me: eventually this should be a single device with pokey subdevices */ +//READ8_HANDLER( quad_pokey_r ); +//WRITE8_HANDLER( quad_pokey_w ); + +/*void pokey_serin_ready (device_t *device, int after); +void pokey_break_w (device_t *device, int shift); +void pokey_kbcode_w (device_t *device, int kbcode, int make);*/ + +void pokey_set_mute_mask(void *chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(POKEY, pokey); diff --git a/Frameworks/GME/gme/pwm.c b/Frameworks/GME/vgmplay/chips/pwm.c similarity index 91% rename from Frameworks/GME/gme/pwm.c rename to Frameworks/GME/vgmplay/chips/pwm.c index b1c78129f..26491efcd 100644 --- a/Frameworks/GME/gme/pwm.c +++ b/Frameworks/GME/vgmplay/chips/pwm.c @@ -1,281 +1,277 @@ -/*************************************************************************** - * Gens: PWM audio emulator. * - * * - * Copyright (c) 1999-2002 by Stéphane Dallongeville * - * Copyright (c) 2003-2004 by Stéphane Akhoun * - * Copyright (c) 2008-2009 by David Korth * - * * - * This program is free software; you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the * - * Free Software Foundation; either version 2 of the License, or (at your * - * option) any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, write to the Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include "pwm.h" - -#include -#include +/*************************************************************************** + * Gens: PWM audio emulator. * + * * + * Copyright (c) 1999-2002 by Stéphane Dallongeville * + * Copyright (c) 2003-2004 by Stéphane Akhoun * + * Copyright (c) 2008-2009 by David Korth * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ -#ifndef INLINE -#define INLINE static __inline -#endif +#include "mamedef.h" +#include "pwm.h" + +#include //#include "gens_core/mem/mem_sh2.h" //#include "gens_core/cpu/sh2/sh2.h" - -#define CHILLY_WILLY_SCALE 1 - -#if PWM_BUF_SIZE == 8 -unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] = -{ - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, -}; -#elif PWM_BUF_SIZE == 4 -unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] = -{ - 0x40, 0x00, 0x00, 0x80, - 0x80, 0x40, 0x00, 0x00, - 0x00, 0x80, 0x40, 0x00, - 0x00, 0x00, 0x80, 0x40, -}; -#else -#error PWM_BUF_SIZE must equal 4 or 8. -#endif /* PWM_BUF_SIZE */ - -typedef struct _pwm_chip -{ - unsigned short PWM_FIFO_R[8]; - unsigned short PWM_FIFO_L[8]; - unsigned int PWM_RP_R; - unsigned int PWM_WP_R; - unsigned int PWM_RP_L; - unsigned int PWM_WP_L; - unsigned int PWM_Cycles; - unsigned int PWM_Cycle; - unsigned int PWM_Cycle_Cnt; - unsigned int PWM_Int; - unsigned int PWM_Int_Cnt; - unsigned int PWM_Mode; - //unsigned int PWM_Enable; - unsigned int PWM_Out_R; - unsigned int PWM_Out_L; - - unsigned int PWM_Cycle_Tmp; - unsigned int PWM_Cycles_Tmp; - unsigned int PWM_Int_Tmp; - unsigned int PWM_FIFO_L_Tmp; - unsigned int PWM_FIFO_R_Tmp; - -#if CHILLY_WILLY_SCALE -// TODO: Fix Chilly Willy's new scaling algorithm. - /* PWM scaling variables. */ - int PWM_Offset; - int PWM_Scale; - //int PWM_Loudness; -#endif - - int clock; -} pwm_chip; -#if CHILLY_WILLY_SCALE -// TODO: Fix Chilly Willy's new scaling algorithm. -#define PWM_Loudness 0 -#endif - -void PWM_Init(pwm_chip* chip); -void PWM_Recalc_Scale(pwm_chip* chip); - -void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle); -void PWM_Set_Int(pwm_chip* chip, unsigned int int_time); - -void PWM_Update(pwm_chip* chip, int **buf, int length); - - -/** - * PWM_Init(): Initialize the PWM audio emulator. - */ -void PWM_Init(pwm_chip* chip) -{ - chip->PWM_Mode = 0; - chip->PWM_Out_R = 0; - chip->PWM_Out_L = 0; - - memset(chip->PWM_FIFO_R, 0x00, sizeof(chip->PWM_FIFO_R)); - memset(chip->PWM_FIFO_L, 0x00, sizeof(chip->PWM_FIFO_L)); - - chip->PWM_RP_R = 0; - chip->PWM_WP_R = 0; - chip->PWM_RP_L = 0; - chip->PWM_WP_L = 0; - chip->PWM_Cycle_Tmp = 0; - chip->PWM_Int_Tmp = 0; - chip->PWM_FIFO_L_Tmp = 0; - chip->PWM_FIFO_R_Tmp = 0; - - //PWM_Loudness = 0; - PWM_Set_Cycle(chip, 0); - PWM_Set_Int(chip, 0); -} - - -#if CHILLY_WILLY_SCALE -// TODO: Fix Chilly Willy's new scaling algorithm. -void PWM_Recalc_Scale(pwm_chip* chip) -{ - chip->PWM_Offset = (chip->PWM_Cycle / 2) + 1; - chip->PWM_Scale = 0x7FFF00 / chip->PWM_Offset; -} -#endif - - -void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle) -{ - cycle--; - chip->PWM_Cycle = (cycle & 0xFFF); - chip->PWM_Cycle_Cnt = chip->PWM_Cycles; - -#if CHILLY_WILLY_SCALE - // TODO: Fix Chilly Willy's new scaling algorithm. - PWM_Recalc_Scale(chip); -#endif -} - - -void PWM_Set_Int(pwm_chip* chip, unsigned int int_time) -{ - int_time &= 0x0F; - if (int_time) - chip->PWM_Int = chip->PWM_Int_Cnt = int_time; - else - chip->PWM_Int = chip->PWM_Int_Cnt = 16; -} - - -void PWM_Clear_Timer(pwm_chip* chip) -{ - chip->PWM_Cycle_Cnt = 0; -} - - -/** - * PWM_SHIFT(): Shift PWM data. - * @param src: Channel (L or R) with the source data. - * @param dest Channel (L or R) for the destination. - */ -#define PWM_SHIFT(src, dest) \ -{ \ - /* Make sure the source FIFO isn't empty. */ \ - if (PWM_RP_##src != PWM_WP_##src) \ - { \ - /* Get destination channel output from the source channel FIFO. */ \ - PWM_Out_##dest = PWM_FIFO_##src[PWM_RP_##src]; \ - \ - /* Increment the source channel read pointer, resetting to 0 if it overflows. */ \ - PWM_RP_##src = (PWM_RP_##src + 1) & (PWM_BUF_SIZE - 1); \ - } \ -} - - -/*static void PWM_Shift_Data(void) -{ - switch (PWM_Mode & 0x0F) - { - case 0x01: - case 0x0D: - // Rx_LL: Right -> Ignore, Left -> Left - PWM_SHIFT(L, L); - break; - - case 0x02: - case 0x0E: - // Rx_LR: Right -> Ignore, Left -> Right - PWM_SHIFT(L, R); - break; - - case 0x04: - case 0x07: - // RL_Lx: Right -> Left, Left -> Ignore - PWM_SHIFT(R, L); - break; - - case 0x05: - case 0x09: - // RR_LL: Right -> Right, Left -> Left - PWM_SHIFT(L, L); - PWM_SHIFT(R, R); - break; - - case 0x06: - case 0x0A: - // RL_LR: Right -> Left, Left -> Right - PWM_SHIFT(L, R); - PWM_SHIFT(R, L); - break; - - case 0x08: - case 0x0B: - // RR_Lx: Right -> Right, Left -> Ignore - PWM_SHIFT(R, R); - break; - - case 0x00: - case 0x03: - case 0x0C: - case 0x0F: - default: - // Rx_Lx: Right -> Ignore, Left -> Ignore - break; - } -} - - -void PWM_Update_Timer(unsigned int cycle) -{ - // Don't do anything if PWM is disabled in the Sound menu. - - // Don't do anything if PWM isn't active. - if ((PWM_Mode & 0x0F) == 0x00) - return; - - if (PWM_Cycle == 0x00 || (PWM_Cycle_Cnt > cycle)) - return; - - PWM_Shift_Data(); - - PWM_Cycle_Cnt += PWM_Cycle; - - PWM_Int_Cnt--; - if (PWM_Int_Cnt == 0) - { - PWM_Int_Cnt = PWM_Int; - - if (PWM_Mode & 0x0080) - { - // RPT => generate DREQ1 as well as INT - SH2_DMA1_Request(&M_SH2, 1); - SH2_DMA1_Request(&S_SH2, 1); - } - - if (_32X_MINT & 1) - SH2_Interrupt(&M_SH2, 6); - if (_32X_SINT & 1) - SH2_Interrupt(&S_SH2, 6); - } + +#define CHILLY_WILLY_SCALE 1 + +#if PWM_BUF_SIZE == 8 +unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] = +{ + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, +}; +#elif PWM_BUF_SIZE == 4 +unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] = +{ + 0x40, 0x00, 0x00, 0x80, + 0x80, 0x40, 0x00, 0x00, + 0x00, 0x80, 0x40, 0x00, + 0x00, 0x00, 0x80, 0x40, +}; +#else +#error PWM_BUF_SIZE must equal 4 or 8. +#endif /* PWM_BUF_SIZE */ + +typedef struct _pwm_chip +{ + unsigned short PWM_FIFO_R[8]; + unsigned short PWM_FIFO_L[8]; + unsigned int PWM_RP_R; + unsigned int PWM_WP_R; + unsigned int PWM_RP_L; + unsigned int PWM_WP_L; + unsigned int PWM_Cycles; + unsigned int PWM_Cycle; + unsigned int PWM_Cycle_Cnt; + unsigned int PWM_Int; + unsigned int PWM_Int_Cnt; + unsigned int PWM_Mode; + //unsigned int PWM_Enable; + unsigned int PWM_Out_R; + unsigned int PWM_Out_L; + + unsigned int PWM_Cycle_Tmp; + unsigned int PWM_Cycles_Tmp; + unsigned int PWM_Int_Tmp; + unsigned int PWM_FIFO_L_Tmp; + unsigned int PWM_FIFO_R_Tmp; + +#if CHILLY_WILLY_SCALE +// TODO: Fix Chilly Willy's new scaling algorithm. + /* PWM scaling variables. */ + int PWM_Offset; + int PWM_Scale; + //int PWM_Loudness; +#endif + + int clock; +} pwm_chip; +#if CHILLY_WILLY_SCALE +// TODO: Fix Chilly Willy's new scaling algorithm. +#define PWM_Loudness 0 +#endif + +void PWM_Init(pwm_chip* chip); +void PWM_Recalc_Scale(pwm_chip* chip); + +void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle); +void PWM_Set_Int(pwm_chip* chip, unsigned int int_time); + +void PWM_Update(pwm_chip* chip, int **buf, int length); + + +/** + * PWM_Init(): Initialize the PWM audio emulator. + */ +void PWM_Init(pwm_chip* chip) +{ + chip->PWM_Mode = 0; + chip->PWM_Out_R = 0; + chip->PWM_Out_L = 0; + + memset(chip->PWM_FIFO_R, 0x00, sizeof(chip->PWM_FIFO_R)); + memset(chip->PWM_FIFO_L, 0x00, sizeof(chip->PWM_FIFO_L)); + + chip->PWM_RP_R = 0; + chip->PWM_WP_R = 0; + chip->PWM_RP_L = 0; + chip->PWM_WP_L = 0; + chip->PWM_Cycle_Tmp = 0; + chip->PWM_Int_Tmp = 0; + chip->PWM_FIFO_L_Tmp = 0; + chip->PWM_FIFO_R_Tmp = 0; + + //PWM_Loudness = 0; + PWM_Set_Cycle(chip, 0); + PWM_Set_Int(chip, 0); +} + + +#if CHILLY_WILLY_SCALE +// TODO: Fix Chilly Willy's new scaling algorithm. +void PWM_Recalc_Scale(pwm_chip* chip) +{ + chip->PWM_Offset = (chip->PWM_Cycle / 2) + 1; + chip->PWM_Scale = 0x7FFF00 / chip->PWM_Offset; +} +#endif + + +void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle) +{ + cycle--; + chip->PWM_Cycle = (cycle & 0xFFF); + chip->PWM_Cycle_Cnt = chip->PWM_Cycles; + +#if CHILLY_WILLY_SCALE + // TODO: Fix Chilly Willy's new scaling algorithm. + PWM_Recalc_Scale(chip); +#endif +} + + +void PWM_Set_Int(pwm_chip* chip, unsigned int int_time) +{ + int_time &= 0x0F; + if (int_time) + chip->PWM_Int = chip->PWM_Int_Cnt = int_time; + else + chip->PWM_Int = chip->PWM_Int_Cnt = 16; +} + + +void PWM_Clear_Timer(pwm_chip* chip) +{ + chip->PWM_Cycle_Cnt = 0; +} + + +/** + * PWM_SHIFT(): Shift PWM data. + * @param src: Channel (L or R) with the source data. + * @param dest Channel (L or R) for the destination. + */ +#define PWM_SHIFT(src, dest) \ +{ \ + /* Make sure the source FIFO isn't empty. */ \ + if (PWM_RP_##src != PWM_WP_##src) \ + { \ + /* Get destination channel output from the source channel FIFO. */ \ + PWM_Out_##dest = PWM_FIFO_##src[PWM_RP_##src]; \ + \ + /* Increment the source channel read pointer, resetting to 0 if it overflows. */ \ + PWM_RP_##src = (PWM_RP_##src + 1) & (PWM_BUF_SIZE - 1); \ + } \ +} + + +/*static void PWM_Shift_Data(void) +{ + switch (PWM_Mode & 0x0F) + { + case 0x01: + case 0x0D: + // Rx_LL: Right -> Ignore, Left -> Left + PWM_SHIFT(L, L); + break; + + case 0x02: + case 0x0E: + // Rx_LR: Right -> Ignore, Left -> Right + PWM_SHIFT(L, R); + break; + + case 0x04: + case 0x07: + // RL_Lx: Right -> Left, Left -> Ignore + PWM_SHIFT(R, L); + break; + + case 0x05: + case 0x09: + // RR_LL: Right -> Right, Left -> Left + PWM_SHIFT(L, L); + PWM_SHIFT(R, R); + break; + + case 0x06: + case 0x0A: + // RL_LR: Right -> Left, Left -> Right + PWM_SHIFT(L, R); + PWM_SHIFT(R, L); + break; + + case 0x08: + case 0x0B: + // RR_Lx: Right -> Right, Left -> Ignore + PWM_SHIFT(R, R); + break; + + case 0x00: + case 0x03: + case 0x0C: + case 0x0F: + default: + // Rx_Lx: Right -> Ignore, Left -> Ignore + break; + } +} + + +void PWM_Update_Timer(unsigned int cycle) +{ + // Don't do anything if PWM is disabled in the Sound menu. + + // Don't do anything if PWM isn't active. + if ((PWM_Mode & 0x0F) == 0x00) + return; + + if (PWM_Cycle == 0x00 || (PWM_Cycle_Cnt > cycle)) + return; + + PWM_Shift_Data(); + + PWM_Cycle_Cnt += PWM_Cycle; + + PWM_Int_Cnt--; + if (PWM_Int_Cnt == 0) + { + PWM_Int_Cnt = PWM_Int; + + if (PWM_Mode & 0x0080) + { + // RPT => generate DREQ1 as well as INT + SH2_DMA1_Request(&M_SH2, 1); + SH2_DMA1_Request(&S_SH2, 1); + } + + if (_32X_MINT & 1) + SH2_Interrupt(&M_SH2, 6); + if (_32X_SINT & 1) + SH2_Interrupt(&S_SH2, 6); + } }*/ @@ -283,162 +279,168 @@ INLINE int PWM_Update_Scale(pwm_chip* chip, int PWM_In) { if (PWM_In == 0) return 0; - - // TODO: Chilly Willy's new scaling algorithm breaks drx's Sonic 1 32X (with PWM drums). -#ifdef CHILLY_WILLY_SCALE - //return (((PWM_In & 0xFFF) - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness); - // Knuckles' Chaotix: Tachy Touch uses the values 0xF?? for negative values - // This small modification fixes the terrible pops. - PWM_In &= 0xFFF; - if (PWM_In & 0x800) - PWM_In |= ~0xFFF; - return ((PWM_In - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness); -#else - const int PWM_adjust = ((chip->PWM_Cycle >> 1) + 1); - int PWM_Ret = ((chip->PWM_In & 0xFFF) - PWM_adjust); - - // Increase PWM volume so it's audible. - PWM_Ret <<= (5+2); - - // Make sure the PWM isn't oversaturated. - if (PWM_Ret > 32767) - PWM_Ret = 32767; - else if (PWM_Ret < -32768) - PWM_Ret = -32768; - - return PWM_Ret; -#endif -} - - -void PWM_Update(pwm_chip* chip, int **buf, int length) -{ - int tmpOutL; - int tmpOutR; - int i; - - //if (!PWM_Enable) - // return; - - if (chip->PWM_Out_L == 0 && chip->PWM_Out_R == 0) - { - memset(buf[0], 0x00, length * sizeof(int)); - memset(buf[1], 0x00, length * sizeof(int)); - return; - } - - // New PWM scaling algorithm provided by Chilly Willy on the Sonic Retro forums. - tmpOutL = PWM_Update_Scale(chip, (int)chip->PWM_Out_L); - tmpOutR = PWM_Update_Scale(chip, (int)chip->PWM_Out_R); - - for (i = 0; i < length; i ++) - { - buf[0][i] = tmpOutL; - buf[1][i] = tmpOutR; - } -} - - -void pwm_update(void *_chip, stream_sample_t **outputs, int samples) -{ - pwm_chip *chip = (pwm_chip *) _chip; - - PWM_Update(chip, outputs, samples); -} - -void * device_start_pwm(int clock) -{ - /* allocate memory for the chip */ - //pwm_state *chip = get_safe_token(device); - pwm_chip *chip; - int rate; - - chip = (pwm_chip *) malloc(sizeof(pwm_chip)); - if (!chip) return chip; - - rate = 22020; // that's the rate the PWM is mostly used - chip->clock = clock; - - PWM_Init(chip); - /* allocate the stream */ - //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); - - return chip; -} - -void device_stop_pwm(void *chip) -{ - //pwm_chip *chip = &PWM_Chip[ChipID]; - //free(chip->ram); - free(chip); -} - -void device_reset_pwm(void *_chip) -{ - pwm_chip *chip = (pwm_chip *) _chip; - PWM_Init(chip); -} - -void pwm_chn_w(void *_chip, UINT8 Channel, UINT16 data) -{ - pwm_chip *chip = (pwm_chip *) _chip; - - if (chip->clock == 1) - { // old-style commands - switch(Channel) - { - case 0x00: - chip->PWM_Out_L = data; - break; - case 0x01: - chip->PWM_Out_R = data; - break; - case 0x02: - PWM_Set_Cycle(chip, data); - break; - case 0x03: - chip->PWM_Out_L = data; - chip->PWM_Out_R = data; - break; - } - } - else - { - switch(Channel) - { - case 0x00/2: // control register - PWM_Set_Int(chip, data >> 8); - break; - case 0x02/2: // cycle register - PWM_Set_Cycle(chip, data); - break; - case 0x04/2: // l ch - chip->PWM_Out_L = data; - break; - case 0x06/2: // r ch - chip->PWM_Out_R = data; - if (! chip->PWM_Mode) - { - if (chip->PWM_Out_L == chip->PWM_Out_R) - { - // fixes these terrible pops when - // starting/stopping/pausing the song - chip->PWM_Offset = data; - chip->PWM_Mode = 0x01; - } - } - break; - case 0x08/2: // mono ch - chip->PWM_Out_L = data; - chip->PWM_Out_R = data; - if (! chip->PWM_Mode) - { - chip->PWM_Offset = data; - chip->PWM_Mode = 0x01; - } - break; - } - } - - return; -} + + // TODO: Chilly Willy's new scaling algorithm breaks drx's Sonic 1 32X (with PWM drums). +#ifdef CHILLY_WILLY_SCALE + //return (((PWM_In & 0xFFF) - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness); + // Knuckles' Chaotix: Tachy Touch uses the values 0xF?? for negative values + // This small modification fixes the terrible pops. + PWM_In &= 0xFFF; + if (PWM_In & 0x800) + PWM_In |= ~0xFFF; + return ((PWM_In - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness); +#else + const int PWM_adjust = ((chip->PWM_Cycle >> 1) + 1); + int PWM_Ret = ((chip->PWM_In & 0xFFF) - PWM_adjust); + + // Increase PWM volume so it's audible. + PWM_Ret <<= (5+2); + + // Make sure the PWM isn't oversaturated. + if (PWM_Ret > 32767) + PWM_Ret = 32767; + else if (PWM_Ret < -32768) + PWM_Ret = -32768; + + return PWM_Ret; +#endif +} + + +void PWM_Update(pwm_chip* chip, int **buf, int length) +{ + int tmpOutL; + int tmpOutR; + int i; + + //if (!PWM_Enable) + // return; + + if (chip->PWM_Out_L == 0 && chip->PWM_Out_R == 0) + { + memset(buf[0], 0x00, length * sizeof(int)); + memset(buf[1], 0x00, length * sizeof(int)); + return; + } + + // New PWM scaling algorithm provided by Chilly Willy on the Sonic Retro forums. + tmpOutL = PWM_Update_Scale(chip, (int)chip->PWM_Out_L); + tmpOutR = PWM_Update_Scale(chip, (int)chip->PWM_Out_R); + + for (i = 0; i < length; i ++) + { + buf[0][i] = tmpOutL; + buf[1][i] = tmpOutR; + } +} + + +void pwm_update(void *_info, stream_sample_t **outputs, int samples) +{ + pwm_chip *chip = (pwm_chip *)_info; + + PWM_Update(chip, outputs, samples); +} + +int device_start_pwm(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + /* allocate memory for the chip */ + //pwm_state *chip = get_safe_token(device); + pwm_chip *chip; + int rate; + + chip = (pwm_chip *) calloc(1, sizeof(pwm_chip)); + *_info = (void *) chip; + + rate = 22020; // that's the rate the PWM is mostly used + if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + chip->clock = clock; + + PWM_Init(chip); + /* allocate the stream */ + //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); + + return rate; +} + +void device_stop_pwm(void *_info) +{ + //pwm_chip *chip = &PWM_Chip[ChipID]; + //free(chip->ram); + + free(_info); + + return; +} + +void device_reset_pwm(void *_info) +{ + pwm_chip *chip = (pwm_chip *)_info; + PWM_Init(chip); +} + +void pwm_chn_w(void *_info, UINT8 Channel, UINT16 data) +{ + pwm_chip *chip = (pwm_chip *)_info; + + if (chip->clock == 1) + { // old-style commands + switch(Channel) + { + case 0x00: + chip->PWM_Out_L = data; + break; + case 0x01: + chip->PWM_Out_R = data; + break; + case 0x02: + PWM_Set_Cycle(chip, data); + break; + case 0x03: + chip->PWM_Out_L = data; + chip->PWM_Out_R = data; + break; + } + } + else + { + switch(Channel) + { + case 0x00/2: // control register + PWM_Set_Int(chip, data >> 8); + break; + case 0x02/2: // cycle register + PWM_Set_Cycle(chip, data); + break; + case 0x04/2: // l ch + chip->PWM_Out_L = data; + break; + case 0x06/2: // r ch + chip->PWM_Out_R = data; + if (! chip->PWM_Mode) + { + if (chip->PWM_Out_L == chip->PWM_Out_R) + { + // fixes these terrible pops when + // starting/stopping/pausing the song + chip->PWM_Offset = data; + chip->PWM_Mode = 0x01; + } + } + break; + case 0x08/2: // mono ch + chip->PWM_Out_L = data; + chip->PWM_Out_R = data; + if (! chip->PWM_Mode) + { + chip->PWM_Offset = data; + chip->PWM_Mode = 0x01; + } + break; + } + } + + return; +} diff --git a/Frameworks/GME/gme/pwm.h b/Frameworks/GME/vgmplay/chips/pwm.h similarity index 93% rename from Frameworks/GME/gme/pwm.h rename to Frameworks/GME/vgmplay/chips/pwm.h index 98ccdbc7d..864467b25 100644 --- a/Frameworks/GME/gme/pwm.h +++ b/Frameworks/GME/vgmplay/chips/pwm.h @@ -1,71 +1,61 @@ -/*************************************************************************** - * Gens: PWM audio emulator. * - * * - * Copyright (c) 1999-2002 by Stéphane Dallongeville * - * Copyright (c) 2003-2004 by Stéphane Akhoun * - * Copyright (c) 2008 by David Korth * - * * - * This program is free software; you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the * - * Free Software Foundation; either version 2 of the License, or (at your * - * option) any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, write to the Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#define PWM_BUF_SIZE 4 - -#include "mamedef.h" - -/*extern unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE]; - -extern unsigned short PWM_FIFO_R[8]; -extern unsigned short PWM_FIFO_L[8]; -extern unsigned int PWM_RP_R; -extern unsigned int PWM_WP_R; -extern unsigned int PWM_RP_L; -extern unsigned int PWM_WP_L; -extern unsigned int PWM_Cycles; -extern unsigned int PWM_Cycle; -extern unsigned int PWM_Cycle_Cnt; -extern unsigned int PWM_Int; -extern unsigned int PWM_Int_Cnt; -extern unsigned int PWM_Mode; -extern unsigned int PWM_Enable; -extern unsigned int PWM_Out_R; -extern unsigned int PWM_Out_L;*/ - -//void PWM_Init(void); -//void PWM_Recalc_Scale(void); - -/* Functions called by x86 asm. */ -//void PWM_Set_Cycle(unsigned int cycle); -//void PWM_Set_Int(unsigned int int_time); - -/* Functions called by C/C++ code only. */ -//void PWM_Clear_Timer(void); -//void PWM_Update_Timer(unsigned int cycle); -//void PWM_Update(int **buf, int length); - -#ifdef __cplusplus -extern "C" { -#endif - -void pwm_update(void *chip, stream_sample_t **outputs, int samples); - -void * device_start_pwm(int clock); -void device_stop_pwm(void *chip); -void device_reset_pwm(void *chip); - -void pwm_chn_w(void *chip, UINT8 Channel, UINT16 data); - -#ifdef __cplusplus -} -#endif +/*************************************************************************** + * Gens: PWM audio emulator. * + * * + * Copyright (c) 1999-2002 by Stéphane Dallongeville * + * Copyright (c) 2003-2004 by Stéphane Akhoun * + * Copyright (c) 2008 by David Korth * + * * + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#define PWM_BUF_SIZE 4 +/*extern unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE]; + +extern unsigned short PWM_FIFO_R[8]; +extern unsigned short PWM_FIFO_L[8]; +extern unsigned int PWM_RP_R; +extern unsigned int PWM_WP_R; +extern unsigned int PWM_RP_L; +extern unsigned int PWM_WP_L; +extern unsigned int PWM_Cycles; +extern unsigned int PWM_Cycle; +extern unsigned int PWM_Cycle_Cnt; +extern unsigned int PWM_Int; +extern unsigned int PWM_Int_Cnt; +extern unsigned int PWM_Mode; +extern unsigned int PWM_Enable; +extern unsigned int PWM_Out_R; +extern unsigned int PWM_Out_L;*/ + +//void PWM_Init(void); +//void PWM_Recalc_Scale(void); + +/* Functions called by x86 asm. */ +//void PWM_Set_Cycle(unsigned int cycle); +//void PWM_Set_Int(unsigned int int_time); + +/* Functions called by C/C++ code only. */ +//void PWM_Clear_Timer(void); +//void PWM_Update_Timer(unsigned int cycle); +//void PWM_Update(int **buf, int length); + + +void pwm_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_pwm(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_pwm(void *chip); +void device_reset_pwm(void *chip); + +void pwm_chn_w(void *chip, UINT8 Channel, UINT16 data); diff --git a/Frameworks/GME/vgmplay/chips/qsound.c b/Frameworks/GME/vgmplay/chips/qsound.c new file mode 100644 index 000000000..fca0a263d --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/qsound.c @@ -0,0 +1,483 @@ +/*************************************************************************** + + Capcom System QSound(tm) + ======================== + + Driver by Paul Leaman (paul@vortexcomputing.demon.co.uk) + and Miguel Angel Horna (mahorna@teleline.es) + + A 16 channel stereo sample player. + + QSpace position is simulated by panning the sound in the stereo space. + + Register + 0 xxbb xx = unknown bb = start high address + 1 ssss ssss = sample start address + 2 pitch + 3 unknown (always 0x8000) + 4 loop offset from end address + 5 end + 6 master channel volume + 7 not used + 8 Balance (left=0x0110 centre=0x0120 right=0x0130) + 9 unknown (most fixed samples use 0 for this register) + + Many thanks to CAB (the author of Amuse), without whom this probably would + never have been finished. + + If anybody has some information about this hardware, please send it to me + to mahorna@teleline.es or 432937@cepsz.unizar.es. + http://teleline.terra.es/personal/mahorna + +***************************************************************************/ + +//#include "emu.h" +#include "mamedef.h" +#ifdef _DEBUG +#include +#endif +#include +#include +#include +#include "qsound.h" + +#define NULL ((void *)0) + +/* +Debug defines +*/ +#define LOG_WAVE 0 +#define VERBOSE 0 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +/* 8 bit source ROM samples */ +typedef INT8 QSOUND_SRC_SAMPLE; + + +#define QSOUND_CLOCKDIV 166 /* Clock divider */ +#define QSOUND_CHANNELS 16 +typedef stream_sample_t QSOUND_SAMPLE; + +struct QSOUND_CHANNEL +{ + UINT32 bank; // bank + UINT32 address; // start/cur address + UINT16 loop; // loop address + UINT16 end; // end address + UINT32 freq; // frequency + UINT16 vol; // master volume + + // work variables + UINT8 enabled; // key on / key off + int lvol; // left volume + int rvol; // right volume + UINT32 step_ptr; // current offset counter + + UINT8 Muted; +}; + +typedef struct _qsound_state qsound_state; +struct _qsound_state +{ + /* Private variables */ + //sound_stream * stream; /* Audio stream */ + struct QSOUND_CHANNEL channel[QSOUND_CHANNELS]; + + UINT16 data; /* register latch data */ + QSOUND_SRC_SAMPLE *sample_rom; /* Q sound sample ROM */ + UINT32 sample_rom_length; + + int pan_table[33]; /* Pan volume table */ + + //FILE *fpRawDataL; + //FILE *fpRawDataR; +}; + +/*INLINE qsound_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == QSOUND); + return (qsound_state *)downcast(device)->token(); +}*/ + + +/* Function prototypes */ +//static STREAM_UPDATE( qsound_update ); +static void qsound_set_command(qsound_state *chip, UINT8 address, UINT16 data); + +//static DEVICE_START( qsound ) +int device_start_qsound(void **_info, int clock) +{ + //qsound_state *chip = get_safe_token(device); + qsound_state *chip; + int i; + + chip = (qsound_state *) calloc(1, sizeof(qsound_state)); + *_info = (void *) chip; + + //chip->sample_rom = (QSOUND_SRC_SAMPLE *)*device->region(); + //chip->sample_rom_length = device->region()->bytes(); + chip->sample_rom = NULL; + chip->sample_rom_length = 0x00; + + /* Create pan table */ + for (i=0; i<33; i++) + chip->pan_table[i]=(int)((256/sqrt(32.0)) * sqrt((double)i)); + + // init sound regs + memset(chip->channel, 0, sizeof(chip->channel)); + +// LOG(("Pan table\n")); +// for (i=0; i<33; i++) +// LOG(("%02x ", chip->pan_table[i])); + + /* Allocate stream */ + /*chip->stream = device->machine().sound().stream_alloc( + *device, 0, 2, + device->clock() / QSOUND_CLOCKDIV, + chip, + qsound_update );*/ + + /*if (LOG_WAVE) + { + chip->fpRawDataR=fopen("qsoundr.raw", "w+b"); + chip->fpRawDataL=fopen("qsoundl.raw", "w+b"); + }*/ + + /* state save */ + /*for (i=0; isave_item(NAME(chip->channel[i].bank), i); + device->save_item(NAME(chip->channel[i].address), i); + device->save_item(NAME(chip->channel[i].pitch), i); + device->save_item(NAME(chip->channel[i].loop), i); + device->save_item(NAME(chip->channel[i].end), i); + device->save_item(NAME(chip->channel[i].vol), i); + device->save_item(NAME(chip->channel[i].pan), i); + device->save_item(NAME(chip->channel[i].key), i); + device->save_item(NAME(chip->channel[i].lvol), i); + device->save_item(NAME(chip->channel[i].rvol), i); + device->save_item(NAME(chip->channel[i].lastdt), i); + device->save_item(NAME(chip->channel[i].offset), i); + }*/ + + for (i = 0; i < QSOUND_CHANNELS; i ++) + chip->channel[i].Muted = 0x00; + + return clock / QSOUND_CLOCKDIV; +} + +//static DEVICE_STOP( qsound ) +void device_stop_qsound(void *_info) +{ + //qsound_state *chip = get_safe_token(device); + qsound_state *chip = (qsound_state *)_info; + /*if (chip->fpRawDataR) + { + fclose(chip->fpRawDataR); + } + chip->fpRawDataR = NULL; + if (chip->fpRawDataL) + { + fclose(chip->fpRawDataL); + } + chip->fpRawDataL = NULL;*/ + free(chip->sample_rom); chip->sample_rom = NULL; + free(chip); +} + +void device_reset_qsound(void *_info) +{ + qsound_state *chip = (qsound_state *)_info; + int adr; + + // init sound regs + memset(chip->channel, 0, sizeof(chip->channel)); + + for (adr = 0x7f; adr >= 0; adr--) + qsound_set_command(chip, adr, 0); + for (adr = 0x80; adr < 0x90; adr++) + qsound_set_command(chip, adr, 0x120); + + return; +} + +//WRITE8_DEVICE_HANDLER( qsound_w ) +void qsound_w(void *_info, offs_t offset, UINT8 data) +{ + //qsound_state *chip = get_safe_token(device); + qsound_state *chip = (qsound_state *)_info; + switch (offset) + { + case 0: + chip->data=(chip->data&0xff)|(data<<8); + break; + + case 1: + chip->data=(chip->data&0xff00)|data; + break; + + case 2: + qsound_set_command(chip, data, chip->data); + break; + + default: + //logerror("%s: unexpected qsound write to offset %d == %02X\n", device->machine().describe_context(), offset, data); + logerror("QSound: unexpected qsound write to offset %d == %02X\n", offset, data); + break; + } +} + +//READ8_DEVICE_HANDLER( qsound_r ) +UINT8 qsound_r(void *chip, offs_t offset) +{ + /* Port ready bit (0x80 if ready) */ + return 0x80; +} + +static void qsound_set_command(qsound_state *chip, UINT8 address, UINT16 data) +{ + int ch = 0, reg = 0; + + // direct sound reg + if (address < 0x80) + { + ch = address >> 3; + reg = address & 0x07; + } + // >= 0x80 is probably for the dsp? + else if (address < 0x90) + { + ch = address & 0x0F; + reg = 8; + } + else if (address >= 0xba && address < 0xca) + { + ch = address - 0xba; + reg=9; + } + else + { + /* Unknown registers */ + ch = 99; + reg = 99; + } + + switch (reg) + { + case 0: + // bank, high bits unknown + ch = (ch + 1) & 0x0f; /* strange ... */ + chip->channel[ch].bank = (data & 0x7f) << 16; // Note: The most recent MAME doesn't do "& 0x7F" +//#ifdef _DEBUG + if (data && !(data & 0x8000)) + printf("QSound Ch %u: Bank = %04x\n",ch,data); +//#endif + break; + case 1: + // start/cur address + chip->channel[ch].address = data; + break; + case 2: + // frequency + chip->channel[ch].freq = data; + // This was working with the old code, but breaks the songs with the new one. + // And I'm pretty sure the hardware won't do this. -Valley Bell + /*if (!data) + { + // key off + chip->channel[ch].enabled = 0; + }*/ + break; + case 3: +//#ifdef _DEBUG + if (chip->channel[ch].enabled && data != 0x8000) + printf("QSound Ch %u: KeyOn = %04x\n",ch,data); +//#endif + // key on (does the value matter? it always writes 0x8000) + //chip->channel[ch].enabled = 1; + chip->channel[ch].enabled = (data & 0x8000) >> 15; + chip->channel[ch].step_ptr = 0; + break; + case 4: + // loop address + chip->channel[ch].loop = data; + break; + case 5: + // end address + chip->channel[ch].end = data; + break; + case 6: + // master volume + if (! chip->channel[ch].enabled && data) + printf("QSound update warning - please report!\n"); + chip->channel[ch].vol = data; + break; + case 7: + // unused? +#ifdef MAME_DEBUG + popmessage("UNUSED QSOUND REG 7=%04x",data); +#endif + break; + case 8: + { + // panning (left=0x0110, centre=0x0120, right=0x0130) + // looks like it doesn't write other values than that + int pan = (data & 0x3f) - 0x10; + if (pan > 0x20) + pan = 0x20; + if (pan < 0) + pan = 0; + + chip->channel[ch].rvol=chip->pan_table[pan]; + chip->channel[ch].lvol=chip->pan_table[0x20 - pan]; + } + break; + case 9: + // unknown +/* +#ifdef MAME_DEBUG + popmessage("QSOUND REG 9=%04x",data); +#endif +*/ + break; + default: + //logerror("%s: write_data %02x = %04x\n", machine().describe_context(), address, data); + break; + } + //LOG(("QSOUND WRITE %02x CH%02d-R%02d =%04x\n", address, ch, reg, data)); +} + + +//static STREAM_UPDATE( qsound_update ) +void qsound_update(void *param, stream_sample_t **outputs, int samples) +{ + qsound_state *chip = (qsound_state *)param; + int i,j; + UINT32 offset; + UINT32 advance; + INT8 sample; + struct QSOUND_CHANNEL *pC=&chip->channel[0]; + + memset( outputs[0], 0x00, samples * sizeof(*outputs[0]) ); + memset( outputs[1], 0x00, samples * sizeof(*outputs[1]) ); + if (! chip->sample_rom_length) + return; + + for (i=0; ienabled && ! pC->Muted) + { + QSOUND_SAMPLE *pOutL=outputs[0]; + QSOUND_SAMPLE *pOutR=outputs[1]; + + for (j=samples-1; j>=0; j--) + { + advance = (pC->step_ptr >> 12); + pC->step_ptr &= 0xfff; + pC->step_ptr += pC->freq; + + if (advance) + { + pC->address += advance; + if (pC->freq && pC->address >= pC->end) + { + if (pC->loop) + { + // Reached the end, restart the loop + pC->address -= pC->loop; + + // Make sure we don't overflow (what does the real chip do in this case?) + if (pC->address >= pC->end) + pC->address = pC->end - pC->loop; + + pC->address &= 0xffff; + } + else + { + // Reached the end of a non-looped sample + //pC->enabled = 0; + pC->address --; // ensure that old ripped VGMs still work + pC->step_ptr += 0x1000; + break; + } + } + } + + offset = (pC->bank | pC->address) % chip->sample_rom_length; + sample = chip->sample_rom[offset]; + *pOutL++ += ((sample * pC->lvol * pC->vol) >> 14); + *pOutR++ += ((sample * pC->rvol * pC->vol) >> 14); + } + } + } + + /*if (chip->fpRawDataL) + fwrite(outputs[0], samples*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataL); + if (chip->fpRawDataR) + fwrite(outputs[1], samples*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataR);*/ +} + +void qsound_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + qsound_state* info = (qsound_state *)_info; + + if (info->sample_rom_length != ROMSize) + { + info->sample_rom = (QSOUND_SRC_SAMPLE*)realloc(info->sample_rom, ROMSize); + info->sample_rom_length = ROMSize; + memset(info->sample_rom, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(info->sample_rom + DataStart, ROMData, DataLength); + + return; +} + + +void qsound_set_mute_mask(void *_info, UINT32 MuteMask) +{ + qsound_state* info = (qsound_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < QSOUND_CHANNELS; CurChn ++) + info->channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( qsound ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- // + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(qsound_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- // + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( qsound ); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( qsound ); break; + case DEVINFO_FCT_RESET: // Nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- // + case DEVINFO_STR_NAME: strcpy(info->s, "Q-Sound"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Capcom custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}// + +/**************** end of file ****************/ + +//DEFINE_LEGACY_SOUND_DEVICE(QSOUND, qsound); diff --git a/Frameworks/GME/vgmplay/chips/qsound.h b/Frameworks/GME/vgmplay/chips/qsound.h new file mode 100644 index 000000000..70edd60b1 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/qsound.h @@ -0,0 +1,29 @@ +/********************************************************* + + Capcom Q-Sound system + +*********************************************************/ + +#pragma once + +//#include "devlegcy.h" + +#define QSOUND_CLOCK 4000000 /* default 4MHz clock */ + +void qsound_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_qsound(void **chip, int clock); +void device_stop_qsound(void *chip); +void device_reset_qsound(void *chip); + + +//WRITE8_DEVICE_HANDLER( qsound_w ); +//READ8_DEVICE_HANDLER( qsound_r ); +void qsound_w(void *chip, offs_t offset, UINT8 data); +UINT8 qsound_r(void *chip, offs_t offset); + + +void qsound_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); +void qsound_set_mute_mask(void *chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(QSOUND, qsound); diff --git a/Frameworks/GME/gme/rf5c68.c b/Frameworks/GME/vgmplay/chips/rf5c68.c similarity index 80% rename from Frameworks/GME/gme/rf5c68.c rename to Frameworks/GME/vgmplay/chips/rf5c68.c index 92c56ca8f..672017013 100644 --- a/Frameworks/GME/gme/rf5c68.c +++ b/Frameworks/GME/vgmplay/chips/rf5c68.c @@ -1,448 +1,474 @@ -/*********************************************************/ -/* ricoh RF5C68(or clone) PCM controller */ -/*********************************************************/ - -#include -#include -//#include "sndintrf.h" -//#include "streams.h" -#include "rf5c68.h" -#include - -#undef NULL -#define NULL ((void *)0) - - -#define NUM_CHANNELS (8) -#define WRITES_PER_SAMPLE 12 - - - -typedef struct _pcm_channel pcm_channel; -struct _pcm_channel -{ - UINT8 enable; - UINT8 env; - UINT8 pan; - UINT8 start; - UINT32 addr; - UINT16 step; - UINT16 loopst; - UINT8 Muted; -}; - -typedef struct _mem_stream mem_stream; -struct _mem_stream -{ - UINT32 BaseAddr; - UINT32 EndAddr; - UINT32 CurAddr; - const UINT8* MemPnt; -}; - - -typedef struct _rf5c68_state rf5c68_state; -struct _rf5c68_state -{ - //sound_stream * stream; - pcm_channel chan[NUM_CHANNELS]; - UINT8 cbank; - UINT8 wbank; - UINT8 enable; - UINT32 datasize; - UINT8* data; - //void (*sample_callback)(running_device* device,int channel); - mem_stream memstrm; -}; - - -static void rf5c68_mem_stream_flush(rf5c68_state *chip); - -/*INLINE rf5c68_state *get_safe_token(const device_config *device) -{ - assert(device != NULL); - assert(device->token != NULL); - assert(device->type == SOUND); - assert(sound_get_type(device) == SOUND_RF5C68); - return (rf5c68_state *)device->token; -}*/ - -/************************************************/ -/* RF5C68 stream update */ -/************************************************/ - -static void memstream_sample_check(rf5c68_state *chip, UINT32 addr) -{ - mem_stream* ms = &chip->memstrm; - - if (addr >= ms->CurAddr) - { - if (addr - ms->CurAddr <= WRITES_PER_SAMPLE * 5) - { - ms->CurAddr -= WRITES_PER_SAMPLE * 2; - if (ms->CurAddr < ms->BaseAddr) - ms->CurAddr = ms->BaseAddr; - } - } - else - { - if (ms->CurAddr - addr <= WRITES_PER_SAMPLE * 4) - { - rf5c68_mem_stream_flush(chip); - } - } - - return; -} - -//static STREAM_UPDATE( rf5c68_update ) -void rf5c68_update(void *_chip, stream_sample_t **outputs, int samples) -{ - //rf5c68_state *chip = (rf5c68_state *)param; - rf5c68_state *chip = (rf5c68_state *) _chip; - mem_stream* ms = &chip->memstrm; - stream_sample_t *left = outputs[0]; - stream_sample_t *right = outputs[1]; - int i, j; - - /* start with clean buffers */ - memset(left, 0, samples * sizeof(*left)); - memset(right, 0, samples * sizeof(*right)); - - /* bail if not enabled */ - if (!chip->enable) - return; - - /* loop over channels */ - for (i = 0; i < NUM_CHANNELS; i++) - { - pcm_channel *chan = &chip->chan[i]; - - /* if this channel is active, accumulate samples */ - if (chan->enable && ! chan->Muted) - { - int lv = (chan->pan & 0x0f) * chan->env; - int rv = ((chan->pan >> 4) & 0x0f) * chan->env; - - /* loop over the sample buffer */ - for (j = 0; j < samples; j++) - { - int sample; - - /* trigger sample callback */ - /*if(chip->sample_callback) - { - if(((chan->addr >> 11) & 0xfff) == 0xfff) - chip->sample_callback(chip->device,((chan->addr >> 11)/0x2000)); - }*/ - - memstream_sample_check(chip, (chan->addr >> 11) & 0xffff); - /* fetch the sample and handle looping */ - sample = chip->data[(chan->addr >> 11) & 0xffff]; - if (sample == 0xff) - { - chan->addr = chan->loopst << 11; - sample = chip->data[(chan->addr >> 11) & 0xffff]; - - /* if we loop to a loop point, we're effectively dead */ - if (sample == 0xff) - break; - } - chan->addr += chan->step; - - /* add to the buffer */ - if (sample & 0x80) - { - sample &= 0x7f; - left[j] += (sample * lv) >> 5; - right[j] += (sample * rv) >> 5; - } - else - { - left[j] -= (sample * lv) >> 5; - right[j] -= (sample * rv) >> 5; - } - } - } - } - - if (samples && ms->CurAddr < ms->EndAddr) - { - i = WRITES_PER_SAMPLE * samples; - if (ms->CurAddr + i > ms->EndAddr) - i = ms->EndAddr - ms->CurAddr; - - memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), i); - ms->CurAddr += i; - } - - // I think, this is completely useless - /* now clamp and shift the result (output is only 10 bits) */ - /*for (j = 0; j < samples; j++) - { - stream_sample_t temp; - - temp = left[j]; - if (temp > 32767) temp = 32767; - else if (temp < -32768) temp = -32768; - left[j] = temp & ~0x3f; - - temp = right[j]; - if (temp > 32767) temp = 32767; - else if (temp < -32768) temp = -32768; - right[j] = temp & ~0x3f; - }*/ -} - - -/************************************************/ -/* RF5C68 start */ -/************************************************/ - -//static DEVICE_START( rf5c68 ) -void * device_start_rf5c68() -{ - //const rf5c68_interface* intf = (const rf5c68_interface*)device->baseconfig().static_config(); - - /* allocate memory for the chip */ - //rf5c68_state *chip = get_safe_token(device); - rf5c68_state *chip; - int chn; - - chip = (rf5c68_state *) malloc(sizeof(rf5c68_state)); - if (!chip) return chip; - - chip->datasize = 0x10000; - chip->data = (UINT8*)malloc(chip->datasize); - - /* allocate the stream */ - //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); - - /* set up callback */ - /*if(intf != NULL) - chip->sample_callback = intf->sample_end_callback; - else - chip->sample_callback = NULL;*/ - for (chn = 0; chn < NUM_CHANNELS; chn ++) - chip->chan[chn].Muted = 0x00; - - return chip; -} - -void device_stop_rf5c68(void *_chip) -{ - rf5c68_state *chip = (rf5c68_state *) _chip; - free(chip->data); chip->data = NULL; - free(chip); -} - -void device_reset_rf5c68(void *_chip) -{ - rf5c68_state *chip = (rf5c68_state *) _chip; - int i; - pcm_channel* chan; - mem_stream* ms = &chip->memstrm; - - // Clear the PCM memory. - memset(chip->data, 0x00, chip->datasize); - - chip->enable = 0; - chip->cbank = 0; - chip->wbank = 0; - - /* clear channel registers */ - for (i = 0; i < NUM_CHANNELS; i ++) - { - chan = &chip->chan[i]; - chan->enable = 0; - chan->env = 0; - chan->pan = 0; - chan->start = 0; - chan->addr = 0; - chan->step = 0; - chan->loopst = 0; - } - - ms->BaseAddr = 0x0000; - ms->CurAddr = 0x0000; - ms->EndAddr = 0x0000; - ms->MemPnt = NULL; -} - -/************************************************/ -/* RF5C68 write register */ -/************************************************/ - -//WRITE8_DEVICE_HANDLER( rf5c68_w ) -void rf5c68_w(void *_chip, offs_t offset, UINT8 data) -{ - //rf5c68_state *chip = get_safe_token(device); - rf5c68_state *chip = (rf5c68_state *) _chip; - pcm_channel *chan = &chip->chan[chip->cbank]; - int i; - - /* force the stream to update first */ - //stream_update(chip->stream); - - /* switch off the address */ - switch (offset) - { - case 0x00: /* envelope */ - chan->env = data; - break; - - case 0x01: /* pan */ - chan->pan = data; - break; - - case 0x02: /* FDL */ - chan->step = (chan->step & 0xff00) | (data & 0x00ff); - break; - - case 0x03: /* FDH */ - chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00); - break; - - case 0x04: /* LSL */ - chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff); - break; - - case 0x05: /* LSH */ - chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00); - break; - - case 0x06: /* ST */ - chan->start = data; - if (!chan->enable) - chan->addr = chan->start << (8 + 11); - break; - - case 0x07: /* control reg */ - chip->enable = (data >> 7) & 1; - if (data & 0x40) - chip->cbank = data & 7; - else - chip->wbank = data & 15; - break; - - case 0x08: /* channel on/off reg */ - for (i = 0; i < 8; i++) - { - chip->chan[i].enable = (~data >> i) & 1; - if (!chip->chan[i].enable) - chip->chan[i].addr = chip->chan[i].start << (8 + 11); - } - break; - } -} - - -/************************************************/ -/* RF5C68 read memory */ -/************************************************/ - -//READ8_DEVICE_HANDLER( rf5c68_mem_r ) -UINT8 rf5c68_mem_r(void *_chip, offs_t offset) -{ - //rf5c68_state *chip = get_safe_token(device); - rf5c68_state *chip = (rf5c68_state *) _chip; - return chip->data[chip->wbank * 0x1000 + offset]; -} - - -/************************************************/ -/* RF5C68 write memory */ -/************************************************/ - -//WRITE8_DEVICE_HANDLER( rf5c68_mem_w ) -void rf5c68_mem_w(void *_chip, offs_t offset, UINT8 data) -{ - //rf5c68_state *chip = get_safe_token(device); - rf5c68_state *chip = (rf5c68_state *) _chip; - rf5c68_mem_stream_flush(chip); - chip->data[chip->wbank * 0x1000 | offset] = data; -} - -static void rf5c68_mem_stream_flush(rf5c68_state *chip) -{ - mem_stream* ms = &chip->memstrm; - - if (ms->CurAddr >= ms->EndAddr) - return; - - memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), ms->EndAddr - ms->CurAddr); - ms->CurAddr = ms->EndAddr; - - return; -} - -void rf5c68_write_ram(void *_chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) -{ - rf5c68_state *chip = (rf5c68_state *) _chip; - mem_stream* ms = &chip->memstrm; - UINT16 BytCnt; - - if (DataStart >= chip->datasize) - return; - if (DataStart + DataLength > chip->datasize) - DataLength = chip->datasize - DataStart; - - //memcpy(chip->data + (chip->wbank * 0x1000 | DataStart), RAMData, DataLength); - - rf5c68_mem_stream_flush(chip); - - ms->BaseAddr = chip->wbank * 0x1000 | DataStart; - ms->CurAddr = ms->BaseAddr; - ms->EndAddr = ms->BaseAddr + DataLength; - ms->MemPnt = RAMData; - - BytCnt = WRITES_PER_SAMPLE; - if (ms->CurAddr + BytCnt > ms->EndAddr) - BytCnt = ms->EndAddr - ms->CurAddr; - - memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), BytCnt); - ms->CurAddr += BytCnt; - - return; -} - - -void rf5c68_set_mute_mask(void *_chip, UINT32 MuteMask) -{ - rf5c68_state *chip = (rf5c68_state *) _chip; - unsigned char CurChn; - - for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) - chip->chan[CurChn].Muted = (MuteMask >> CurChn) & 0x01; - - return; -} - - - -/************************************************************************** - * Generic get_info - **************************************************************************/ - -/*DEVICE_GET_INFO( rf5c68 ) -{ - switch (state) - { - // --- the following bits of info are returned as 64-bit signed integers --- - case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(rf5c68_state); break; - - // --- the following bits of info are returned as pointers to data or functions --- - case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( rf5c68 ); break; - case DEVINFO_FCT_STOP: // Nothing break; - case DEVINFO_FCT_RESET: // Nothing break; - - // --- the following bits of info are returned as NULL-terminated strings --- - case DEVINFO_STR_NAME: strcpy(info->s, "RF5C68"); break; - case DEVINFO_STR_FAMILY: strcpy(info->s, "Ricoh PCM"); break; - case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; - case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; - case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; - } -}*/ - -/**************** end of file ****************/ +/*********************************************************/ +/* ricoh RF5C68(or clone) PCM controller */ +/*********************************************************/ + +#include "mamedef.h" +#include +#include +//#include "sndintrf.h" +//#include "streams.h" +#include "rf5c68.h" +#include + +#define NULL ((void *)0) + + +#define NUM_CHANNELS (8) +#define STEAM_STEP 0x800 + + + +typedef struct _pcm_channel pcm_channel; +struct _pcm_channel +{ + UINT8 enable; + UINT8 env; + UINT8 pan; + UINT8 start; + UINT32 addr; + UINT16 step; + UINT16 loopst; + UINT8 Muted; +}; + +typedef struct _mem_stream mem_stream; +struct _mem_stream +{ + UINT32 BaseAddr; + UINT32 EndAddr; + UINT32 CurAddr; + UINT16 CurStep; + const UINT8* MemPnt; +}; + + +typedef struct _rf5c68_state rf5c68_state; +struct _rf5c68_state +{ + //sound_stream * stream; + pcm_channel chan[NUM_CHANNELS]; + UINT8 cbank; + UINT8 wbank; + UINT8 enable; + UINT32 datasize; + UINT8* data; + //void (*sample_callback)(running_device* device,int channel); + mem_stream memstrm; +}; + + +static void rf5c68_mem_stream_flush(rf5c68_state *chip); + +/*INLINE rf5c68_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_RF5C68); + return (rf5c68_state *)device->token; +}*/ + +/************************************************/ +/* RF5C68 stream update */ +/************************************************/ + +static void memstream_sample_check(rf5c68_state *chip, UINT32 addr, UINT16 Speed) +{ + mem_stream* ms = &chip->memstrm; + UINT32 SmplSpd; + + SmplSpd = (Speed >= 0x0800) ? (Speed >> 11) : 1; + if (addr >= ms->CurAddr) + { + // Is the stream too fast? (e.g. about to catch up the output) + if (addr - ms->CurAddr <= SmplSpd * 5) + { + // Yes - delay the stream + ms->CurAddr -= SmplSpd * 4; + if (ms->CurAddr < ms->BaseAddr) + ms->CurAddr = ms->BaseAddr; + } + } + else + { + // Is the stream too slow? (e.g. the output is about to catch up the stream) + if (ms->CurAddr - addr <= SmplSpd * 5) + { + if (ms->CurAddr + SmplSpd * 4 >= ms->EndAddr) + { + rf5c68_mem_stream_flush(chip); + } + else + { + memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), SmplSpd * 4); + ms->CurAddr += SmplSpd * 4; + } + } + } + + return; +} + +//static STREAM_UPDATE( rf5c68_update ) +void rf5c68_update(void *_info, stream_sample_t **outputs, int samples) +{ + //rf5c68_state *chip = (rf5c68_state *)param; + rf5c68_state *chip = (rf5c68_state *)_info; + mem_stream* ms = &chip->memstrm; + stream_sample_t *left = outputs[0]; + stream_sample_t *right = outputs[1]; + int i, j; + + /* start with clean buffers */ + memset(left, 0, samples * sizeof(*left)); + memset(right, 0, samples * sizeof(*right)); + + /* bail if not enabled */ + if (!chip->enable) + return; + + /* loop over channels */ + for (i = 0; i < NUM_CHANNELS; i++) + { + pcm_channel *chan = &chip->chan[i]; + + /* if this channel is active, accumulate samples */ + if (chan->enable && ! chan->Muted) + { + int lv = (chan->pan & 0x0f) * chan->env; + int rv = ((chan->pan >> 4) & 0x0f) * chan->env; + + /* loop over the sample buffer */ + for (j = 0; j < samples; j++) + { + int sample; + + /* trigger sample callback */ + /*if(chip->sample_callback) + { + if(((chan->addr >> 11) & 0xfff) == 0xfff) + chip->sample_callback(chip->device,((chan->addr >> 11)/0x2000)); + }*/ + + memstream_sample_check(chip, (chan->addr >> 11) & 0xFFFF, chan->step); + /* fetch the sample and handle looping */ + sample = chip->data[(chan->addr >> 11) & 0xffff]; + if (sample == 0xff) + { + chan->addr = chan->loopst << 11; + sample = chip->data[(chan->addr >> 11) & 0xffff]; + + /* if we loop to a loop point, we're effectively dead */ + if (sample == 0xff) + break; + } + chan->addr += chan->step; + + /* add to the buffer */ + if (sample & 0x80) + { + sample &= 0x7f; + left[j] += (sample * lv) >> 5; + right[j] += (sample * rv) >> 5; + } + else + { + left[j] -= (sample * lv) >> 5; + right[j] -= (sample * rv) >> 5; + } + } + } + } + + if (samples && ms->CurAddr < ms->EndAddr) + { + ms->CurStep += STEAM_STEP * samples; + if (ms->CurStep >= 0x0800) // 1 << 11 + { + i = ms->CurStep >> 11; + ms->CurStep &= 0x07FF; + + if (ms->CurAddr + i > ms->EndAddr) + i = ms->EndAddr - ms->CurAddr; + + memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), i); + ms->CurAddr += i; + } + } + + // I think, this is completely useless + /* now clamp and shift the result (output is only 10 bits) */ + /*for (j = 0; j < samples; j++) + { + stream_sample_t temp; + + temp = left[j]; + if (temp > 32767) temp = 32767; + else if (temp < -32768) temp = -32768; + left[j] = temp & ~0x3f; + + temp = right[j]; + if (temp > 32767) temp = 32767; + else if (temp < -32768) temp = -32768; + right[j] = temp & ~0x3f; + }*/ +} + + +/************************************************/ +/* RF5C68 start */ +/************************************************/ + +//static DEVICE_START( rf5c68 ) +int device_start_rf5c68(void **_info, int clock) +{ + //const rf5c68_interface* intf = (const rf5c68_interface*)device->baseconfig().static_config(); + + /* allocate memory for the chip */ + //rf5c68_state *chip = get_safe_token(device); + rf5c68_state *chip; + int chn; + + chip = (rf5c68_state *) calloc(1, sizeof(rf5c68_state)); + *_info = (void *) chip; + + chip->datasize = 0x10000; + chip->data = (UINT8*)malloc(chip->datasize); + + /* allocate the stream */ + //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); + + /* set up callback */ + /*if(intf != NULL) + chip->sample_callback = intf->sample_end_callback; + else + chip->sample_callback = NULL;*/ + for (chn = 0; chn < NUM_CHANNELS; chn ++) + chip->chan[chn].Muted = 0x00; + + return (clock & 0x7FFFFFFF) / 384; +} + +void device_stop_rf5c68(void *_info) +{ + rf5c68_state *chip = (rf5c68_state *)_info; + free(chip->data); chip->data = NULL; + free(chip); + + return; +} + +void device_reset_rf5c68(void *_info) +{ + rf5c68_state *chip = (rf5c68_state *)_info; + int i; + pcm_channel* chan; + mem_stream* ms = &chip->memstrm; + + // Clear the PCM memory. + memset(chip->data, 0x00, chip->datasize); + + chip->enable = 0; + chip->cbank = 0; + chip->wbank = 0; + + /* clear channel registers */ + for (i = 0; i < NUM_CHANNELS; i ++) + { + chan = &chip->chan[i]; + chan->enable = 0; + chan->env = 0; + chan->pan = 0; + chan->start = 0; + chan->addr = 0; + chan->step = 0; + chan->loopst = 0; + } + + ms->BaseAddr = 0x0000; + ms->CurAddr = 0x0000; + ms->EndAddr = 0x0000; + ms->CurStep = 0x0000; + ms->MemPnt = NULL; +} + +/************************************************/ +/* RF5C68 write register */ +/************************************************/ + +//WRITE8_DEVICE_HANDLER( rf5c68_w ) +void rf5c68_w(void *_info, offs_t offset, UINT8 data) +{ + //rf5c68_state *chip = get_safe_token(device); + rf5c68_state *chip = (rf5c68_state *)_info; + pcm_channel *chan = &chip->chan[chip->cbank]; + int i; + + /* force the stream to update first */ + //stream_update(chip->stream); + + /* switch off the address */ + switch (offset) + { + case 0x00: /* envelope */ + chan->env = data; + break; + + case 0x01: /* pan */ + chan->pan = data; + break; + + case 0x02: /* FDL */ + chan->step = (chan->step & 0xff00) | (data & 0x00ff); + break; + + case 0x03: /* FDH */ + chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00); + break; + + case 0x04: /* LSL */ + chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff); + break; + + case 0x05: /* LSH */ + chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00); + break; + + case 0x06: /* ST */ + chan->start = data; + if (!chan->enable) + chan->addr = chan->start << (8 + 11); + break; + + case 0x07: /* control reg */ + chip->enable = (data >> 7) & 1; + if (data & 0x40) + chip->cbank = data & 7; + else + chip->wbank = data & 15; + break; + + case 0x08: /* channel on/off reg */ + for (i = 0; i < 8; i++) + { + chip->chan[i].enable = (~data >> i) & 1; + if (!chip->chan[i].enable) + chip->chan[i].addr = chip->chan[i].start << (8 + 11); + } + break; + } +} + + +/************************************************/ +/* RF5C68 read memory */ +/************************************************/ + +//READ8_DEVICE_HANDLER( rf5c68_mem_r ) +UINT8 rf5c68_mem_r(void *_info, offs_t offset) +{ + //rf5c68_state *chip = get_safe_token(device); + rf5c68_state *chip = (rf5c68_state *)_info; + return chip->data[chip->wbank * 0x1000 + offset]; +} + + +/************************************************/ +/* RF5C68 write memory */ +/************************************************/ + +//WRITE8_DEVICE_HANDLER( rf5c68_mem_w ) +void rf5c68_mem_w(void *_info, offs_t offset, UINT8 data) +{ + //rf5c68_state *chip = get_safe_token(device); + rf5c68_state *chip = (rf5c68_state *)_info; + rf5c68_mem_stream_flush(chip); + chip->data[chip->wbank * 0x1000 | offset] = data; +} + +static void rf5c68_mem_stream_flush(rf5c68_state *chip) +{ + mem_stream* ms = &chip->memstrm; + + if (ms->CurAddr >= ms->EndAddr) + return; + + memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), ms->EndAddr - ms->CurAddr); + ms->CurAddr = ms->EndAddr; + + return; +} + +void rf5c68_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) +{ + rf5c68_state *chip = (rf5c68_state *)_info; + mem_stream* ms = &chip->memstrm; + UINT16 BytCnt; + + DataStart |= chip->wbank * 0x1000; + if (DataStart >= chip->datasize) + return; + if (DataStart + DataLength > chip->datasize) + DataLength = chip->datasize - DataStart; + + //memcpy(chip->data + DataStart, RAMData, DataLength); + + rf5c68_mem_stream_flush(chip); + + ms->BaseAddr = DataStart; + ms->CurAddr = ms->BaseAddr; + ms->EndAddr = ms->BaseAddr + DataLength; + ms->CurStep = 0x0000; + ms->MemPnt = RAMData; + + //BytCnt = (STEAM_STEP * 32) >> 11; + BytCnt = 0x40; // SegaSonic Arcade: Run! Run! Run! needs such a high value + if (ms->CurAddr + BytCnt > ms->EndAddr) + BytCnt = ms->EndAddr - ms->CurAddr; + + memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), BytCnt); + ms->CurAddr += BytCnt; + + return; +} + + +void rf5c68_set_mute_mask(void *_info, UINT32 MuteMask) +{ + rf5c68_state *chip = (rf5c68_state *)_info; + unsigned char CurChn; + + for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) + chip->chan[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( rf5c68 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(rf5c68_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( rf5c68 ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: // Nothing break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "RF5C68"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Ricoh PCM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + +/**************** end of file ****************/ diff --git a/Frameworks/GME/gme/rf5c68.h b/Frameworks/GME/vgmplay/chips/rf5c68.h similarity index 84% rename from Frameworks/GME/gme/rf5c68.h rename to Frameworks/GME/vgmplay/chips/rf5c68.h index c2f3d07dc..a5f116813 100644 --- a/Frameworks/GME/gme/rf5c68.h +++ b/Frameworks/GME/vgmplay/chips/rf5c68.h @@ -1,38 +1,28 @@ -/*********************************************************/ -/* ricoh RF5C68(or clone) PCM controller */ -/*********************************************************/ - -#pragma once - -#include "mamedef.h" - -/******************************************/ -/*WRITE8_DEVICE_HANDLER( rf5c68_w ); - -READ8_DEVICE_HANDLER( rf5c68_mem_r ); -WRITE8_DEVICE_HANDLER( rf5c68_mem_w ); - -DEVICE_GET_INFO( rf5c68 ); -#define SOUND_RF5C68 DEVICE_GET_INFO_NAME( rf5c68 )*/ - -#ifdef __cplusplus -extern "C" { -#endif - -void rf5c68_update(void *chip, stream_sample_t **outputs, int samples); - -void * device_start_rf5c68(); -void device_stop_rf5c68(void *chip); -void device_reset_rf5c68(void *chip); - -void rf5c68_w(void *chip, offs_t offset, UINT8 data); - -UINT8 rf5c68_mem_r(void *chip, offs_t offset); -void rf5c68_mem_w(void *chip, offs_t offset, UINT8 data); -void rf5c68_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); - -void rf5c68_set_mute_mask(void *chip, UINT32 MuteMask); - -#ifdef __cplusplus -} -#endif +/*********************************************************/ +/* ricoh RF5C68(or clone) PCM controller */ +/*********************************************************/ + +#pragma once + +/******************************************/ +/*WRITE8_DEVICE_HANDLER( rf5c68_w ); + +READ8_DEVICE_HANDLER( rf5c68_mem_r ); +WRITE8_DEVICE_HANDLER( rf5c68_mem_w ); + +DEVICE_GET_INFO( rf5c68 ); +#define SOUND_RF5C68 DEVICE_GET_INFO_NAME( rf5c68 )*/ + +void rf5c68_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_rf5c68(void **chip, int clock); +void device_stop_rf5c68(void *chip); +void device_reset_rf5c68(void *chip); + +void rf5c68_w(void *chip, offs_t offset, UINT8 data); + +UINT8 rf5c68_mem_r(void *chip, offs_t offset); +void rf5c68_mem_w(void *chip, offs_t offset, UINT8 data); +void rf5c68_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); + +void rf5c68_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/saa1099.c b/Frameworks/GME/vgmplay/chips/saa1099.c new file mode 100644 index 000000000..ef9836e78 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/saa1099.c @@ -0,0 +1,592 @@ +/*************************************************************************** + + Philips SAA1099 Sound driver + + By Juergen Buchmueller and Manuel Abadia + + SAA1099 register layout: + ======================== + + offs | 7654 3210 | description + -----+-----------+--------------------------- + 0x00 | ---- xxxx | Amplitude channel 0 (left) + 0x00 | xxxx ---- | Amplitude channel 0 (right) + 0x01 | ---- xxxx | Amplitude channel 1 (left) + 0x01 | xxxx ---- | Amplitude channel 1 (right) + 0x02 | ---- xxxx | Amplitude channel 2 (left) + 0x02 | xxxx ---- | Amplitude channel 2 (right) + 0x03 | ---- xxxx | Amplitude channel 3 (left) + 0x03 | xxxx ---- | Amplitude channel 3 (right) + 0x04 | ---- xxxx | Amplitude channel 4 (left) + 0x04 | xxxx ---- | Amplitude channel 4 (right) + 0x05 | ---- xxxx | Amplitude channel 5 (left) + 0x05 | xxxx ---- | Amplitude channel 5 (right) + | | + 0x08 | xxxx xxxx | Frequency channel 0 + 0x09 | xxxx xxxx | Frequency channel 1 + 0x0a | xxxx xxxx | Frequency channel 2 + 0x0b | xxxx xxxx | Frequency channel 3 + 0x0c | xxxx xxxx | Frequency channel 4 + 0x0d | xxxx xxxx | Frequency channel 5 + | | + 0x10 | ---- -xxx | Channel 0 octave select + 0x10 | -xxx ---- | Channel 1 octave select + 0x11 | ---- -xxx | Channel 2 octave select + 0x11 | -xxx ---- | Channel 3 octave select + 0x12 | ---- -xxx | Channel 4 octave select + 0x12 | -xxx ---- | Channel 5 octave select + | | + 0x14 | ---- ---x | Channel 0 frequency enable (0 = off, 1 = on) + 0x14 | ---- --x- | Channel 1 frequency enable (0 = off, 1 = on) + 0x14 | ---- -x-- | Channel 2 frequency enable (0 = off, 1 = on) + 0x14 | ---- x--- | Channel 3 frequency enable (0 = off, 1 = on) + 0x14 | ---x ---- | Channel 4 frequency enable (0 = off, 1 = on) + 0x14 | --x- ---- | Channel 5 frequency enable (0 = off, 1 = on) + | | + 0x15 | ---- ---x | Channel 0 noise enable (0 = off, 1 = on) + 0x15 | ---- --x- | Channel 1 noise enable (0 = off, 1 = on) + 0x15 | ---- -x-- | Channel 2 noise enable (0 = off, 1 = on) + 0x15 | ---- x--- | Channel 3 noise enable (0 = off, 1 = on) + 0x15 | ---x ---- | Channel 4 noise enable (0 = off, 1 = on) + 0x15 | --x- ---- | Channel 5 noise enable (0 = off, 1 = on) + | | + 0x16 | ---- --xx | Noise generator parameters 0 + 0x16 | --xx ---- | Noise generator parameters 1 + | | + 0x18 | --xx xxxx | Envelope generator 0 parameters + 0x18 | x--- ---- | Envelope generator 0 control enable (0 = off, 1 = on) + 0x19 | --xx xxxx | Envelope generator 1 parameters + 0x19 | x--- ---- | Envelope generator 1 control enable (0 = off, 1 = on) + | | + 0x1c | ---- ---x | All channels enable (0 = off, 1 = on) + 0x1c | ---- --x- | Synch & Reset generators + +***************************************************************************/ + +//#include "emu.h" +#include "mamedef.h" +#include +#include +#include "saa1099.h" + + +#define LEFT 0x00 +#define RIGHT 0x01 + +/* this structure defines a channel */ +struct saa1099_channel +{ + int frequency; /* frequency (0x00..0xff) */ + int freq_enable; /* frequency enable */ + int noise_enable; /* noise enable */ + int octave; /* octave (0x00..0x07) */ + int amplitude[2]; /* amplitude (0x00..0x0f) */ + int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ + + /* vars to simulate the square wave */ + double counter; + double freq; + int level; + UINT8 Muted; +}; + +/* this structure defines a noise channel */ +struct saa1099_noise +{ + /* vars to simulate the noise generator output */ + double counter; + double freq; + int level; /* noise polynomal shifter */ +}; + +/* this structure defines a SAA1099 chip */ +typedef struct _saa1099_state saa1099_state; +struct _saa1099_state +{ + //device_t *device; + //sound_stream * stream; /* our stream */ + int noise_params[2]; /* noise generators parameters */ + int env_enable[2]; /* envelope generators enable */ + int env_reverse_right[2]; /* envelope reversed for right channel */ + int env_mode[2]; /* envelope generators mode */ + int env_bits[2]; /* non zero = 3 bits resolution */ + int env_clock[2]; /* envelope clock mode (non-zero external) */ + int env_step[2]; /* current envelope step */ + int all_ch_enable; /* all channels enable */ + int sync_state; /* sync all channels */ + int selected_reg; /* selected register */ + struct saa1099_channel channels[6]; /* channels */ + struct saa1099_noise noise[2]; /* noise generators */ + double sample_rate; + int master_clock; +}; + +static const int amplitude_lookup[16] = { + 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, + 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, + 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, + 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 +}; + +static const UINT8 envelope[8][64] = { + /* zero amplitude */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* maximum amplitude */ + {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, + /* single decay */ + {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive decay */ + {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + /* single triangular */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive triangular */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + /* single attack */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + /* repetitive attack */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } +}; + + +/*INLINE saa1099_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == SAA1099); + return (saa1099_state *)downcast(device)->token(); +}*/ + + +static void saa1099_envelope(saa1099_state *saa, int ch) +{ + if (saa->env_enable[ch]) + { + int step, mode, mask; + mode = saa->env_mode[ch]; + /* step from 0..63 and then loop in steps 32..63 */ + step = saa->env_step[ch] = + ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); + + mask = 15; + if (saa->env_bits[ch]) + mask &= ~1; /* 3 bit resolution, mask LSB */ + + saa->channels[ch*3+0].envelope[ LEFT] = + saa->channels[ch*3+1].envelope[ LEFT] = + saa->channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask; + if (saa->env_reverse_right[ch] & 0x01) + { + saa->channels[ch*3+0].envelope[RIGHT] = + saa->channels[ch*3+1].envelope[RIGHT] = + saa->channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask; + } + else + { + saa->channels[ch*3+0].envelope[RIGHT] = + saa->channels[ch*3+1].envelope[RIGHT] = + saa->channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask; + } + } + else + { + /* envelope mode off, set all envelope factors to 16 */ + saa->channels[ch*3+0].envelope[ LEFT] = + saa->channels[ch*3+1].envelope[ LEFT] = + saa->channels[ch*3+2].envelope[ LEFT] = + saa->channels[ch*3+0].envelope[RIGHT] = + saa->channels[ch*3+1].envelope[RIGHT] = + saa->channels[ch*3+2].envelope[RIGHT] = 16; + } +} + + +//static STREAM_UPDATE( saa1099_update ) +void saa1099_update(void *param, stream_sample_t **outputs, int samples) +{ + saa1099_state *saa = (saa1099_state *)param; + int j, ch; + int clk2div512; + + /* if the channels are disabled we're done */ + if (!saa->all_ch_enable) + { + /* init output data */ + memset(outputs[LEFT],0,samples*sizeof(*outputs[LEFT])); + memset(outputs[RIGHT],0,samples*sizeof(*outputs[RIGHT])); + return; + } + + for (ch = 0; ch < 2; ch++) + { + switch (saa->noise_params[ch]) + { + case 0: saa->noise[ch].freq = saa->master_clock/ 256.0 * 2; break; + case 1: saa->noise[ch].freq = saa->master_clock/ 512.0 * 2; break; + case 2: saa->noise[ch].freq = saa->master_clock/1024.0 * 2; break; + case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; + } + } + + // clock fix thanks to http://www.vogons.org/viewtopic.php?p=344227#p344227 + //clk2div512 = 2 * saa->master_clock / 512; + clk2div512 = (saa->master_clock + 128) / 256; + + /* fill all data needed */ + for( j = 0; j < samples; j++ ) + { + int output_l = 0, output_r = 0; + + /* for each channel */ + for (ch = 0; ch < 6; ch++) + { + if (saa->channels[ch].freq == 0.0) + saa->channels[ch].freq = (double)(clk2div512 << saa->channels[ch].octave) / + (511.0 - (double)saa->channels[ch].frequency); + + /* check the actual position in the square wave */ + saa->channels[ch].counter -= saa->channels[ch].freq; + while (saa->channels[ch].counter < 0) + { + /* calculate new frequency now after the half wave is updated */ + saa->channels[ch].freq = (double)(clk2div512 << saa->channels[ch].octave) / + (511.0 - (double)saa->channels[ch].frequency); + + saa->channels[ch].counter += saa->sample_rate; + saa->channels[ch].level ^= 1; + + /* eventually clock the envelope counters */ + if (ch == 1 && saa->env_clock[0] == 0) + saa1099_envelope(saa, 0); + if (ch == 4 && saa->env_clock[1] == 0) + saa1099_envelope(saa, 1); + } + + if (saa->channels[ch].Muted) + continue; // placed here to ensure that envelopes are updated + +#if 0 + // if the noise is enabled + if (saa->channels[ch].noise_enable) + { + // if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) + if (saa->noise[ch/3].level & 1) + { + // subtract to avoid overflows, also use only half amplitude + output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2; + output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2; + } + } + + // if the square wave is enabled + if (saa->channels[ch].freq_enable) + { + // if the channel level is high + if (saa->channels[ch].level & 1) + { + output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; + output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; + } + } +#else + // Now with bipolar output. -Valley Bell + if (saa->channels[ch].noise_enable) + { + if (saa->noise[ch/3].level & 1) + { + output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32 / 2; + output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32 / 2; + } + else + { + output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32 / 2; + output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32 / 2; + } + } + + if (saa->channels[ch].freq_enable) + { + if (saa->channels[ch].level & 1) + { + output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32; + output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32; + } + else + { + output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 32; + output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 32; + } + } +#endif + } + + for (ch = 0; ch < 2; ch++) + { + /* check the actual position in noise generator */ + saa->noise[ch].counter -= saa->noise[ch].freq; + while (saa->noise[ch].counter < 0) + { + saa->noise[ch].counter += saa->sample_rate; + if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) + saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; + else + saa->noise[ch].level <<= 1; + } + } + /* write sound data to the buffer */ + outputs[LEFT][j] = output_l / 6; + outputs[RIGHT][j] = output_r / 6; + } +} + + + +//static DEVICE_START( saa1099 ) +int device_start_saa1099(void **_info, int clock) +{ + //saa1099_state *saa = get_safe_token(device); + saa1099_state *saa; + UINT8 CurChn; + + saa = (saa1099_state *) calloc(1, sizeof(saa1099_state)); + *_info = (void *) saa; + + /* copy global parameters */ + //saa->device = device; + //saa->sample_rate = device->clock() / 256; + saa->master_clock = clock; + saa->sample_rate = clock / 256.0; + + /* for each chip allocate one stream */ + //saa->stream = device->machine().sound().stream_alloc(*device, 0, 2, saa->sample_rate, saa, saa1099_update); + + for (CurChn = 0; CurChn < 6; CurChn ++) + saa->channels[CurChn].Muted = 0x00; + + return (int)(saa->sample_rate + 0.5); +} + +void device_stop_saa1099(void *chip) +{ + free(chip); + + return; +} + +void device_reset_saa1099(void *_info) +{ + saa1099_state *saa = (saa1099_state *)_info; + struct saa1099_channel *sachn; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + { + sachn = &saa->channels[CurChn]; + sachn->frequency = 0; + sachn->octave = 0; + sachn->amplitude[0] = 0; + sachn->amplitude[1] = 0; + sachn->envelope[0] = 0; + sachn->envelope[1] = 0; + sachn->freq_enable = 0; + sachn->noise_enable = 0; + + sachn->counter = 0; + sachn->freq = 0; + sachn->level = 0; + } + for (CurChn = 0; CurChn < 2; CurChn ++) + { + saa->noise[CurChn].counter = 0; + saa->noise[CurChn].freq = 0; + saa->noise[CurChn].level = 0; + + saa->noise_params[1] = 0x00; + saa->env_reverse_right[CurChn] = 0x00; + saa->env_mode[CurChn] = 0x00; + saa->env_bits[CurChn] = 0x00; + saa->env_clock[CurChn] = 0x00; + saa->env_enable[CurChn] = 0x00; + saa->env_step[CurChn] = 0; + } + + saa->all_ch_enable = 0x00; + saa->sync_state = 0x00; + + return; +} + +//WRITE8_DEVICE_HANDLER( saa1099_control_w ) +void saa1099_control_w(void *_info, offs_t offset, UINT8 data) +{ + //saa1099_state *saa = get_safe_token(device); + saa1099_state *saa = (saa1099_state *)_info; + + if ((data & 0xff) > 0x1c) + { + /* Error! */ + //logerror("%s: (SAA1099 '%s') Unknown register selected\n",device->machine().describe_context(), device->tag()); + logerror("SAA1099: Unknown register selected\n"); + } + + saa->selected_reg = data & 0x1f; + if (saa->selected_reg == 0x18 || saa->selected_reg == 0x19) + { + /* clock the envelope channels */ + if (saa->env_clock[0]) + saa1099_envelope(saa,0); + if (saa->env_clock[1]) + saa1099_envelope(saa,1); + } +} + + +//WRITE8_DEVICE_HANDLER( saa1099_data_w ) +void saa1099_data_w(void *_info, offs_t offset, UINT8 data) +{ + //saa1099_state *saa = get_safe_token(device); + saa1099_state *saa = (saa1099_state *)_info; + int reg = saa->selected_reg; + int ch; + + /* first update the stream to this point in time */ + //saa->stream->update(); + + switch (reg) + { + /* channel i amplitude */ + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: + ch = reg & 7; + saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; + saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; + break; + /* channel i frequency */ + case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: + ch = reg & 7; + saa->channels[ch].frequency = data & 0xff; + break; + /* channel i octave */ + case 0x10: case 0x11: case 0x12: + ch = (reg - 0x10) << 1; + saa->channels[ch + 0].octave = data & 0x07; + saa->channels[ch + 1].octave = (data >> 4) & 0x07; + break; + /* channel i frequency enable */ + case 0x14: + saa->channels[0].freq_enable = data & 0x01; + saa->channels[1].freq_enable = data & 0x02; + saa->channels[2].freq_enable = data & 0x04; + saa->channels[3].freq_enable = data & 0x08; + saa->channels[4].freq_enable = data & 0x10; + saa->channels[5].freq_enable = data & 0x20; + break; + /* channel i noise enable */ + case 0x15: + saa->channels[0].noise_enable = data & 0x01; + saa->channels[1].noise_enable = data & 0x02; + saa->channels[2].noise_enable = data & 0x04; + saa->channels[3].noise_enable = data & 0x08; + saa->channels[4].noise_enable = data & 0x10; + saa->channels[5].noise_enable = data & 0x20; + break; + /* noise generators parameters */ + case 0x16: + saa->noise_params[0] = data & 0x03; + saa->noise_params[1] = (data >> 4) & 0x03; + break; + /* envelope generators parameters */ + case 0x18: case 0x19: + ch = reg - 0x18; + saa->env_reverse_right[ch] = data & 0x01; + saa->env_mode[ch] = (data >> 1) & 0x07; + saa->env_bits[ch] = data & 0x10; + saa->env_clock[ch] = data & 0x20; + saa->env_enable[ch] = data & 0x80; + /* reset the envelope */ + saa->env_step[ch] = 0; + break; + /* channels enable & reset generators */ + case 0x1c: + saa->all_ch_enable = data & 0x01; + saa->sync_state = data & 0x02; + if (data & 0x02) + { + int i; + + /* Synch & Reset generators */ + //logerror("%s: (SAA1099 '%s') -reg 0x1c- Chip reset\n",device->machine().describe_context(), device->tag()); + logerror("SAA1099: -reg 0x1c- Chip reset\n"); + for (i = 0; i < 6; i++) + { + saa->channels[i].level = 0; + saa->channels[i].counter = 0.0; + } + } + break; + default: /* Error! */ + //logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n",device->machine().describe_context(), device->tag(), reg, data); + logerror("SAA1099: Unknown operation (reg:%02x, data:%02x)\n",reg, data); + } +} + + +void saa1099_set_mute_mask(void *_info, UINT32 MuteMask) +{ + saa1099_state *saa = (saa1099_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + saa->channels[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( saa1099 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(saa1099_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( saa1099 ); break; + case DEVINFO_FCT_STOP: // Nothing // break; + case DEVINFO_FCT_RESET: // Nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "SAA1099"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Philips"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(SAA1099, saa1099);*/ diff --git a/Frameworks/GME/vgmplay/chips/saa1099.h b/Frameworks/GME/vgmplay/chips/saa1099.h new file mode 100644 index 000000000..0fa39af40 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/saa1099.h @@ -0,0 +1,23 @@ +#pragma once + +#ifndef __SAA1099_H__ +#define __SAA1099_H__ + +/********************************************** + Philips SAA1099 Sound driver +**********************************************/ + +//WRITE8_DEVICE_HANDLER( saa1099_control_w ); +void saa1099_control_w(void *chip, offs_t offset, UINT8 data); +//WRITE8_DEVICE_HANDLER( saa1099_data_w ); +void saa1099_data_w(void *chip, offs_t offset, UINT8 data); + +//DECLARE_LEGACY_SOUND_DEVICE(SAA1099, saa1099); +void saa1099_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_saa1099(void **chip, int clock); +void device_stop_saa1099(void *chip); +void device_reset_saa1099(void *chip); + +void saa1099_set_mute_mask(void *chip, UINT32 MuteMask); + +#endif /* __SAA1099_H__ */ diff --git a/Frameworks/GME/gme/scd_pcm.c b/Frameworks/GME/vgmplay/chips/scd_pcm.c similarity index 82% rename from Frameworks/GME/gme/scd_pcm.c rename to Frameworks/GME/vgmplay/chips/scd_pcm.c index ee3d4701f..6d84edff0 100644 --- a/Frameworks/GME/gme/scd_pcm.c +++ b/Frameworks/GME/vgmplay/chips/scd_pcm.c @@ -1,498 +1,510 @@ -/***********************************************************/ -/* */ -/* PCM.C : PCM RF5C164 emulator */ -/* */ -/* This source is a part of Gens project */ -/* Written by Stéphane Dallongeville (gens@consolemul.com) */ -/* Copyright (c) 2002 by Stéphane Dallongeville */ -/* */ -/***********************************************************/ - -#include -#include -#include - -#include "scd_pcm.h" -int PCM_Init(void *chip, int Rate); -void PCM_Set_Rate(void *chip, int Rate); -void PCM_Reset(void *chip); -void PCM_Write_Reg(void *chip, unsigned int Reg, unsigned int Data); -int PCM_Update(void *chip, int **buf, int Length); - -#define PCM_STEP_SHIFT 11 - -static unsigned char VolTabIsInit = 0x00; -static int PCM_Volume_Tab[256 * 256]; - -//unsigned char Ram_PCM[64 * 1024]; -//int PCM_Enable; - - -/** - * PCM_Init(): Initialize the PCM chip. - * @param Rate Sample rate. - * @return 0 if successful. - */ -int PCM_Init(void *_chip, int Rate) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - int i, j, out; - - if (! VolTabIsInit) - { - for (i = 0; i < 0x100; i++) - { - for (j = 0; j < 0x100; j++) - { - if (i & 0x80) - { - out = -(i & 0x7F); - out *= j; - PCM_Volume_Tab[(j << 8) + i] = out; - } - else - { - out = i * j; - PCM_Volume_Tab[(j << 8) + i] = out; - } - } - } - VolTabIsInit = 0x01; - } - - for (i = 0; i < 8; i ++) - chip->Channel[i].Muted = 0x00; - - chip->RAMSize = 64 * 1024; - chip->RAM = (unsigned char*)malloc(chip->RAMSize); - PCM_Reset(chip); - PCM_Set_Rate(chip, Rate); - - return 0; -} - - -/** - * PCM_Reset(): Reset the PCM chip. - */ -void PCM_Reset(void *_chip) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - int i; - struct pcm_chan_* chan; - - // Clear the PCM memory. - memset(chip->RAM, 0x00, chip->RAMSize); - - chip->Enable = 0; - chip->Cur_Chan = 0; - chip->Bank = 0; - - /* clear channel registers */ - for (i = 0; i < 8; i++) - { - chan = &chip->Channel[i]; - chan->Enable = 0; - chan->ENV = 0; - chan->PAN = 0; - chan->St_Addr = 0; - chan->Addr = 0; - chan->Loop_Addr = 0; - chan->Step = 0; - chan->Step_B = 0; - chan->Data = 0; - } -} - - -/** - * PCM_Set_Rate(): Change the PCM sample rate. - * @param Rate New sample rate. - */ -void PCM_Set_Rate(void *_chip, int Rate) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - int i; - - if (Rate == 0) - return; - - //chip->Rate = (float) (32 * 1024) / (float) Rate; - chip->Rate = (float) (31.8 * 1024) / (float) Rate; - - for (i = 0; i < 8; i++) - { - chip->Channel[i].Step = - (int) ((float) chip->Channel[i].Step_B * chip->Rate); - } -} - - -/** - * PCM_Write_Reg(): Write to a PCM register. - * @param Reg Register ID. - * @param Data Data to write. - */ -void PCM_Write_Reg(void *_chip, unsigned int Reg, unsigned int Data) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - int i; - struct pcm_chan_* chan = &chip->Channel[chip->Cur_Chan]; - - Data &= 0xFF; - - switch (Reg) - { - case 0x00: - /* evelope register */ - chan->ENV = Data; - chan->MUL_L = (Data * (chan->PAN & 0x0F)) >> 5; - chan->MUL_R = (Data * (chan->PAN >> 4)) >> 5; - break; - - case 0x01: - /* pan register */ - chan->PAN = Data; - chan->MUL_L = ((Data & 0x0F) * chan->ENV) >> 5; - chan->MUL_R = ((Data >> 4) * chan->ENV) >> 5; - break; - - case 0x02: - /* frequency step (LB) registers */ - chan->Step_B &= 0xFF00; - chan->Step_B += Data; - chan->Step = (int)((float)chan->Step_B * chip->Rate); - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "Step low = %.2X Step calculated = %.8X", - // Data, chan->Step); - break; - - case 0x03: - /* frequency step (HB) registers */ - chan->Step_B &= 0x00FF; - chan->Step_B += Data << 8; - chan->Step = (int)((float)chan->Step_B * chip->Rate); - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "Step high = %.2X Step calculated = %.8X", - // Data, chan->Step); - break; - - case 0x04: - chan->Loop_Addr &= 0xFF00; - chan->Loop_Addr += Data; - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "Loop low = %.2X Loop = %.8X", - // Data, chan->Loop_Addr); - break; - - case 0x05: - /* loop address registers */ - chan->Loop_Addr &= 0x00FF; - chan->Loop_Addr += Data << 8; - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "Loop high = %.2X Loop = %.8X", - // Data, chan->Loop_Addr); - break; - - case 0x06: - /* start address registers */ - chan->St_Addr = Data << (PCM_STEP_SHIFT + 8); - //chan->Addr = chan->St_Addr; - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "Start addr = %.2X New Addr = %.8X", - // Data, chan->Addr); - break; - - case 0x07: - /* control register */ - /* mod is H */ - if (Data & 0x40) - { - /* select channel */ - chip->Cur_Chan = Data & 0x07; - } - /* mod is L */ - else - { - /* pcm ram bank select */ - chip->Bank = (Data & 0x0F) << 12; - } - - /* sounding bit */ - if (Data & 0x80) - chip->Enable = 0xFF; // Used as mask - else - chip->Enable = 0; - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "General Enable = %.2X", Data); - break; - - case 0x08: - /* sound on/off register */ - Data ^= 0xFF; - - //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, - // "Channel Enable = %.2X", Data); - - for (i = 0; i < 8; i++) - { - chan = &chip->Channel[i]; - if (!chan->Enable) - chan->Addr = chan->St_Addr; - } - - for (i = 0; i < 8; i++) - { - chip->Channel[i].Enable = Data & (1 << i); - } - } -} - - -/** - * PCM_Update(): Update the PCM buffer. - * @param buf PCM buffer. - * @param Length Buffer length. - */ -int PCM_Update(void *_chip, int **buf, int Length) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - int i, j; - int *bufL, *bufR; //, *volL, *volR; - unsigned int Addr, k; - struct pcm_chan_ *CH; - - bufL = buf[0]; - bufR = buf[1]; - - // clear buffers - memset(bufL, 0, Length * sizeof(int)); - memset(bufR, 0, Length * sizeof(int)); - - // if PCM disable, no sound - if (!chip->Enable) - return 1; - -#if 0 - // faster for short update - for (j = 0; j < Length; j++) - { - for (i = 0; i < 8; i++) - { - CH = &(chip->Channel[i]); - - // only loop when sounding and on - if (CH->Enable && ! CH->Muted) - { - Addr = CH->Addr >> PCM_STEP_SHIFT; - - if (Addr & 0x10000) - { - for(k = CH->Old_Addr; k < 0x10000; k++) - { - if (chip->RAM[k] == 0xFF) - { - CH->Old_Addr = Addr = CH->Loop_Addr; - CH->Addr = Addr << PCM_STEP_SHIFT; - break; - } - } - - if (Addr & 0x10000) - { - //CH->Addr -= CH->Step; - CH->Enable = 0; - break; - } - } - else - { - for(k = CH->Old_Addr; k <= Addr; k++) - { - if (chip->RAM[k] == 0xFF) - { - CH->Old_Addr = Addr = CH->Loop_Addr; - CH->Addr = Addr << PCM_STEP_SHIFT; - break; - } - } - } - - // test for loop signal - if (chip->RAM[Addr] == 0xFF) - { - Addr = CH->Loop_Addr; - CH->Addr = Addr << PCM_STEP_SHIFT; - } - - if (chip->RAM[Addr] & 0x80) - { - CH->Data = chip->RAM[Addr] & 0x7F; - bufL[j] -= CH->Data * CH->MUL_L; - bufR[j] -= CH->Data * CH->MUL_R; - } - else - { - CH->Data = chip->RAM[Addr]; - bufL[j] += CH->Data * CH->MUL_L; - bufR[j] += CH->Data * CH->MUL_R; - } - - // update address register - //CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF; - CH->Addr += CH->Step; - CH->Old_Addr = Addr + 1; - } - } - } -#endif - -#if 1 - // for long update - for (i = 0; i < 8; i++) - { - CH = &(chip->Channel[i]); - - // only loop when sounding and on - if (CH->Enable && ! CH->Muted) - { - Addr = CH->Addr >> PCM_STEP_SHIFT; - //volL = &(PCM_Volume_Tab[CH->MUL_L << 8]); - //volR = &(PCM_Volume_Tab[CH->MUL_R << 8]); - - for (j = 0; j < Length; j++) - { - // test for loop signal - if (chip->RAM[Addr] == 0xFF) - { - CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT; - if (chip->RAM[Addr] == 0xFF) - break; - else - j--; - } - else - { - if (chip->RAM[Addr] & 0x80) - { - CH->Data = chip->RAM[Addr] & 0x7F; - bufL[j] -= CH->Data * CH->MUL_L; - bufR[j] -= CH->Data * CH->MUL_R; - } - else - { - CH->Data = chip->RAM[Addr]; - bufL[j] += CH->Data * CH->MUL_L; - bufR[j] += CH->Data * CH->MUL_R; - } - - // update address register - k = Addr + 1; - CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF; - Addr = CH->Addr >> PCM_STEP_SHIFT; - - for (; k < Addr; k++) - { - if (chip->RAM[k] == 0xFF) - { - CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT; - break; - } - } - } - } - - if (chip->RAM[Addr] == 0xFF) - { - CH->Addr = CH->Loop_Addr << PCM_STEP_SHIFT; - } - } - } -#endif - - return 0; -} - - -void rf5c164_update(void *_chip, stream_sample_t **outputs, int samples) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - - PCM_Update(chip, outputs, samples); -} - -void * device_start_rf5c164( int clock ) -{ - /* allocate memory for the chip */ - //rf5c164_state *chip = get_safe_token(device); - struct pcm_chip_ *chip; - int rate; - - chip = (struct pcm_chip_ *) malloc(sizeof(struct pcm_chip_)); - if (!chip) return chip; - - rate = clock / 384; - - PCM_Init(chip, rate); - /* allocate the stream */ - //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); - - return chip; -} - -void device_stop_rf5c164(void *_chip) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - free(chip->RAM); chip->RAM = NULL; - free(chip); -} - -void device_reset_rf5c164(void *chip) -{ - //struct pcm_chip_ *chip = &PCM_Chip[ChipID]; - PCM_Reset(chip); -} - -void rf5c164_w(void *chip, offs_t offset, UINT8 data) -{ - //struct pcm_chip_ *chip = &PCM_Chip[ChipID]; - PCM_Write_Reg(chip, offset, data); -} - -void rf5c164_mem_w(void *_chip, offs_t offset, UINT8 data) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - chip->RAM[chip->Bank | offset] = data; -} - -void rf5c164_write_ram(void *_chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - - if (DataStart >= chip->RAMSize) - return; - if (DataStart + DataLength > chip->RAMSize) - DataLength = chip->RAMSize - DataStart; - - memcpy(chip->RAM + (chip->Bank | DataStart), RAMData, DataLength); - - return; -} - - -void rf5c164_set_mute_mask(void *_chip, UINT32 MuteMask) -{ - struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip; - unsigned char CurChn; - - for (CurChn = 0; CurChn < 8; CurChn ++) - chip->Channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; - - return; -} +/***********************************************************/ +/* */ +/* PCM.C : PCM RF5C164 emulator */ +/* */ +/* This source is a part of Gens project */ +/* Written by Stéphane Dallongeville (gens@consolemul.com) */ +/* Copyright (c) 2002 by Stéphane Dallongeville */ +/* */ +/***********************************************************/ + +#include +#include +#include + +#include "mamedef.h" +#include "scd_pcm.h" +int PCM_Init(void *chip, int Rate); +void PCM_Set_Rate(void *chip, int Rate); +void PCM_Reset(void *chip); +void PCM_Write_Reg(void *chip, unsigned int Reg, unsigned int Data); +int PCM_Update(void *chip, int **buf, int Length); + +#define PCM_STEP_SHIFT 11 + +/*static unsigned char VolTabIsInit = 0x00; +static int PCM_Volume_Tab[256 * 256];*/ + +//unsigned char Ram_PCM[64 * 1024]; +//int PCM_Enable; + + +/** + * PCM_Init(): Initialize the PCM chip. + * @param Rate Sample rate. + * @return 0 if successful. + */ +int PCM_Init(void *_info, int Rate) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + int i/*, j, out*/; + + /*if (! VolTabIsInit) + { + for (i = 0; i < 0x100; i++) + { + for (j = 0; j < 0x100; j++) + { + if (i & 0x80) + { + out = -(i & 0x7F); + out *= j; + PCM_Volume_Tab[(j << 8) + i] = out; + } + else + { + out = i * j; + PCM_Volume_Tab[(j << 8) + i] = out; + } + } + } + VolTabIsInit = 0x01; + }*/ + + chip->Smpl0Patch = 0; + for (i = 0; i < 8; i ++) + chip->Channel[i].Muted = 0x00; + + chip->RAMSize = 64 * 1024; + chip->RAM = (unsigned char*)malloc(chip->RAMSize); + PCM_Reset(chip); + PCM_Set_Rate(chip, Rate); + + return 0; +} + + +/** + * PCM_Reset(): Reset the PCM chip. + */ +void PCM_Reset(void *_info) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + int i; + struct pcm_chan_* chan; + + // Clear the PCM memory. + memset(chip->RAM, 0x00, chip->RAMSize); + + chip->Enable = 0; + chip->Cur_Chan = 0; + chip->Bank = 0; + + /* clear channel registers */ + for (i = 0; i < 8; i++) + { + chan = &chip->Channel[i]; + chan->Enable = 0; + chan->ENV = 0; + chan->PAN = 0; + chan->St_Addr = 0; + chan->Addr = 0; + chan->Loop_Addr = 0; + chan->Step = 0; + chan->Step_B = 0; + chan->Data = 0; + } +} + + +/** + * PCM_Set_Rate(): Change the PCM sample rate. + * @param Rate New sample rate. + */ +void PCM_Set_Rate(void *_info, int Rate) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + int i; + + if (Rate == 0) + return; + + //chip->Rate = (float) (32 * 1024) / (float) Rate; + chip->Rate = (float) (31.8 * 1024) / (float) Rate; + + for (i = 0; i < 8; i++) + { + chip->Channel[i].Step = + (int) ((float) chip->Channel[i].Step_B * chip->Rate); + } +} + + +/** + * PCM_Write_Reg(): Write to a PCM register. + * @param Reg Register ID. + * @param Data Data to write. + */ +void PCM_Write_Reg(void *_info, unsigned int Reg, unsigned int Data) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + int i; + struct pcm_chan_* chan = &chip->Channel[chip->Cur_Chan]; + + Data &= 0xFF; + + switch (Reg) + { + case 0x00: + /* evelope register */ + chan->ENV = Data; + chan->MUL_L = (Data * (chan->PAN & 0x0F)) >> 5; + chan->MUL_R = (Data * (chan->PAN >> 4)) >> 5; + break; + + case 0x01: + /* pan register */ + chan->PAN = Data; + chan->MUL_L = ((Data & 0x0F) * chan->ENV) >> 5; + chan->MUL_R = ((Data >> 4) * chan->ENV) >> 5; + break; + + case 0x02: + /* frequency step (LB) registers */ + chan->Step_B &= 0xFF00; + chan->Step_B += Data; + chan->Step = (int)((float)chan->Step_B * chip->Rate); + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "Step low = %.2X Step calculated = %.8X", + // Data, chan->Step); + break; + + case 0x03: + /* frequency step (HB) registers */ + chan->Step_B &= 0x00FF; + chan->Step_B += Data << 8; + chan->Step = (int)((float)chan->Step_B * chip->Rate); + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "Step high = %.2X Step calculated = %.8X", + // Data, chan->Step); + break; + + case 0x04: + chan->Loop_Addr &= 0xFF00; + chan->Loop_Addr += Data; + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "Loop low = %.2X Loop = %.8X", + // Data, chan->Loop_Addr); + break; + + case 0x05: + /* loop address registers */ + chan->Loop_Addr &= 0x00FF; + chan->Loop_Addr += Data << 8; + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "Loop high = %.2X Loop = %.8X", + // Data, chan->Loop_Addr); + break; + + case 0x06: + /* start address registers */ + chan->St_Addr = Data << (PCM_STEP_SHIFT + 8); + //chan->Addr = chan->St_Addr; + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "Start addr = %.2X New Addr = %.8X", + // Data, chan->Addr); + break; + + case 0x07: + /* control register */ + /* mod is H */ + if (Data & 0x40) + { + /* select channel */ + chip->Cur_Chan = Data & 0x07; + } + /* mod is L */ + else + { + /* pcm ram bank select */ + chip->Bank = (Data & 0x0F) << 12; + } + + /* sounding bit */ + if (Data & 0x80) + chip->Enable = 0xFF; // Used as mask + else + chip->Enable = 0; + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "General Enable = %.2X", Data); + break; + + case 0x08: + /* sound on/off register */ + Data ^= 0xFF; + + //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1, + // "Channel Enable = %.2X", Data); + + for (i = 0; i < 8; i++) + { + chan = &chip->Channel[i]; + if (!chan->Enable) + chan->Addr = chan->St_Addr; + } + + for (i = 0; i < 8; i++) + { + chip->Channel[i].Enable = Data & (1 << i); + } + } +} + + +/** + * PCM_Update(): Update the PCM buffer. + * @param buf PCM buffer. + * @param Length Buffer length. + */ +int PCM_Update(void *_info, int **buf, int Length) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + int i, j; + int *bufL, *bufR; //, *volL, *volR; + unsigned int Addr, k; + struct pcm_chan_ *CH; + + bufL = buf[0]; + bufR = buf[1]; + + // clear buffers + memset(bufL, 0, Length * sizeof(int)); + memset(bufR, 0, Length * sizeof(int)); + + // if PCM disable, no sound + if (!chip->Enable) + return 1; + +#if 0 + // faster for short update + for (j = 0; j < Length; j++) + { + for (i = 0; i < 8; i++) + { + CH = &(chip->Channel[i]); + + // only loop when sounding and on + if (CH->Enable && ! CH->Muted) + { + Addr = CH->Addr >> PCM_STEP_SHIFT; + + if (Addr & 0x10000) + { + for(k = CH->Old_Addr; k < 0x10000; k++) + { + if (chip->RAM[k] == 0xFF) + { + CH->Old_Addr = Addr = CH->Loop_Addr; + CH->Addr = Addr << PCM_STEP_SHIFT; + break; + } + } + + if (Addr & 0x10000) + { + //CH->Addr -= CH->Step; + CH->Enable = 0; + break; + } + } + else + { + for(k = CH->Old_Addr; k <= Addr; k++) + { + if (chip->RAM[k] == 0xFF) + { + CH->Old_Addr = Addr = CH->Loop_Addr; + CH->Addr = Addr << PCM_STEP_SHIFT; + break; + } + } + } + + // test for loop signal + if (chip->RAM[Addr] == 0xFF) + { + Addr = CH->Loop_Addr; + CH->Addr = Addr << PCM_STEP_SHIFT; + } + + if (chip->RAM[Addr] & 0x80) + { + CH->Data = chip->RAM[Addr] & 0x7F; + bufL[j] -= CH->Data * CH->MUL_L; + bufR[j] -= CH->Data * CH->MUL_R; + } + else + { + CH->Data = chip->RAM[Addr]; + bufL[j] += CH->Data * CH->MUL_L; + bufR[j] += CH->Data * CH->MUL_R; + } + + // update address register + //CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF; + CH->Addr += CH->Step; + CH->Old_Addr = Addr + 1; + } + } + } +#endif + +#if 1 + // for long update + for (i = 0; i < 8; i++) + { + CH = &(chip->Channel[i]); + + // only loop when sounding and on + if (CH->Enable && ! CH->Muted) + { + Addr = CH->Addr >> PCM_STEP_SHIFT; + //volL = &(PCM_Volume_Tab[CH->MUL_L << 8]); + //volR = &(PCM_Volume_Tab[CH->MUL_R << 8]); + + for (j = 0; j < Length; j++) + { + // test for loop signal + if (chip->RAM[Addr] == 0xFF) + { + CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT; + if (chip->RAM[Addr] == 0xFF) + break; + else + j--; + } + else + { + if (chip->RAM[Addr] & 0x80) + { + CH->Data = chip->RAM[Addr] & 0x7F; + bufL[j] -= CH->Data * CH->MUL_L; + bufR[j] -= CH->Data * CH->MUL_R; + } + else + { + CH->Data = chip->RAM[Addr]; + // this improves the sound of Cosmic Fantasy Stories, + // although it's definately false behaviour + if (! CH->Data && chip->Smpl0Patch) + CH->Data = -0x7F; + bufL[j] += CH->Data * CH->MUL_L; + bufR[j] += CH->Data * CH->MUL_R; + } + + // update address register + k = Addr + 1; + CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF; + Addr = CH->Addr >> PCM_STEP_SHIFT; + + for (; k < Addr; k++) + { + if (chip->RAM[k] == 0xFF) + { + CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT; + break; + } + } + } + } + + if (chip->RAM[Addr] == 0xFF) + { + CH->Addr = CH->Loop_Addr << PCM_STEP_SHIFT; + } + } + } +#endif + + return 0; +} + + +void rf5c164_update(void *chip, stream_sample_t **outputs, int samples) +{ + PCM_Update(chip, outputs, samples); +} + +int device_start_rf5c164(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + /* allocate memory for the chip */ + //rf5c164_state *chip = get_safe_token(device); + struct pcm_chip_ *chip; + int rate; + + chip = (struct pcm_chip_ *) calloc(1, sizeof(struct pcm_chip_)); + *_info = (void *) chip; + + rate = (clock & 0x7FFFFFFF) / 384; + if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + rate = CHIP_SAMPLE_RATE; + + PCM_Init(chip, rate); + chip->Smpl0Patch = (clock & 0x80000000) >> 31; + + /* allocate the stream */ + //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); + + return rate; +} + +void device_stop_rf5c164(void *_info) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + free(chip->RAM); chip->RAM = NULL; + free(chip); + + return; +} + +void device_reset_rf5c164(void *chip) +{ + //struct pcm_chip_ *chip = &PCM_Chip[ChipID]; + PCM_Reset(chip); +} + +void rf5c164_w(void *chip, offs_t offset, UINT8 data) +{ + //struct pcm_chip_ *chip = &PCM_Chip[ChipID]; + PCM_Write_Reg(chip, offset, data); +} + +void rf5c164_mem_w(void *_info, offs_t offset, UINT8 data) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + chip->RAM[chip->Bank | offset] = data; +} + +void rf5c164_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + + DataStart |= chip->Bank; + if (DataStart >= chip->RAMSize) + return; + if (DataStart + DataLength > chip->RAMSize) + DataLength = chip->RAMSize - DataStart; + + memcpy(chip->RAM + DataStart, RAMData, DataLength); + + return; +} + + +void rf5c164_set_mute_mask(void *_info, UINT32 MuteMask) +{ + struct pcm_chip_ *chip = (struct pcm_chip_ *)_info; + unsigned char CurChn; + + for (CurChn = 0; CurChn < 8; CurChn ++) + chip->Channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} diff --git a/Frameworks/GME/gme/scd_pcm.h b/Frameworks/GME/vgmplay/chips/scd_pcm.h similarity index 51% rename from Frameworks/GME/gme/scd_pcm.h rename to Frameworks/GME/vgmplay/chips/scd_pcm.h index aab658868..bd3bc384e 100644 --- a/Frameworks/GME/gme/scd_pcm.h +++ b/Frameworks/GME/vgmplay/chips/scd_pcm.h @@ -1,57 +1,47 @@ -#include "mamedef.h" - -struct pcm_chan_ { - unsigned int ENV; /* envelope register */ - unsigned int PAN; /* pan register */ - unsigned int MUL_L; /* envelope & pan product letf */ - unsigned int MUL_R; /* envelope & pan product right */ - unsigned int St_Addr; /* start address register */ - unsigned int Loop_Addr; /* loop address register */ - unsigned int Addr; /* current address register */ - unsigned int Step; /* frequency register */ - unsigned int Step_B; /* frequency register binaire */ - unsigned int Enable; /* channel on/off register */ - int Data; /* wave data */ - unsigned int Muted; -}; - -struct pcm_chip_ -{ - float Rate; - int Enable; - int Cur_Chan; - int Bank; - - struct pcm_chan_ Channel[8]; - - unsigned long int RAMSize; - unsigned char* RAM; -}; - -//extern struct pcm_chip_ PCM_Chip; -//extern unsigned char Ram_PCM[64 * 1024]; -//extern int PCM_Enable; - -//int PCM_Init(int Rate); -//void PCM_Set_Rate(int Rate); -//void PCM_Reset(void); -//void PCM_Write_Reg(unsigned int Reg, unsigned int Data); -//int PCM_Update(int **buf, int Length); - -#ifdef __cplusplus -extern "C" { -#endif - -void rf5c164_update(void *chip, stream_sample_t **outputs, int samples); -void * device_start_rf5c164(int clock); -void device_stop_rf5c164(void *chip); -void device_reset_rf5c164(void *chip); -void rf5c164_w(void *chip, offs_t offset, UINT8 data); -void rf5c164_mem_w(void *chip, offs_t offset, UINT8 data); -void rf5c164_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); - -void rf5c164_set_mute_mask(void *chip, UINT32 MuteMask); - -#ifdef __cplusplus -} -#endif +struct pcm_chip_ +{ + float Rate; + int Smpl0Patch; + int Enable; + int Cur_Chan; + int Bank; + + struct pcm_chan_ + { + unsigned int ENV; /* envelope register */ + unsigned int PAN; /* pan register */ + unsigned int MUL_L; /* envelope & pan product letf */ + unsigned int MUL_R; /* envelope & pan product right */ + unsigned int St_Addr; /* start address register */ + unsigned int Loop_Addr; /* loop address register */ + unsigned int Addr; /* current address register */ + unsigned int Step; /* frequency register */ + unsigned int Step_B; /* frequency register binaire */ + unsigned int Enable; /* channel on/off register */ + int Data; /* wave data */ + unsigned int Muted; + } Channel[8]; + + unsigned long int RAMSize; + unsigned char* RAM; +}; + +//extern struct pcm_chip_ PCM_Chip; +//extern unsigned char Ram_PCM[64 * 1024]; +//extern int PCM_Enable; + +//int PCM_Init(int Rate); +//void PCM_Set_Rate(int Rate); +//void PCM_Reset(void); +//void PCM_Write_Reg(unsigned int Reg, unsigned int Data); +//int PCM_Update(int **buf, int Length); + +void rf5c164_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_rf5c164(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_rf5c164(void *chip); +void device_reset_rf5c164(void *chip); +void rf5c164_w(void *chip, offs_t offset, UINT8 data); +void rf5c164_mem_w(void *chip, offs_t offset, UINT8 data); +void rf5c164_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); + +void rf5c164_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/scsp.c b/Frameworks/GME/vgmplay/chips/scsp.c new file mode 100644 index 000000000..d150a9a0a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/scsp.c @@ -0,0 +1,1692 @@ +/* + Sega/Yamaha YMF292-F (SCSP = Saturn Custom Sound Processor) emulation + By ElSemi + MAME/M1 conversion and cleanup by R. Belmont + Additional code and bugfixes by kingshriek + + This chip has 32 voices. Each voice can play a sample or be part of + an FM construct. Unlike traditional Yamaha FM chips, the base waveform + for the FM still comes from the wavetable RAM. + + ChangeLog: + * November 25, 2003 (ES) Fixed buggy timers and envelope overflows. + (RB) Improved sample rates other than 44100, multiple + chips now works properly. + * December 02, 2003 (ES) Added DISDL register support, improves mix. + * April 28, 2004 (ES) Corrected envelope rates, added key-rate scaling, + added ringbuffer support. + * January 8, 2005 (RB) Added ability to specify region offset for RAM. + * January 26, 2007 (ES) Added on-board DSP capability + * September 24, 2007 (RB+ES) Removed fake reverb. Rewrote timers and IRQ handling. + Fixed case where voice frequency is updated while looping. + Enabled DSP again. + * December 16, 2007 (kingshriek) Many EG bug fixes, implemented effects mixer, + implemented FM. + * January 5, 2008 (kingshriek+RB) Working, good-sounding FM, removed obsolete non-USEDSP code. + * April 22, 2009 ("PluginNinja") Improved slot monitor, misc cleanups + * June 6, 2011 (AS) Rewrote DMA from scratch, Darius 2 relies on it. +*/ + +//#include "emu.h" +#include "mamedef.h" +#include // for pow() in scsplfo.c +#include +//#include +#include // for memset +#include "scsp.h" +#include "scspdsp.h" + + +#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) + +#define SHIFT 12 +#define FIX(v) ((UINT32) ((float) (1<udata.data[0x0]>>0x0)&0x1000) +#define KEYONB(slot) ((slot->udata.data[0x0]>>0x0)&0x0800) +#define SBCTL(slot) ((slot->udata.data[0x0]>>0x9)&0x0003) +#define SSCTL(slot) ((slot->udata.data[0x0]>>0x7)&0x0003) +#define LPCTL(slot) ((slot->udata.data[0x0]>>0x5)&0x0003) +#define PCM8B(slot) ((slot->udata.data[0x0]>>0x0)&0x0010) + +#define SA(slot) (((slot->udata.data[0x0]&0xF)<<16)|(slot->udata.data[0x1])) + +#define LSA(slot) (slot->udata.data[0x2]) + +#define LEA(slot) (slot->udata.data[0x3]) + +#define D2R(slot) ((slot->udata.data[0x4]>>0xB)&0x001F) +#define D1R(slot) ((slot->udata.data[0x4]>>0x6)&0x001F) +#define EGHOLD(slot) ((slot->udata.data[0x4]>>0x0)&0x0020) +#define AR(slot) ((slot->udata.data[0x4]>>0x0)&0x001F) + +#define LPSLNK(slot) ((slot->udata.data[0x5]>>0x0)&0x4000) +#define KRS(slot) ((slot->udata.data[0x5]>>0xA)&0x000F) +#define DL(slot) ((slot->udata.data[0x5]>>0x5)&0x001F) +#define RR(slot) ((slot->udata.data[0x5]>>0x0)&0x001F) + +#define STWINH(slot) ((slot->udata.data[0x6]>>0x0)&0x0200) +#define SDIR(slot) ((slot->udata.data[0x6]>>0x0)&0x0100) +#define TL(slot) ((slot->udata.data[0x6]>>0x0)&0x00FF) + +#define MDL(slot) ((slot->udata.data[0x7]>>0xC)&0x000F) +#define MDXSL(slot) ((slot->udata.data[0x7]>>0x6)&0x003F) +#define MDYSL(slot) ((slot->udata.data[0x7]>>0x0)&0x003F) + +#define OCT(slot) ((slot->udata.data[0x8]>>0xB)&0x000F) +#define FNS(slot) ((slot->udata.data[0x8]>>0x0)&0x03FF) + +#define LFORE(slot) ((slot->udata.data[0x9]>>0x0)&0x8000) +#define LFOF(slot) ((slot->udata.data[0x9]>>0xA)&0x001F) +#define PLFOWS(slot) ((slot->udata.data[0x9]>>0x8)&0x0003) +#define PLFOS(slot) ((slot->udata.data[0x9]>>0x5)&0x0007) +#define ALFOWS(slot) ((slot->udata.data[0x9]>>0x3)&0x0003) +#define ALFOS(slot) ((slot->udata.data[0x9]>>0x0)&0x0007) + +#define ISEL(slot) ((slot->udata.data[0xA]>>0x3)&0x000F) +#define IMXL(slot) ((slot->udata.data[0xA]>>0x0)&0x0007) + +#define DISDL(slot) ((slot->udata.data[0xB]>>0xD)&0x0007) +#define DIPAN(slot) ((slot->udata.data[0xB]>>0x8)&0x001F) +#define EFSDL(slot) ((slot->udata.data[0xB]>>0x5)&0x0007) +#define EFPAN(slot) ((slot->udata.data[0xB]>>0x0)&0x001F) + +//Envelope times in ms +static const double ARTimes[64]={100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0, + 1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0, + 76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4, + 2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0}; +static const double DRTimes[64]={100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0, + 14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0, + 920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0, + 28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1}; + +typedef enum {ATTACK,DECAY1,DECAY2,RELEASE} _STATE; +struct _EG +{ + int volume; // + _STATE state; + int step; + //step vals + int AR; //Attack + int D1R; //Decay1 + int D2R; //Decay2 + int RR; //Release + + int DL; //Decay level + UINT8 EGHOLD; + UINT8 LPLINK; +}; + +struct _SLOT +{ + union + { + UINT16 data[0x10]; //only 0x1a bytes used + UINT8 datab[0x20]; + } udata; + UINT8 Backwards; //the wave is playing backwards + UINT8 active; //this slot is currently playing + UINT8 Muted; + UINT8 *base; //samples base address + UINT32 cur_addr; //current play address (24.8) + UINT32 nxt_addr; //next play address + UINT32 step; //pitch step (24.8) + struct _EG EG; //Envelope + struct _LFO PLFO; //Phase LFO + struct _LFO ALFO; //Amplitude LFO + int slot; + signed short Prev; //Previous sample (for interpolation) +}; + + +#define MEM4B(scsp) ((scsp->udata.data[0]>>0x0)&0x0200) +#define DAC18B(scsp) ((scsp->udata.data[0]>>0x0)&0x0100) +#define MVOL(scsp) ((scsp->udata.data[0]>>0x0)&0x000F) +#define RBL(scsp) ((scsp->udata.data[1]>>0x7)&0x0003) +#define RBP(scsp) ((scsp->udata.data[1]>>0x0)&0x003F) +#define MOFULL(scsp) ((scsp->udata.data[2]>>0x0)&0x1000) +#define MOEMPTY(scsp) ((scsp->udata.data[2]>>0x0)&0x0800) +#define MIOVF(scsp) ((scsp->udata.data[2]>>0x0)&0x0400) +#define MIFULL(scsp) ((scsp->udata.data[2]>>0x0)&0x0200) +#define MIEMPTY(scsp) ((scsp->udata.data[2]>>0x0)&0x0100) + +#define SCILV0(scsp) ((scsp->udata.data[0x24/2]>>0x0)&0xff) +#define SCILV1(scsp) ((scsp->udata.data[0x26/2]>>0x0)&0xff) +#define SCILV2(scsp) ((scsp->udata.data[0x28/2]>>0x0)&0xff) + +#define SCIEX0 0 +#define SCIEX1 1 +#define SCIEX2 2 +#define SCIMID 3 +#define SCIDMA 4 +#define SCIIRQ 5 +#define SCITMA 6 +#define SCITMB 7 + +#define USEDSP + +typedef struct _scsp_state scsp_state; +struct _scsp_state +{ + union + { + UINT16 data[0x30/2]; + UINT8 datab[0x30]; + } udata; + struct _SLOT Slots[32]; + signed short RINGBUF[128]; + unsigned char BUFPTR; +#if FM_DELAY + signed short DELAYBUF[FM_DELAY]; + unsigned char DELAYPTR; +#endif + unsigned char *SCSPRAM; + UINT32 SCSPRAM_LENGTH; + //char Master; + //void (*Int68kCB)(device_t *device, int irq); + //sound_stream * stream; + int clock; + int rate; + + //INT32 *buffertmpl,*buffertmpr; + + /*UINT32 IrqTimA; + UINT32 IrqTimBC; + UINT32 IrqMidi;*/ + + UINT8 MidiOutW,MidiOutR; + UINT8 MidiStack[32]; + UINT8 MidiW,MidiR; + + INT32 EG_TABLE[0x400]; + + int LPANTABLE[0x10000]; + int RPANTABLE[0x10000]; + + int TimPris[3]; + int TimCnt[3]; + + // timers + //emu_timer *timerA, *timerB, *timerC; + + // DMA stuff + struct + { + UINT32 dmea; + UINT16 drga; + UINT16 dtlg; + UINT8 dgate; + UINT8 ddir; + } dma; + + UINT16 mcieb; + UINT16 mcipd; + + int ARTABLE[64], DRTABLE[64]; + + struct _SCSPDSP DSP; + //devcb_resolved_write_line main_irq; + + //device_t *device; + + signed short *RBUFDST; + + UINT8 BypassDSP; +}; + +//static void SCSP_exec_dma(address_space *space, scsp_state *scsp); /*state DMA transfer function*/ +/* TODO */ +//#define dma_transfer_end ((scsp_regs[0x24/2] & 0x10)>>4)|(((scsp_regs[0x26/2] & 0x10)>>4)<<1)|(((scsp_regs[0x28/2] & 0x10)>>4)<<2) + +static const float SDLT[8]={-1000000.0f,-36.0f,-30.0f,-24.0f,-18.0f,-12.0f,-6.0f,0.0f}; + +//static stream_sample_t *bufferl; +//static stream_sample_t *bufferr; + +//static int length; + + +/*INLINE scsp_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == SCSP); + return (scsp_state *)downcast(device)->token(); +}*/ + +static unsigned char DecodeSCI(scsp_state *scsp,unsigned char irq) +{ + unsigned char SCI=0; + unsigned char v; + v=(SCILV0((scsp))&(1<udata.data[0x20/2]; + UINT32 en=scsp->udata.data[0x1e/2]; + + if(scsp->MidiW!=scsp->MidiR) + { + scsp->udata.data[0x20/2] |= 8; + pend |= 8; + } + if(!pend) + return; + if(pend&0x40) + if(en&0x40) + { + scsp->Int68kCB(scsp->device, scsp->IrqTimA); + return; + } + if(pend&0x80) + if(en&0x80) + { + scsp->Int68kCB(scsp->device, scsp->IrqTimBC); + return; + } + if(pend&0x100) + if(en&0x100) + { + scsp->Int68kCB(scsp->device, scsp->IrqTimBC); + return; + } + if(pend&8) + if (en&8) + { + scsp->Int68kCB(scsp->device, scsp->IrqMidi); + scsp->udata.data[0x20/2] &= ~8; + return; + } + + scsp->Int68kCB(scsp->device, 0); +} + +static void ResetInterrupts(scsp_state *scsp) +{ + UINT32 reset = scsp->udata.data[0x22/2]; + + if (reset & 0x40) + { + scsp->Int68kCB(scsp->device, -scsp->IrqTimA); + } + if (reset & 0x180) + { + scsp->Int68kCB(scsp->device, -scsp->IrqTimBC); + } + if (reset & 0x8) + { + scsp->Int68kCB(scsp->device, -scsp->IrqMidi); + } + + CheckPendingIRQ(scsp); +} + +static TIMER_CALLBACK( timerA_cb ) +{ + scsp_state *scsp = (scsp_state *)ptr; + + scsp->TimCnt[0] = 0xFFFF; + scsp->udata.data[0x20/2]|=0x40; + scsp->udata.data[0x18/2]&=0xff00; + scsp->udata.data[0x18/2]|=scsp->TimCnt[0]>>8; + + CheckPendingIRQ(scsp); +} + +static TIMER_CALLBACK( timerB_cb ) +{ + scsp_state *scsp = (scsp_state *)ptr; + + scsp->TimCnt[1] = 0xFFFF; + scsp->udata.data[0x20/2]|=0x80; + scsp->udata.data[0x1a/2]&=0xff00; + scsp->udata.data[0x1a/2]|=scsp->TimCnt[1]>>8; + + CheckPendingIRQ(scsp); +} + +static TIMER_CALLBACK( timerC_cb ) +{ + scsp_state *scsp = (scsp_state *)ptr; + + scsp->TimCnt[2] = 0xFFFF; + scsp->udata.data[0x20/2]|=0x100; + scsp->udata.data[0x1c/2]&=0xff00; + scsp->udata.data[0x1c/2]|=scsp->TimCnt[2]>>8; + + CheckPendingIRQ(scsp); +}*/ + +static int Get_AR(scsp_state *scsp,int base,int R) +{ + int Rate=base+(R<<1); + if(Rate>63) Rate=63; + if(Rate<0) Rate=0; + return scsp->ARTABLE[Rate]; +} + +static int Get_DR(scsp_state *scsp,int base,int R) +{ + int Rate=base+(R<<1); + if(Rate>63) Rate=63; + if(Rate<0) Rate=0; + return scsp->DRTABLE[Rate]; +} + +static int Get_RR(scsp_state *scsp,int base,int R) +{ + int Rate=base+(R<<1); + if(Rate>63) Rate=63; + if(Rate<0) Rate=0; + return scsp->DRTABLE[Rate]; +} + +static void Compute_EG(scsp_state *scsp,struct _SLOT *slot) +{ + int octave=(OCT(slot)^8)-8; + int rate; + if(KRS(slot)!=0xf) + rate=octave+2*KRS(slot)+((FNS(slot)>>9)&1); + else + rate=0; //rate=((FNS(slot)>>9)&1); + + slot->EG.volume=0x17F<EG.AR=Get_AR(scsp,rate,AR(slot)); + slot->EG.D1R=Get_DR(scsp,rate,D1R(slot)); + slot->EG.D2R=Get_DR(scsp,rate,D2R(slot)); + slot->EG.RR=Get_RR(scsp,rate,RR(slot)); + slot->EG.DL=0x1f-DL(slot); + slot->EG.EGHOLD=EGHOLD(slot); +} + +static void SCSP_StopSlot(struct _SLOT *slot,int keyoff); + +static int EG_Update(struct _SLOT *slot) +{ + switch(slot->EG.state) + { + case ATTACK: + slot->EG.volume+=slot->EG.AR; + if(slot->EG.volume>=(0x3ff<EG.state=DECAY1; + if(slot->EG.D1R>=(1024<EG.state=DECAY2; + } + slot->EG.volume=0x3ff<EG.EGHOLD) + return 0x3ff<<(SHIFT-10); + break; + case DECAY1: + slot->EG.volume-=slot->EG.D1R; + if(slot->EG.volume<=0) + slot->EG.volume=0; + if(slot->EG.volume>>(EG_SHIFT+5)<=slot->EG.DL) + slot->EG.state=DECAY2; + break; + case DECAY2: + if(D2R(slot)==0) + return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10); + slot->EG.volume-=slot->EG.D2R; + if(slot->EG.volume<=0) + slot->EG.volume=0; + + break; + case RELEASE: + slot->EG.volume-=slot->EG.RR; + if(slot->EG.volume<=0) + { + slot->EG.volume=0; + SCSP_StopSlot(slot,0); + //slot->EG.volume=0x17F<EG.state=ATTACK; + } + break; + default: + return 1<EG.volume>>EG_SHIFT)<<(SHIFT-10); +} + +static UINT32 SCSP_Step(struct _SLOT *slot) +{ + int octave=(OCT(slot)^8)-8+SHIFT-10; + UINT32 Fn=FNS(slot)+(1 << 10); + if (octave >= 0) + { + Fn<<=octave; + } + else + { + Fn>>=-octave; + } + + return Fn; +} + + +static void Compute_LFO(struct _SLOT *slot) +{ + if(PLFOS(slot)!=0) + LFO_ComputeStep(&(slot->PLFO),LFOF(slot),PLFOWS(slot),PLFOS(slot),0); + if(ALFOS(slot)!=0) + LFO_ComputeStep(&(slot->ALFO),LFOF(slot),ALFOWS(slot),ALFOS(slot),1); +} + +static void SCSP_StartSlot(scsp_state *scsp, struct _SLOT *slot) +{ + UINT32 start_offset; + + slot->active=1; + start_offset = PCM8B(slot) ? SA(slot) : SA(slot) & 0x7FFFE; + slot->base=scsp->SCSPRAM + start_offset; + slot->cur_addr=0; + slot->nxt_addr=1<step=SCSP_Step(slot); + Compute_EG(scsp,slot); + slot->EG.state=ATTACK; + slot->EG.volume=0x17F<Prev=0; + slot->Backwards=0; + + Compute_LFO(slot); + +// printf("StartSlot[%p]: SA %x PCM8B %x LPCTL %x ALFOS %x STWINH %x TL %x EFSDL %x\n", slot, SA(slot), PCM8B(slot), LPCTL(slot), ALFOS(slot), STWINH(slot), TL(slot), EFSDL(slot)); +} + +static void SCSP_StopSlot(struct _SLOT *slot,int keyoff) +{ + if(keyoff /*&& slot->EG.state!=RELEASE*/) + { + slot->EG.state=RELEASE; + } + else + { + slot->active=0; + } + slot->udata.data[0]&=~0x800; +} + +#define log_base_2(n) (log((double)(n))/log(2.0)) + +//static void SCSP_Init(device_t *device, scsp_state *scsp, const scsp_interface *intf) +static void SCSP_Init(scsp_state *scsp, int clock) +{ + int i; + + memset(scsp,0,sizeof(*scsp)); + + SCSPDSP_Init(&scsp->DSP); + + //scsp->device = device; + scsp->clock = clock; + scsp->rate = clock / 512; + + //scsp->IrqTimA = scsp->IrqTimBC = scsp->IrqMidi = 0; + scsp->MidiR=scsp->MidiW=0; + scsp->MidiOutR=scsp->MidiOutW=0; + + // get SCSP RAM + /*if (strcmp(device->tag(), "scsp") == 0 || strcmp(device->tag(), "scsp1") == 0) + { + scsp->Master=1; + } + else + { + scsp->Master=0; + }*/ + + /*scsp->SCSPRAM = *device->region(); + if (scsp->SCSPRAM) + { + scsp->SCSPRAM_LENGTH = device->region()->bytes(); + scsp->DSP.SCSPRAM = (UINT16 *)scsp->SCSPRAM; + scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH/2; + scsp->SCSPRAM += intf->roffset; + }*/ + scsp->SCSPRAM_LENGTH = 0x80000; // 512 KB + scsp->SCSPRAM = (unsigned char*)malloc(scsp->SCSPRAM_LENGTH); + scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH / 2; + scsp->DSP.SCSPRAM = (UINT16*)scsp->SCSPRAM; + + /*scsp->timerA = device->machine().scheduler().timer_alloc(FUNC(timerA_cb), scsp); + scsp->timerB = device->machine().scheduler().timer_alloc(FUNC(timerB_cb), scsp); + scsp->timerC = device->machine().scheduler().timer_alloc(FUNC(timerC_cb), scsp);*/ + + for(i=0;i<0x400;++i) + { + float envDB=((float)(3*(i-0x3ff)))/32.0f; + float scale=(float)(1<EG_TABLE[i]=(INT32)(pow(10.0,envDB/20.0)*scale); + } + + for(i=0;i<0x10000;++i) + { + int iTL =(i>>0x0)&0xff; + int iPAN=(i>>0x8)&0x1f; + int iSDL=(i>>0xD)&0x07; + float TL=1.0f; + float SegaDB=0.0f; + float fSDL=1.0f; + float PAN=1.0f; + float LPAN,RPAN; + + if(iTL&0x01) SegaDB-=0.4f; + if(iTL&0x02) SegaDB-=0.8f; + if(iTL&0x04) SegaDB-=1.5f; + if(iTL&0x08) SegaDB-=3.0f; + if(iTL&0x10) SegaDB-=6.0f; + if(iTL&0x20) SegaDB-=12.0f; + if(iTL&0x40) SegaDB-=24.0f; + if(iTL&0x80) SegaDB-=48.0f; + + TL=pow(10.0,SegaDB/20.0); + + SegaDB=0; + if(iPAN&0x1) SegaDB-=3.0f; + if(iPAN&0x2) SegaDB-=6.0f; + if(iPAN&0x4) SegaDB-=12.0f; + if(iPAN&0x8) SegaDB-=24.0f; + + if((iPAN&0xf)==0xf) PAN=0.0; + else PAN=pow(10.0,SegaDB/20.0); + + if(iPAN<0x10) + { + LPAN=PAN; + RPAN=1.0; + } + else + { + RPAN=PAN; + LPAN=1.0; + } + + if(iSDL) + fSDL=pow(10.0,(SDLT[iSDL])/20.0); + else + fSDL=0.0; + + scsp->LPANTABLE[i]=FIX((4.0*LPAN*TL*fSDL)); + scsp->RPANTABLE[i]=FIX((4.0*RPAN*TL*fSDL)); + } + + scsp->ARTABLE[0]=scsp->DRTABLE[0]=0; //Infinite time + scsp->ARTABLE[1]=scsp->DRTABLE[1]=0; //Infinite time + for(i=2;i<64;++i) + { + double t,step,scale; + t=ARTimes[i]; //In ms + if(t!=0.0) + { + step=(1023*1000.0)/((float) 44100.0f*t); + scale=(double) (1<ARTABLE[i]=(int) (step*scale); + } + else + scsp->ARTABLE[i]=1024<DRTABLE[i]=(int) (step*scale); + } + + // make sure all the slots are off + for(i=0;i<32;++i) + { + scsp->Slots[i].slot=i; + scsp->Slots[i].active=0; + scsp->Slots[i].base=NULL; + scsp->Slots[i].EG.state=RELEASE; + } + + //LFO_Init(device->machine()); + LFO_Init(); + //scsp->buffertmpl=auto_alloc_array_clear(device->machine(), signed int, 44100); + //scsp->buffertmpr=auto_alloc_array_clear(device->machine(), signed int, 44100); + + // no "pend" + scsp->udata.data[0x20/2] = 0; + scsp->TimCnt[0] = 0xffff; + scsp->TimCnt[1] = 0xffff; + scsp->TimCnt[2] = 0xffff; +} + +INLINE void SCSP_UpdateSlotReg(scsp_state *scsp,int s,int r) +{ + struct _SLOT *slot=scsp->Slots+s; + int sl; + switch(r&0x3f) + { + case 0: + case 1: + if(KEYONEX(slot)) + { + for(sl=0;sl<32;++sl) + { + struct _SLOT *s2=scsp->Slots+sl; + { + if(KEYONB(s2) && s2->EG.state==RELEASE/*&& !s2->active*/) + { + SCSP_StartSlot(scsp, s2); + } + if(!KEYONB(s2) /*&& s2->active*/) + { + SCSP_StopSlot(s2,1); + } + } + } + slot->udata.data[0]&=~0x1000; + } + break; + case 0x10: + case 0x11: + slot->step=SCSP_Step(slot); + break; + case 0xA: + case 0xB: + slot->EG.RR=Get_RR(scsp,0,RR(slot)); + slot->EG.DL=0x1f-DL(slot); + break; + case 0x12: + case 0x13: + Compute_LFO(slot); + break; + } +} + +INLINE void SCSP_UpdateReg(scsp_state *scsp, /*address_space &space,*/ int reg) +{ + switch(reg&0x3f) + { + case 0x0: + // TODO: Make this work in VGMPlay + //scsp->stream->set_output_gain(0,MVOL(scsp) / 15.0); + //scsp->stream->set_output_gain(1,MVOL(scsp) / 15.0); + break; + case 0x2: + case 0x3: + { + unsigned int v=RBL(scsp); + scsp->DSP.RBP=RBP(scsp); + if(v==0) + scsp->DSP.RBL=8*1024; + else if(v==1) + scsp->DSP.RBL=16*1024; + else if(v==2) + scsp->DSP.RBL=32*1024; + else if(v==3) + scsp->DSP.RBL=64*1024; + } + break; + case 0x6: + case 0x7: + //scsp_midi_in(space->machine().device("scsp"), 0, scsp->udata.data[0x6/2]&0xff, 0); + break; + case 8: + case 9: + /* Only MSLC could be written. */ + scsp->udata.data[0x8/2] &= 0x7800; + break; + case 0x12: + case 0x13: + //scsp->dma.dmea = (scsp->udata.data[0x12/2] & 0xfffe) | (scsp->dma.dmea & 0xf0000); + break; + case 0x14: + case 0x15: + //scsp->dma.dmea = ((scsp->udata.data[0x14/2] & 0xf000) << 4) | (scsp->dma.dmea & 0xfffe); + //scsp->dma.drga = (scsp->udata.data[0x14/2] & 0x0ffe); + break; + case 0x16: + case 0x17: + //scsp->dma.dtlg = (scsp->udata.data[0x16/2] & 0x0ffe); + //scsp->dma.ddir = (scsp->udata.data[0x16/2] & 0x2000) >> 13; + //scsp->dma.dgate = (scsp->udata.data[0x16/2] & 0x4000) >> 14; + //if(scsp->udata.data[0x16/2] & 0x1000) // dexe + // SCSP_exec_dma(space, scsp); + break; + case 0x18: + case 0x19: + /*if(scsp->Master) + { + UINT32 time; + + scsp->TimPris[0]=1<<((scsp->udata.data[0x18/2]>>8)&0x7); + scsp->TimCnt[0]=(scsp->udata.data[0x18/2]&0xff)<<8; + + if ((scsp->udata.data[0x18/2]&0xff) != 255) + { + time = (44100 / scsp->TimPris[0]) / (255-(scsp->udata.data[0x18/2]&0xff)); + if (time) + { + scsp->timerA->adjust(attotime::from_hz(time)); + } + } + }*/ + break; + case 0x1a: + case 0x1b: + /*if(scsp->Master) + { + UINT32 time; + + scsp->TimPris[1]=1<<((scsp->udata.data[0x1A/2]>>8)&0x7); + scsp->TimCnt[1]=(scsp->udata.data[0x1A/2]&0xff)<<8; + + if ((scsp->udata.data[0x1A/2]&0xff) != 255) + { + time = (44100 / scsp->TimPris[1]) / (255-(scsp->udata.data[0x1A/2]&0xff)); + if (time) + { + scsp->timerB->adjust(attotime::from_hz(time)); + } + } + }*/ + break; + case 0x1C: + case 0x1D: + /*if(scsp->Master) + { + UINT32 time; + + scsp->TimPris[2]=1<<((scsp->udata.data[0x1C/2]>>8)&0x7); + scsp->TimCnt[2]=(scsp->udata.data[0x1C/2]&0xff)<<8; + + if ((scsp->udata.data[0x1C/2]&0xff) != 255) + { + time = (44100 / scsp->TimPris[2]) / (255-(scsp->udata.data[0x1C/2]&0xff)); + if (time) + { + scsp->timerC->adjust(attotime::from_hz(time)); + } + } + }*/ + break; + case 0x1e: // SCIEB + case 0x1f: + /*if(scsp->Master) + { + CheckPendingIRQ(scsp); + + if(scsp->udata.data[0x1e/2] & 0x610) + popmessage("SCSP SCIEB enabled %04x, contact MAMEdev",scsp->udata.data[0x1e/2]); + }*/ + break; + case 0x20: // SCIPD + case 0x21: + /*if(scsp->Master) + { + if(scsp->udata.data[0x1e/2] & scsp->udata.data[0x20/2] & 0x20) + popmessage("SCSP SCIPD write %04x, contact MAMEdev",scsp->udata.data[0x20/2]); + }*/ + break; + case 0x22: //SCIRE + case 0x23: + + /*if(scsp->Master) + { + scsp->udata.data[0x20/2]&=~scsp->udata.data[0x22/2]; + ResetInterrupts(scsp); + + // behavior from real hardware: if you SCIRE a timer that's expired, + // it'll immediately pop up again in SCIPD. ask Sakura Taisen on the Saturn... + if (scsp->TimCnt[0] == 0xffff) + { + scsp->udata.data[0x20/2] |= 0x40; + } + if (scsp->TimCnt[1] == 0xffff) + { + scsp->udata.data[0x20/2] |= 0x80; + } + if (scsp->TimCnt[2] == 0xffff) + { + scsp->udata.data[0x20/2] |= 0x100; + } + }*/ + break; + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + /*if(scsp->Master) + { + scsp->IrqTimA=DecodeSCI(scsp,SCITMA); + scsp->IrqTimBC=DecodeSCI(scsp,SCITMB); + scsp->IrqMidi=DecodeSCI(scsp,SCIMID); + }*/ + break; + case 0x2a: + case 0x2b: + scsp->mcieb = scsp->udata.data[0x2a/2]; + + /*MainCheckPendingIRQ(scsp, 0); + if(scsp->mcieb & ~0x60) + popmessage("SCSP MCIEB enabled %04x, contact MAMEdev",scsp->mcieb);*/ + break; + case 0x2c: + case 0x2d: + //if(scsp->udata.data[0x2c/2] & 0x20) + // MainCheckPendingIRQ(scsp, 0x20); + break; + case 0x2e: + case 0x2f: + scsp->mcipd &= ~scsp->udata.data[0x2e/2]; + //MainCheckPendingIRQ(scsp, 0); + break; + + } +} + +static void SCSP_UpdateSlotRegR(scsp_state *scsp, int slot,int reg) +{ + +} + +static void SCSP_UpdateRegR(scsp_state *scsp, int reg) +{ + switch(reg&0x3f) + { + case 4: + case 5: + { + unsigned short v=scsp->udata.data[0x5/2]; + v&=0xff00; + v|=scsp->MidiStack[scsp->MidiR]; + /*scsp->Int68kCB(scsp->device, -scsp->IrqMidi); // cancel the IRQ + logerror("Read %x from SCSP MIDI\n", v);*/ + if(scsp->MidiR!=scsp->MidiW) + { + ++scsp->MidiR; + scsp->MidiR&=31; + } + scsp->udata.data[0x5/2]=v; + } + break; + case 8: + case 9: + { + // MSLC | CA |SGC|EG + // f e d c b a 9 8 7 6 5 4 3 2 1 0 + unsigned char MSLC=(scsp->udata.data[0x8/2]>>11)&0x1f; + struct _SLOT *slot=scsp->Slots + MSLC; + unsigned int SGC = (slot->EG.state) & 3; + unsigned int CA = (slot->cur_addr>>(SHIFT+12)) & 0xf; + unsigned int EG = (0x1f - (slot->EG.volume>>(EG_SHIFT+5))) & 0x1f; + /* note: according to the manual MSLC is write only, CA, SGC and EG read only. */ + scsp->udata.data[0x8/2] = /*(MSLC << 11) |*/ (CA << 7) | (SGC << 5) | EG; + } + break; + + case 0x18: + case 0x19: + break; + + case 0x1a: + case 0x1b: + break; + + case 0x1c: + case 0x1d: + break; + + case 0x2a: + case 0x2b: + scsp->udata.data[0x2a/2] = scsp->mcieb; + break; + + case 0x2c: + case 0x2d: + scsp->udata.data[0x2c/2] = scsp->mcipd; + break; + } +} + +INLINE void SCSP_w16(scsp_state *scsp,unsigned int addr,unsigned short val) +{ + addr&=0xffff; + if(addr<0x400) + { + int slot=addr/0x20; + addr&=0x1f; + *((unsigned short *) (scsp->Slots[slot].udata.datab+(addr))) = val; + SCSP_UpdateSlotReg(scsp,slot,addr&0x1f); + } + else if(addr<0x600) + { + if (addr < 0x430) + { + *((unsigned short *) (scsp->udata.datab+((addr&0x3f)))) = val; + SCSP_UpdateReg(scsp, addr&0x3f); + } + } + else if(addr<0x700) + scsp->RINGBUF[(addr-0x600)/2]=val; + else + { + //DSP + if(addr<0x780) //COEF + *((unsigned short *) (scsp->DSP.COEF+(addr-0x700)/2))=val; + else if(addr<0x7c0) + *((unsigned short *) (scsp->DSP.MADRS+(addr-0x780)/2))=val; + else if(addr<0x800) // MADRS is mirrored twice + *((unsigned short *) (scsp->DSP.MADRS+(addr-0x7c0)/2))=val; + else if(addr<0xC00) + { + *((unsigned short *) (scsp->DSP.MPRO+(addr-0x800)/2))=val; + + if(addr==0xBF0) + { + SCSPDSP_Start(&scsp->DSP); + } + } + } +} + +INLINE unsigned short SCSP_r16(scsp_state *scsp, unsigned int addr) +{ + unsigned short v=0; + addr&=0xffff; + if(addr<0x400) + { + int slot=addr/0x20; + addr&=0x1f; + SCSP_UpdateSlotRegR(scsp, slot,addr&0x1f); + v=*((unsigned short *) (scsp->Slots[slot].udata.datab+(addr))); + } + else if(addr<0x600) + { + if (addr < 0x430) + { + SCSP_UpdateRegR(scsp, addr&0x3f); + v= *((unsigned short *) (scsp->udata.datab+((addr&0x3f)))); + } + } + else if(addr<0x700) + v=scsp->RINGBUF[(addr-0x600)/2]; +#if 1 // disabled by default until I get the DSP to work correctly + // can be enabled using separate option + else + { + //DSP + if(addr<0x780) //COEF + v= *((unsigned short *) (scsp->DSP.COEF+(addr-0x700)/2)); + else if(addr<0x7c0) + v= *((unsigned short *) (scsp->DSP.MADRS+(addr-0x780)/2)); + else if(addr<0x800) + v= *((unsigned short *) (scsp->DSP.MADRS+(addr-0x7c0)/2)); + else if(addr<0xC00) + v= *((unsigned short *) (scsp->DSP.MPRO+(addr-0x800)/2)); + else if(addr<0xE00) + { + if(addr & 2) + v= scsp->DSP.TEMP[(addr >> 2) & 0x7f] & 0xffff; + else + v= scsp->DSP.TEMP[(addr >> 2) & 0x7f] >> 16; + } + else if(addr<0xE80) + { + if(addr & 2) + v= scsp->DSP.MEMS[(addr >> 2) & 0x1f] & 0xffff; + else + v= scsp->DSP.MEMS[(addr >> 2) & 0x1f] >> 16; + } + else if(addr<0xEC0) + { + if(addr & 2) + v= scsp->DSP.MIXS[(addr >> 2) & 0xf] & 0xffff; + else + v= scsp->DSP.MIXS[(addr >> 2) & 0xf] >> 16; + } + else if(addr<0xEE0) + v= *((unsigned short *) (scsp->DSP.EFREG+(addr-0xec0)/2)); + else + { + /* + Kyuutenkai reads from 0xee0/0xee2, it's tied with EXTS register(s) also used for CD-Rom Player equalizer. + This port is actually an external parallel port, directly connected from the CD Block device, hence code is a bit of an hack. + */ + logerror("SCSP: Reading from EXTS register %08x\n",addr); + //if(addr == 0xee0) + // v = space.machine().device("cdda")->get_channel_volume(0); + //if(addr == 0xee2) + // v = space.machine().device("cdda")->get_channel_volume(1); + v = 0xFFFF; + } + } +#endif + return v; +} + + +#define REVSIGN(v) ((~v)+1) + +INLINE INT32 SCSP_UpdateSlot(scsp_state *scsp, struct _SLOT *slot) +{ + INT32 sample; + int step=slot->step; + UINT32 addr1,addr2,addr_select; // current and next sample addresses + UINT32 *addr[2] = {&addr1, &addr2}; // used for linear interpolation + UINT32 *slot_addr[2] = {&(slot->cur_addr), &(slot->nxt_addr)}; // + + if(SSCTL(slot)!=0) //no FM or noise yet + return 0; + + if(PLFOS(slot)!=0) + { + step=step*PLFO_Step(&(slot->PLFO)); + step>>=SHIFT; + } + + if(PCM8B(slot)) + { + addr1=slot->cur_addr>>SHIFT; + addr2=slot->nxt_addr>>SHIFT; + } + else + { + addr1=(slot->cur_addr>>(SHIFT-1))&0x7fffe; + addr2=(slot->nxt_addr>>(SHIFT-1))&0x7fffe; + } + + if(MDL(slot)!=0 || MDXSL(slot)!=0 || MDYSL(slot)!=0) + { + INT32 smp=(scsp->RINGBUF[(scsp->BUFPTR+MDXSL(slot))&63]+scsp->RINGBUF[(scsp->BUFPTR+MDYSL(slot))&63])/2; + + smp<<=0xA; // associate cycle with 1024 + smp>>=0x1A-MDL(slot); // ex. for MDL=0xF, sample range corresponds to +/- 64 pi (32=2^5 cycles) so shift by 11 (16-5 == 0x1A-0xF) + if(!PCM8B(slot)) smp<<=1; + + addr1+=smp; addr2+=smp; + } + +#if 0 + // Since the SCSP is for Big Endian platforms, this code expects the data in + // byte order 1 0 3 2 5 4 .... + if(PCM8B(slot)) //8 bit signed + { + INT8 *p1=(signed char *) (scsp->SCSPRAM+BYTE_XOR_BE(((SA(slot)+addr1))&0x7FFFF)); + INT8 *p2=(signed char *) (scsp->SCSPRAM+BYTE_XOR_BE(((SA(slot)+addr2))&0x7FFFF)); + //sample=(p[0])<<8; + INT32 s; + INT32 fpart=slot->cur_addr&((1<>SHIFT); + } + else //16 bit signed (endianness?) + { + INT16 *p1=(signed short *) (scsp->SCSPRAM+((SA(slot)+addr1)&0x7FFFE)); + INT16 *p2=(signed short *) (scsp->SCSPRAM+((SA(slot)+addr2)&0x7FFFE)); + INT32 s; + INT32 fpart=slot->cur_addr&((1<>SHIFT); + } +#else +#define READ_BE16(ptr) (((ptr)[0] << 8) | (ptr)[1]) + // I prefer the byte order 0 1 2 3 4 5 ... + // also, I won't use pointers here, since they only used [0] on them anyway. + if(PCM8B(slot)) //8 bit signed + { + INT8 p1=(INT8)scsp->SCSPRAM[(SA(slot)+addr1)&0x7FFFF]; + INT8 p2=(INT8)scsp->SCSPRAM[(SA(slot)+addr2)&0x7FFFF]; + INT32 s; + INT32 fpart=slot->cur_addr&((1<>SHIFT); + } + else //16 bit signed + { + UINT8 *pp1 = &scsp->SCSPRAM[(SA(slot)+addr1)&0x7FFFE]; + UINT8 *pp2 = &scsp->SCSPRAM[(SA(slot)+addr2)&0x7FFFE]; + INT16 p1 = (INT16)READ_BE16(pp1); + INT16 p2 = (INT16)READ_BE16(pp2); + INT32 s; + INT32 fpart=slot->cur_addr&((1<>SHIFT); + } +#endif + + if(SBCTL(slot)&0x1) + sample ^= 0x7FFF; + if(SBCTL(slot)&0x2) + sample = (INT16)(sample^0x8000); + + if(slot->Backwards) + slot->cur_addr-=step; + else + slot->cur_addr+=step; + slot->nxt_addr=slot->cur_addr+(1<cur_addr>>SHIFT; + addr2=slot->nxt_addr>>SHIFT; + + if(addr1>=LSA(slot) && !(slot->Backwards)) + { + if(LPSLNK(slot) && slot->EG.state==ATTACK) + slot->EG.state = DECAY1; + } + + for (addr_select=0;addr_select<2;addr_select++) + { + INT32 rem_addr; + switch(LPCTL(slot)) + { + case 0: //no loop + if(*addr[addr_select]>=LSA(slot) && *addr[addr_select]>=LEA(slot)) + { + //slot->active=0; + SCSP_StopSlot(slot,0); + } + break; + case 1: //normal loop + if(*addr[addr_select]>=LEA(slot)) + { + rem_addr = *slot_addr[addr_select] - (LEA(slot)<=LSA(slot)) && !(slot->Backwards)) + { + rem_addr = *slot_addr[addr_select] - (LSA(slot)<Backwards=1; + } + else if((*addr[addr_select]Backwards) + { + rem_addr = (LSA(slot)<=LEA(slot)) //reached end, reverse till start + { + rem_addr = *slot_addr[addr_select] - (LEA(slot)<Backwards=1; + } + else if((*addr[addr_select]Backwards)//reached start or negative + { + rem_addr = (LSA(slot)<Backwards=0; + } + break; + } + } + + if(!SDIR(slot)) + { + if(ALFOS(slot)!=0) + { + sample=sample*ALFO_Step(&(slot->ALFO)); + sample>>=SHIFT; + } + + if(slot->EG.state==ATTACK) + sample=(sample*EG_Update(slot))>>SHIFT; + else + sample=(sample*scsp->EG_TABLE[EG_Update(slot)>>(SHIFT-10)])>>SHIFT; + } + + if(!STWINH(slot)) + { + if(!SDIR(slot)) + { + unsigned short Enc=((TL(slot))<<0x0)|(0x7<<0xd); + *scsp->RBUFDST=(sample*scsp->LPANTABLE[Enc])>>(SHIFT+1); + } + else + { + unsigned short Enc=(0<<0x0)|(0x7<<0xd); + *scsp->RBUFDST=(sample*scsp->LPANTABLE[Enc])>>(SHIFT+1); + } + } + + return sample; +} + +INLINE void SCSP_DoMasterSamples(scsp_state *scsp, stream_sample_t **outputs, int nsamples) +{ + stream_sample_t *bufr,*bufl; + int sl, s, i; + + //bufr=bufferr; + //bufl=bufferl; + bufl = outputs[0]; + bufr = outputs[1]; + + for(s=0;sRBUFDST=scsp->DELAYBUF+scsp->DELAYPTR; +#else + scsp->RBUFDST=scsp->RINGBUF+scsp->BUFPTR; +#endif + if(scsp->Slots[sl].active && ! scsp->Slots[sl].Muted) + { + struct _SLOT *slot=scsp->Slots+sl; + unsigned short Enc; + signed int sample; + + sample=SCSP_UpdateSlot(scsp, slot); + + if (! scsp->BypassDSP) + { + Enc=((TL(slot))<<0x0)|((IMXL(slot))<<0xd); + SCSPDSP_SetSample(&scsp->DSP,(sample*scsp->LPANTABLE[Enc])>>(SHIFT-2),ISEL(slot),IMXL(slot)); + } + Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((DISDL(slot))<<0xd); + { + smpl+=(sample*scsp->LPANTABLE[Enc])>>SHIFT; + smpr+=(sample*scsp->RPANTABLE[Enc])>>SHIFT; + } + } + +#if FM_DELAY + scsp->RINGBUF[(scsp->BUFPTR+64-(FM_DELAY-1))&63] = scsp->DELAYBUF[(scsp->DELAYPTR+FM_DELAY-(FM_DELAY-1))%FM_DELAY]; +#endif + ++scsp->BUFPTR; + scsp->BUFPTR&=63; +#if FM_DELAY + ++scsp->DELAYPTR; + if(scsp->DELAYPTR>FM_DELAY-1) scsp->DELAYPTR=0; +#endif + } + + if (! scsp->BypassDSP) + { + SCSPDSP_Step(&scsp->DSP); + + for(i=0;i<16;++i) + { + struct _SLOT *slot=scsp->Slots+i; + if(EFSDL(slot)) + { + unsigned short Enc=((EFPAN(slot))<<0x8)|((EFSDL(slot))<<0xd); + smpl+=(scsp->DSP.EFREG[i]*scsp->LPANTABLE[Enc])>>SHIFT; + smpr+=(scsp->DSP.EFREG[i]*scsp->RPANTABLE[Enc])>>SHIFT; + } + } + } + + //*bufl++ = ICLIP16(smpl>>2); + //*bufr++ = ICLIP16(smpr>>2); + *bufl++ = smpl>>3; + *bufr++ = smpr>>3; + } +} + +/* TODO: this needs to be timer-ized */ +/*static void SCSP_exec_dma(address_space *space, scsp_state *scsp) +{ + static UINT16 tmp_dma[3]; + int i; + + logerror("SCSP: DMA transfer START\n" + "DMEA: %04x DRGA: %04x DTLG: %04x\n" + "DGATE: %d DDIR: %d\n",scsp->dma.dmea,scsp->dma.drga,scsp->dma.dtlg,scsp->dma.dgate ? 1 : 0,scsp->dma.ddir ? 1 : 0); + + // Copy the dma values in a temp storage for resuming later + // (DMA *can't* overwrite its parameters) + if(!(dma.ddir)) + { + for(i=0;i<3;i++) + tmp_dma[i] = scsp->udata.data[(0x12+(i*2))/2]; + } + + // note: we don't use space.read_word / write_word because it can happen that SH-2 enables the DMA instead of m68k. + // TODO: don't know if params auto-updates, I guess not ... + if(dma.ddir) + { + if(dma.dgate) + { + popmessage("Check: SCSP DMA DGATE enabled, contact MAME/MESSdev"); + for(i=0;i < scsp->dma.dtlg;i+=2) + { + scsp->SCSPRAM[scsp->dma.dmea] = 0; + scsp->SCSPRAM[scsp->dma.dmea+1] = 0; + scsp->dma.dmea+=2; + } + } + else + { + for(i=0;i < scsp->dma.dtlg;i+=2) + { + UINT16 tmp; + tmp = SCSP_r16(scsp, space, scsp->dma.drga); + scsp->SCSPRAM[scsp->dma.dmea] = tmp & 0xff; + scsp->SCSPRAM[scsp->dma.dmea+1] = tmp>>8; + scsp->dma.dmea+=2; + scsp->dma.drga+=2; + } + } + } + else + { + if(dma.dgate) + { + popmessage("Check: SCSP DMA DGATE enabled, contact MAME/MESSdev"); + for(i=0;i < scsp->dma.dtlg;i+=2) + { + SCSP_w16(scsp, space, scsp->dma.drga, 0); + scsp->dma.drga+=2; + } + } + else + { + for(i=0;i < scsp->dma.dtlg;i+=2) + { + UINT16 tmp; + tmp = scsp->SCSPRAM[scsp->dma.dmea]; + tmp|= scsp->SCSPRAM[scsp->dma.dmea+1]<<8; + SCSP_w16(scsp, space, scsp->dma.drga, tmp); + scsp->dma.dmea+=2; + scsp->dma.drga+=2; + } + } + } + + //Resume the values + if(!(dma.ddir)) + { + for(i=0;i<3;i++) + scsp->udata.data[(0x12+(i*2))/2] = tmp_dma[i]; + } + + // Job done + scsp->udata.data[0x16/2] &= ~0x1000; + // request a dma end irq (TODO: make it inside the interface) + if(scsp->udata.data[0x1e/2] & 0x10) + { + popmessage("SCSP DMA IRQ triggered, contact MAMEdev"); + device_set_input_line(space->machine().device("audiocpu"),DecodeSCI(scsp,SCIDMA),HOLD_LINE); + } +}*/ + +#ifdef UNUSED_FUNCTION +int SCSP_IRQCB(void *param) +{ + CheckPendingIRQ(param); + return -1; +} +#endif + +//static STREAM_UPDATE( SCSP_Update ) +void SCSP_Update(void *_info, stream_sample_t **outputs, int samples) +{ + //scsp_state *scsp = (scsp_state *)param; + scsp_state *scsp = (scsp_state *)_info; + //bufferl = outputs[0]; + //bufferr = outputs[1]; + //length = samples; + SCSP_DoMasterSamples(scsp, outputs, samples); +} + +//static DEVICE_START( scsp ) +int device_start_scsp(void **_info, int clock, int Flags) +{ + /*const scsp_interface *intf; + + scsp_state *scsp = get_safe_token(device); + + intf = (const scsp_interface *)device->static_config();*/ + scsp_state *scsp; + + scsp = (scsp_state *) calloc(1, sizeof(scsp_state)); + *_info = (void *) scsp; + + scsp->BypassDSP = (Flags & 0x01) >> 0; + + if (clock < 1000000) // if < 1 MHz, then it's the sample rate, not the clock + clock *= 512; // (for backwards compatibility with old VGM logs) + + // init the emulation + //SCSP_Init(device, scsp, intf); + SCSP_Init(scsp, clock); + + // set up the IRQ callbacks + /*{ + scsp->Int68kCB = intf->irq_callback; + + scsp->stream = device->machine().sound().stream_alloc(*device, 0, 2, 44100, scsp, SCSP_Update); + } + + scsp->main_irq.resolve(intf->main_irq, *device);*/ + + return scsp->rate; // 44100 +} + +void device_stop_scsp(void *_info) +{ + scsp_state *scsp = (scsp_state *)_info; + + free(scsp->SCSPRAM); scsp->SCSPRAM = NULL; + + free(scsp); + + return; +} + +void device_reset_scsp(void *_info) +{ + scsp_state *scsp = (scsp_state *)_info; + int i; + + // make sure all the slots are off + for(i=0;i<32;++i) + { + scsp->Slots[i].slot=i; + scsp->Slots[i].active=0; + scsp->Slots[i].base=NULL; + scsp->Slots[i].EG.state=RELEASE; + } + + SCSPDSP_Init(&scsp->DSP); + scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH / 2; + scsp->DSP.SCSPRAM = (UINT16*)scsp->SCSPRAM; + + return; +} + + +/*void scsp_set_ram_base(device_t *device, void *base) +{ + scsp_state *scsp = get_safe_token(device); + if (scsp) + { + scsp->SCSPRAM = (unsigned char *)base; + scsp->DSP.SCSPRAM = (UINT16 *)base; + scsp->SCSPRAM_LENGTH = 0x80000; + scsp->DSP.SCSPRAM_LENGTH = 0x80000/2; + } +}*/ + + +//READ16_DEVICE_HANDLER( scsp_r ) +UINT16 scsp_r(void *_info, offs_t offset) +{ + //scsp_state *scsp = get_safe_token(device); + scsp_state *scsp = (scsp_state *)_info; + + //scsp->stream->update(); + + return SCSP_r16(scsp, offset*2); +} + +//WRITE16_DEVICE_HANDLER( scsp_w ) +void scsp_w(void *_info, offs_t offset, UINT8 data) +{ + //scsp_state *scsp = get_safe_token(device); + scsp_state *scsp = (scsp_state *)_info; + UINT16 tmp; + + //scsp->stream->update(); + + tmp = SCSP_r16(scsp, offset & 0xFFFE); + //COMBINE_DATA(&tmp); + if (offset & 1) + tmp = (tmp & 0xFF00) | (data << 0); + else + tmp = (tmp & 0x00FF) | (data << 8); + SCSP_w16(scsp,offset & 0xFFFE, tmp); +} + +/*WRITE16_DEVICE_HANDLER( scsp_midi_in ) +{ + scsp_state *scsp = get_safe_token(device); + +// printf("scsp_midi_in: %02x\n", data); + + scsp->MidiStack[scsp->MidiW++]=data; + scsp->MidiW &= 31; + + //CheckPendingIRQ(scsp); +} + +READ16_DEVICE_HANDLER( scsp_midi_out_r ) +{ + scsp_state *scsp = get_safe_token(device); + unsigned char val; + + val=scsp->MidiStack[scsp->MidiR++]; + scsp->MidiR&=31; + return val; +}*/ + + +/*void scsp_write_rom(UINT8 ChipID, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + scsp_state *scsp = &SCSPData[ChipID]; + + if (scsp->SCSPRAM_LENGTH != ROMSize) + { + scsp->SCSPRAM = (unsigned char*)realloc(scsp->SCSPRAM, ROMSize); + scsp->SCSPRAM_LENGTH = ROMSize; + scsp->DSP.SCSPRAM = (UINT16*)scsp->SCSPRAM; + scsp->DSP.SCSPRAM_LENGTH = scsp->SCSPRAM_LENGTH / 2; + memset(scsp->SCSPRAM, 0x00, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(scsp->SCSPRAM + DataStart, ROMData, DataLength); + + return; +}*/ + +void scsp_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) +{ + scsp_state *scsp = (scsp_state *)_info; + + if (DataStart >= scsp->SCSPRAM_LENGTH) + return; + if (DataStart + DataLength > scsp->SCSPRAM_LENGTH) + DataLength = scsp->SCSPRAM_LENGTH - DataStart; + + memcpy(scsp->SCSPRAM + DataStart, RAMData, DataLength); + + return; +} + + +void scsp_set_mute_mask(void *_info, UINT32 MuteMask) +{ + scsp_state *scsp = (scsp_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 32; CurChn ++) + scsp->Slots[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + +/*UINT8 scsp_get_channels(void *_info, UINT32* ChannelMask) +{ + scsp_state *scsp = (scsp_state *)_info; + UINT8 CurChn; + UINT8 UsedChns; + UINT32 ChnMask; + + ChnMask = 0x00000000; + UsedChns = 0x00; + for (CurChn = 0; CurChn < 32; CurChn ++) + { + if (scsp->Slots[CurChn].active) + { + ChnMask |= (1 << CurChn); + UsedChns ++; + } + } + if (ChannelMask != NULL) + *ChannelMask = ChnMask; + + return UsedChns; +}*/ + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( scsp ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(scsp_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( scsp ); break; + case DEVINFO_FCT_STOP: // Nothing // break; + case DEVINFO_FCT_RESET: // Nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "SCSP"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega/Yamaha custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "2.1.1"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + + +//DEFINE_LEGACY_SOUND_DEVICE(SCSP, scsp); diff --git a/Frameworks/GME/vgmplay/chips/scsp.h b/Frameworks/GME/vgmplay/chips/scsp.h new file mode 100644 index 000000000..6280a098f --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/scsp.h @@ -0,0 +1,47 @@ +/* + SCSP (YMF292-F) header +*/ + +#pragma once + +#ifndef __SCSP_H__ +#define __SCSP_H__ + +//#include "devlegcy.h" + +/*typedef struct _scsp_interface scsp_interface; +struct _scsp_interface +{ + int roffset; // offset in the region + void (*irq_callback)(device_t *device, int state); // irq callback + devcb_write_line main_irq; +}; + +void scsp_set_ram_base(device_t *device, void *base);*/ + + +void SCSP_Update(void *chip, stream_sample_t **outputs, int samples); +int device_start_scsp(void **chip, int clock, int options); +void device_stop_scsp(void *chip); +void device_reset_scsp(void *chip); + +// SCSP register access +/*READ16_DEVICE_HANDLER( scsp_r ); +WRITE16_DEVICE_HANDLER( scsp_w );*/ +void scsp_w(void *chip, offs_t offset, UINT8 data); +//UINT8 scsp_r(void *chip, offs_t offset); + +// MIDI I/O access (used for comms on Model 2/3) +/*WRITE16_DEVICE_HANDLER( scsp_midi_in ); +READ16_DEVICE_HANDLER( scsp_midi_out_r );*/ + +//void scsp_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +// const UINT8* ROMData); +void scsp_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData); +void scsp_set_mute_mask(void *chip, UINT32 MuteMask); + +/*extern UINT32* stv_scu; + +DECLARE_LEGACY_SOUND_DEVICE(SCSP, scsp);*/ + +#endif /* __SCSP_H__ */ diff --git a/Frameworks/GME/vgmplay/chips/scspdsp.c b/Frameworks/GME/vgmplay/chips/scspdsp.c new file mode 100644 index 000000000..830ab7fff --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/scspdsp.c @@ -0,0 +1,356 @@ +//#include "emu.h" +#include // for memset +#include "mamedef.h" +#include "scsp.h" +#include "scspdsp.h" + +static UINT16 PACK(INT32 val) +{ + UINT32 temp; + int sign,exponent,k; + + sign = (val >> 23) & 0x1; + temp = (val ^ (val << 1)) & 0xFFFFFF; + exponent = 0; + for (k=0; k<12; k++) + { + if (temp & 0x800000) + break; + temp <<= 1; + exponent += 1; + } + if (exponent < 12) + val = (val << exponent) & 0x3FFFFF; + else + val <<= 11; + val >>= 11; + val &= 0x7FF; + val |= sign << 15; + val |= exponent << 11; + + return (UINT16)val; +} + +static INT32 UNPACK(UINT16 val) +{ + int sign,exponent,mantissa; + INT32 uval; + + sign = (val >> 15) & 0x1; + exponent = (val >> 11) & 0xF; + mantissa = val & 0x7FF; + uval = mantissa << 11; + if (exponent > 11) + { + exponent = 11; + uval |= sign << 22; + } + else + uval |= (sign ^ 1) << 22; + uval |= sign << 23; + uval <<= 8; + uval >>= 8; + uval >>= exponent; + + return uval; +} + +void SCSPDSP_Init(struct _SCSPDSP *DSP) +{ + memset(DSP,0,sizeof(struct _SCSPDSP)); + DSP->RBL=0x8000; + DSP->Stopped=1; +} + +void SCSPDSP_Step(struct _SCSPDSP *DSP) +{ + INT32 ACC=0; //26 bit + INT32 SHIFTED=0; //24 bit + INT32 X=0; //24 bit + INT32 Y=0; //13 bit + INT32 B=0; //26 bit + INT32 INPUTS=0; //24 bit + INT32 MEMVAL=0; + INT32 FRC_REG=0; //13 bit + INT32 Y_REG=0; //24 bit + UINT32 ADDR=0; + UINT32 ADRS_REG=0; //13 bit + int step; + + if(DSP->Stopped) + return; + + memset(DSP->EFREG,0,2*16); +#if 0 + int dump=0; + FILE *f=NULL; + if(dump) + f=fopen("dsp.txt","wt"); +#endif + for(step=0;stepLastStep;++step) + { + UINT16 *IPtr=DSP->MPRO+step*4; + +// if(IPtr[0]==0 && IPtr[1]==0 && IPtr[2]==0 && IPtr[3]==0) +// break; + + UINT32 TRA=(IPtr[0]>>8)&0x7F; + UINT32 TWT=(IPtr[0]>>7)&0x01; + UINT32 TWA=(IPtr[0]>>0)&0x7F; + + UINT32 XSEL=(IPtr[1]>>15)&0x01; + UINT32 YSEL=(IPtr[1]>>13)&0x03; + UINT32 IRA=(IPtr[1]>>6)&0x3F; + UINT32 IWT=(IPtr[1]>>5)&0x01; + UINT32 IWA=(IPtr[1]>>0)&0x1F; + + UINT32 TABLE=(IPtr[2]>>15)&0x01; + UINT32 MWT=(IPtr[2]>>14)&0x01; + UINT32 MRD=(IPtr[2]>>13)&0x01; + UINT32 EWT=(IPtr[2]>>12)&0x01; + UINT32 EWA=(IPtr[2]>>8)&0x0F; + UINT32 ADRL=(IPtr[2]>>7)&0x01; + UINT32 FRCL=(IPtr[2]>>6)&0x01; + UINT32 SHIFT=(IPtr[2]>>4)&0x03; + UINT32 YRL=(IPtr[2]>>3)&0x01; + UINT32 NEGB=(IPtr[2]>>2)&0x01; + UINT32 ZERO=(IPtr[2]>>1)&0x01; + UINT32 BSEL=(IPtr[2]>>0)&0x01; + + UINT32 NOFL=(IPtr[3]>>15)&1; //???? + UINT32 COEF=(IPtr[3]>>9)&0x3f; + + UINT32 MASA=(IPtr[3]>>2)&0x1f; //??? + UINT32 ADREB=(IPtr[3]>>1)&0x1; + UINT32 NXADR=(IPtr[3]>>0)&0x1; + + INT64 v; + + //operations are done at 24 bit precision +#if 0 + if(MASA) + int a=1; + if(NOFL) + int a=1; + +// int dump=0; + + if(f) + { +#define DUMP(v) fprintf(f," " #v ": %04X",v); + + fprintf(f,"%d: ",step); + DUMP(ACC); + DUMP(SHIFTED); + DUMP(X); + DUMP(Y); + DUMP(B); + DUMP(INPUTS); + DUMP(MEMVAL); + DUMP(FRC_REG); + DUMP(Y_REG); + DUMP(ADDR); + DUMP(ADRS_REG); + fprintf(f,"\n"); + } +#endif + //INPUTS RW +// colmns97 hits this +// assert(IRA<0x32); + if(IRA<=0x1f) + INPUTS=DSP->MEMS[IRA]; + else if(IRA<=0x2F) + INPUTS=DSP->MIXS[IRA-0x20]<<4; //MIXS is 20 bit + else if(IRA<=0x31) + INPUTS=0; + else + return; + + INPUTS<<=8; + INPUTS>>=8; + //if(INPUTS&0x00800000) + // INPUTS|=0xFF000000; + + if(IWT) + { + DSP->MEMS[IWA]=MEMVAL; //MEMVAL was selected in previous MRD + if(IRA==IWA) + INPUTS=MEMVAL; + } + + //Operand sel + //B + if(!ZERO) + { + if(BSEL) + B=ACC; + else + { + B=DSP->TEMP[(TRA+DSP->DEC)&0x7F]; + B<<=8; + B>>=8; + //if(B&0x00800000) + // B|=0xFF000000; //Sign extend + } + if(NEGB) + B=0-B; + } + else + B=0; + + //X + if(XSEL) + X=INPUTS; + else + { + X=DSP->TEMP[(TRA+DSP->DEC)&0x7F]; + X<<=8; + X>>=8; + //if(X&0x00800000) + // X|=0xFF000000; + } + + //Y + if(YSEL==0) + Y=FRC_REG; + else if(YSEL==1) + Y=DSP->COEF[COEF]>>3; //COEF is 16 bits + else if(YSEL==2) + Y=(Y_REG>>11)&0x1FFF; + else if(YSEL==3) + Y=(Y_REG>>4)&0x0FFF; + + if(YRL) + Y_REG=INPUTS; + + //Shifter + if(SHIFT==0) + { + SHIFTED=ACC; + if(SHIFTED>0x007FFFFF) + SHIFTED=0x007FFFFF; + if(SHIFTED<(-0x00800000)) + SHIFTED=-0x00800000; + } + else if(SHIFT==1) + { + SHIFTED=ACC*2; + if(SHIFTED>0x007FFFFF) + SHIFTED=0x007FFFFF; + if(SHIFTED<(-0x00800000)) + SHIFTED=-0x00800000; + } + else if(SHIFT==2) + { + SHIFTED=ACC*2; + SHIFTED<<=8; + SHIFTED>>=8; + //SHIFTED&=0x00FFFFFF; + //if(SHIFTED&0x00800000) + // SHIFTED|=0xFF000000; + } + else if(SHIFT==3) + { + SHIFTED=ACC; + SHIFTED<<=8; + SHIFTED>>=8; + //SHIFTED&=0x00FFFFFF; + //if(SHIFTED&0x00800000) + // SHIFTED|=0xFF000000; + } + + //ACCUM + Y<<=19; + Y>>=19; + //if(Y&0x1000) + // Y|=0xFFFFF000; + + v=(((INT64) X*(INT64) Y)>>12); + ACC=(int) v+B; + + if(TWT) + DSP->TEMP[(TWA+DSP->DEC)&0x7F]=SHIFTED; + + if(FRCL) + { + if(SHIFT==3) + FRC_REG=SHIFTED&0x0FFF; + else + FRC_REG=(SHIFTED>>11)&0x1FFF; + } + + if(MRD || MWT) + //if(0) + { + ADDR=DSP->MADRS[MASA]; + if(!TABLE) + ADDR+=DSP->DEC; + if(ADREB) + ADDR+=ADRS_REG&0x0FFF; + if(NXADR) + ADDR++; + if(!TABLE) + ADDR&=DSP->RBL-1; + else + ADDR&=0xFFFF; + //ADDR<<=1; + //ADDR+=DSP->RBP<<13; + //MEMVAL=DSP->SCSPRAM[ADDR>>1]; + ADDR+=DSP->RBP<<12; + if (ADDR > 0x7ffff) ADDR = 0; + if(MRD && (step&1)) //memory only allowed on odd? DoA inserts NOPs on even + { + if(NOFL) + MEMVAL=DSP->SCSPRAM[ADDR]<<8; + else + MEMVAL=UNPACK(DSP->SCSPRAM[ADDR]); + } + if(MWT && (step&1)) + { + if(NOFL) + DSP->SCSPRAM[ADDR]=SHIFTED>>8; + else + DSP->SCSPRAM[ADDR]=PACK(SHIFTED); + } + } + + if(ADRL) + { + if(SHIFT==3) + ADRS_REG=(SHIFTED>>12)&0xFFF; + else + ADRS_REG=(INPUTS>>16); + } + + if(EWT) + DSP->EFREG[EWA]+=SHIFTED>>8; + + } + --DSP->DEC; + memset(DSP->MIXS,0,4*16); +// if(f) +// fclose(f); +} + +void SCSPDSP_SetSample(struct _SCSPDSP *DSP,INT32 sample,int SEL,int MXL) +{ + //DSP->MIXS[SEL]+=sample<<(MXL+1)/*7*/; + DSP->MIXS[SEL]+=sample; +// if(MXL) +// int a=1; +} + +void SCSPDSP_Start(struct _SCSPDSP *DSP) +{ + int i; + DSP->Stopped=0; + for(i=127;i>=0;--i) + { + UINT16 *IPtr=DSP->MPRO+i*4; + + if(IPtr[0]!=0 || IPtr[1]!=0 || IPtr[2]!=0 || IPtr[3]!=0) + break; + } + DSP->LastStep=i+1; + +} diff --git a/Frameworks/GME/vgmplay/chips/scspdsp.h b/Frameworks/GME/vgmplay/chips/scspdsp.h new file mode 100644 index 000000000..5040aa17c --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/scspdsp.h @@ -0,0 +1,40 @@ +#pragma once + +#ifndef __SCSPDSP_H__ +#define __SCSPDSP_H__ + +//the DSP Context +struct _SCSPDSP +{ +//Config + UINT16 *SCSPRAM; + UINT32 SCSPRAM_LENGTH; + UINT32 RBP; //Ring buf pointer + UINT32 RBL; //Delay ram (Ring buffer) size in words + +//context + + INT16 COEF[64]; //16 bit signed + UINT16 MADRS[32]; //offsets (in words), 16 bit + UINT16 MPRO[128*4]; //128 steps 64 bit + INT32 TEMP[128]; //TEMP regs,24 bit signed + INT32 MEMS[32]; //MEMS regs,24 bit signed + UINT32 DEC; + +//input + INT32 MIXS[16]; //MIXS, 24 bit signed + INT16 EXTS[2]; //External inputs (CDDA) 16 bit signed + +//output + INT16 EFREG[16]; //EFREG, 16 bit signed + + int Stopped; + int LastStep; +}; + +void SCSPDSP_Init(struct _SCSPDSP *DSP); +void SCSPDSP_SetSample(struct _SCSPDSP *DSP, INT32 sample, INT32 SEL, INT32 MXL); +void SCSPDSP_Step(struct _SCSPDSP *DSP); +void SCSPDSP_Start(struct _SCSPDSP *DSP); + +#endif /* __SCSPDSP_H__ */ diff --git a/Frameworks/GME/vgmplay/chips/scsplfo.c b/Frameworks/GME/vgmplay/chips/scsplfo.c new file mode 100644 index 000000000..fde300da8 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/scsplfo.c @@ -0,0 +1,165 @@ +/* + SCSP LFO handling + + Part of the SCSP (YMF292-F) emulator package. + (not compiled directly, #included from scsp.c) + + By ElSemi + MAME/M1 conversion and cleanup by R. Belmont +*/ + +#define LFO_SHIFT 8 + +struct _LFO +{ + unsigned short phase; + UINT32 phase_step; + int *table; + int *scale; +}; + +#define LFIX(v) ((unsigned int) ((float) (1<phase+=LFO->phase_step; +#if LFO_SHIFT!=8 + LFO->phase&=(1<<(LFO_SHIFT+8))-1; +#endif + p=LFO->table[LFO->phase>>LFO_SHIFT]; + p=LFO->scale[p+128]; + return p<<(SHIFT-LFO_SHIFT); +} + +INLINE signed int ALFO_Step(struct _LFO *LFO) +{ + int p; + LFO->phase+=LFO->phase_step; +#if LFO_SHIFT!=8 + LFO->phase&=(1<<(LFO_SHIFT+8))-1; +#endif + p=LFO->table[LFO->phase>>LFO_SHIFT]; + p=LFO->scale[p]; + return p<<(SHIFT-LFO_SHIFT); +} + +static void LFO_ComputeStep(struct _LFO *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO) +{ + float step=(float) LFOFreq[LFOF]*256.0/(float)44100; + LFO->phase_step=(unsigned int) ((float) (1<table=ALFO_SAW; break; + case 1: LFO->table=ALFO_SQR; break; + case 2: LFO->table=ALFO_TRI; break; + case 3: LFO->table=ALFO_NOI; break; + } + LFO->scale=ASCALES[LFOS]; + } + else + { + switch(LFOWS) + { + case 0: LFO->table=PLFO_SAW; break; + case 1: LFO->table=PLFO_SQR; break; + case 2: LFO->table=PLFO_TRI; break; + case 3: LFO->table=PLFO_NOI; break; + } + LFO->scale=PSCALES[LFOS]; + } +} diff --git a/Frameworks/GME/gme/segapcm.c b/Frameworks/GME/vgmplay/chips/segapcm.c similarity index 76% rename from Frameworks/GME/gme/segapcm.c rename to Frameworks/GME/vgmplay/chips/segapcm.c index 7370c369e..154e06e04 100644 --- a/Frameworks/GME/gme/segapcm.c +++ b/Frameworks/GME/vgmplay/chips/segapcm.c @@ -1,384 +1,409 @@ -/*********************************************************/ -/* SEGA 16ch 8bit PCM */ -/*********************************************************/ - -#include -#include -#include -//#include "sndintrf.h" -//#include "streams.h" -#include "segapcm.h" - - -typedef struct _segapcm_state segapcm_state; -struct _segapcm_state -{ - UINT8 *ram; - UINT8 low[16]; - UINT32 ROMSize; - UINT8 *rom; -#ifdef _DEBUG - UINT8 *romusage; -#endif - int bankshift; - int bankmask; - int rgnmask; - sega_pcm_interface intf; - UINT8 Muted[16]; - //sound_stream * stream; -}; - -UINT8 SegaPCM_NewCore = 0x00; - -/*INLINE segapcm_state *get_safe_token(const device_config *device) -{ - assert(device != NULL); - assert(device->token != NULL); - assert(device->type == SOUND); - assert(sound_get_type(device) == SOUND_SEGAPCM); - return (segapcm_state *)device->token; -}*/ - -//static STREAM_UPDATE( SEGAPCM_update ) -void SEGAPCM_update(void *chip, stream_sample_t **outputs, int samples) -{ - //segapcm_state *spcm = (segapcm_state *)param; - segapcm_state *spcm = (segapcm_state *) chip; - int rgnmask = spcm->rgnmask; - int ch; - - /* clear the buffers */ - memset(outputs[0], 0, samples*sizeof(stream_sample_t)); - memset(outputs[1], 0, samples*sizeof(stream_sample_t)); - - // reg function - // ------------------------------------------------ - // 0x00 ? - // 0x01 ? - // 0x02 volume left - // 0x03 volume right - // 0x04 loop address (08-15) - // 0x05 loop address (16-23) - // 0x06 end address - // 0x07 address delta - // 0x80 ? - // 0x81 ? - // 0x82 ? - // 0x83 ? - // 0x84 current address (08-15), 00-07 is internal? - // 0x85 current address (16-23) - // 0x86 bit 0: channel disable? - // bit 1: loop disable - // other bits: bank - // 0x87 ? - - /* loop over channels */ - for (ch = 0; ch < 16; ch++) - { -if (! SegaPCM_NewCore) -{ - /* only process active channels */ - if (!(spcm->ram[0x86+8*ch] & 1) && ! spcm->Muted[ch]) - { - UINT8 *base = spcm->ram+8*ch; - UINT8 flags = base[0x86]; - const UINT8 *rom = spcm->rom + ((flags & spcm->bankmask) << spcm->bankshift); -#ifdef _DEBUG - const UINT8 *romusage = spcm->romusage + ((flags & spcm->bankmask) << spcm->bankshift); -#endif - UINT32 addr = (base[5] << 16) | (base[4] << 8) | spcm->low[ch]; - UINT16 loop = (base[0x85] << 8) | base[0x84]; - UINT8 end = base[6] + 1; - UINT8 delta = base[7]; - UINT8 voll = base[2]; - UINT8 volr = base[3]; - int i; - - /* loop over samples on this channel */ - for (i = 0; i < samples; i++) - { - INT8 v = 0; - - /* handle looping if we've hit the end */ - if ((addr >> 16) == end) - { - if (!(flags & 2)) - addr = loop << 8; - else - { - flags |= 1; - break; - } - } - - /* fetch the sample */ - v = rom[(addr >> 8) & rgnmask] - 0x80; -#ifdef _DEBUG - if (romusage[(addr >> 8) & rgnmask] == 0xFF && (voll || volr)) - printf("Access to empty ROM section! (0x%06lX)\n", - ((flags & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask); -#endif - - /* apply panning and advance */ - outputs[0][i] += v * voll; - outputs[1][i] += v * volr; - addr += delta; - } - - /* store back the updated address and info */ - base[0x86] = flags; - base[4] = addr >> 8; - base[5] = addr >> 16; - spcm->low[ch] = flags & 1 ? 0 : addr; - } -} -else -{ - UINT8 *regs = spcm->ram+8*ch; - - /* only process active channels */ - if (!(regs[0x86] & 1) && ! spcm->Muted[ch]) - { - const UINT8 *rom = spcm->rom + ((regs[0x86] & spcm->bankmask) << spcm->bankshift); -#ifdef _DEBUG - const UINT8 *romusage = spcm->romusage + ((regs[0x86] & spcm->bankmask) << spcm->bankshift); -#endif - UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | spcm->low[ch]; - UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8); - UINT8 end = regs[6] + 1; - int i; - - /* loop over samples on this channel */ - for (i = 0; i < samples; i++) - { - INT8 v = 0; - - /* handle looping if we've hit the end */ - if ((addr >> 16) == end) - { - if (regs[0x86] & 2) - { - regs[0x86] |= 1; - break; - } - else addr = loop; - } - - /* fetch the sample */ - v = rom[(addr >> 8) & rgnmask] - 0x80; -#ifdef _DEBUG - if (romusage[(addr >> 8) & rgnmask] == 0xFF && (regs[2] || regs[3])) - printf("Access to empty ROM section! (0x%06lX)\n", - ((regs[0x86] & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask); -#endif - - /* apply panning and advance */ - outputs[0][i] += v * regs[2]; - outputs[1][i] += v * regs[3]; - addr = (addr + regs[7]) & 0xffffff; - } - - /* store back the updated address */ - regs[0x84] = addr >> 8; - regs[0x85] = addr >> 16; - spcm->low[ch] = regs[0x86] & 1 ? 0 : addr; - } -} - } -} - -//static DEVICE_START( segapcm ) -void * device_start_segapcm(int intf_bank) -{ - const UINT32 STD_ROM_SIZE = 0x80000; - //const sega_pcm_interface *intf = (const sega_pcm_interface *)device->static_config; - sega_pcm_interface *intf; - int mask, rom_mask, len; - //segapcm_state *spcm = get_safe_token(device); - segapcm_state *spcm; - - spcm = (segapcm_state *) malloc(sizeof(segapcm_state)); - if (!spcm) return spcm; - - intf = &spcm->intf; - intf->bank = intf_bank; - - //spcm->rom = (const UINT8 *)device->region; - //spcm->ram = auto_alloc_array(device->machine, UINT8, 0x800); - spcm->ROMSize = STD_ROM_SIZE; - spcm->rom = (UINT8*) malloc(STD_ROM_SIZE); -#ifdef _DEBUG - spcm->romusage = (UINT8*) malloc(STD_ROM_SIZE); -#endif - spcm->ram = (UINT8*) malloc(0x800); - - memset(spcm->rom, 0xFF, STD_ROM_SIZE); -#ifdef _DEBUG - memset(spcm->romusage, 0xFF, STD_ROM_SIZE); -#endif - //memset(spcm->ram, 0xff, 0x800); // RAM Clear is done at device_reset - - spcm->bankshift = (UINT8)(intf->bank); - mask = intf->bank >> 16; - if(!mask) - mask = BANK_MASK7>>16; - - len = STD_ROM_SIZE; - spcm->rgnmask = len - 1; - for(rom_mask = 1; rom_mask < len; rom_mask *= 2); - rom_mask--; - - spcm->bankmask = mask & (rom_mask >> spcm->bankshift); - - //spcm->stream = stream_create(device, 0, 2, device->clock / 128, spcm, SEGAPCM_update); - - //state_save_register_device_item_array(device, 0, spcm->low); - //state_save_register_device_item_pointer(device, 0, spcm->ram, 0x800); - - for (mask = 0; mask < 16; mask ++) - spcm->Muted[mask] = 0x00; - - return spcm; -} - -//static DEVICE_STOP( segapcm ) -void device_stop_segapcm(void *chip) -{ - //segapcm_state *spcm = get_safe_token(device); - segapcm_state *spcm = (segapcm_state *) chip; - free(spcm->rom); spcm->rom = NULL; -#ifdef _DEBUG - free(spcm->romusage); -#endif - free(spcm->ram); - - free(spcm); -} - -//static DEVICE_RESET( segapcm ) -void device_reset_segapcm(void *chip) -{ - //segapcm_state *spcm = get_safe_token(device); - segapcm_state *spcm = (segapcm_state *) chip; - - memset(spcm->ram, 0xFF, 0x800); - - return; -} - - -//WRITE8_DEVICE_HANDLER( sega_pcm_w ) -void sega_pcm_w(void *chip, offs_t offset, UINT8 data) -{ - //segapcm_state *spcm = get_safe_token(device); - segapcm_state *spcm = (segapcm_state *) chip; - //stream_update(spcm->stream); - - spcm->ram[offset & 0x07ff] = data; -} - -//READ8_DEVICE_HANDLER( sega_pcm_r ) -UINT8 sega_pcm_r(void *chip, offs_t offset) -{ - //segapcm_state *spcm = get_safe_token(device); - segapcm_state *spcm = (segapcm_state *) chip; - //stream_update(spcm->stream); - return spcm->ram[offset & 0x07ff]; -} - -void sega_pcm_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, - const UINT8* ROMData) -{ - segapcm_state *spcm = (segapcm_state *) chip; - - if (spcm->ROMSize != ROMSize) - { - unsigned long int mask, rom_mask; - - spcm->rom = (UINT8*)realloc(spcm->rom, ROMSize); -#ifdef _DEBUG - spcm->romusage = (UINT8*)realloc(spcm->romusage, ROMSize); -#endif - spcm->ROMSize = ROMSize; - memset(spcm->rom, 0xFF, ROMSize); -#ifdef _DEBUG - memset(spcm->romusage, 0xFF, ROMSize); -#endif - - // recalculate bankmask - mask = spcm->intf.bank >> 16; - if (! mask) - mask = BANK_MASK7 >> 16; - - spcm->rgnmask = ROMSize - 1; - for (rom_mask = 1; rom_mask < ROMSize; rom_mask *= 2); - rom_mask --; - - spcm->bankmask = mask & (rom_mask >> spcm->bankshift); - } - if (DataStart > ROMSize) - return; - if (DataStart + DataLength > ROMSize) - DataLength = ROMSize - DataStart; - - memcpy(spcm->rom + DataStart, ROMData, DataLength); -#ifdef _DEBUG - memset(spcm->romusage + DataStart, 0x00, DataLength); -#endif - - return; -} - - -/*void sega_pcm_fwrite_romusage(UINT8 ChipID) -{ - segapcm_state *spcm = &SPCMData[ChipID]; - - FILE* hFile; - - hFile = fopen("SPCM_ROMUsage.bin", "wb"); - if (hFile == NULL) - return; - - fwrite(spcm->romusage, 0x01, spcm->ROMSize, hFile); - - fclose(hFile); - return; -}*/ - -void segapcm_set_mute_mask(void *chip, UINT32 MuteMask) -{ - segapcm_state *spcm = (segapcm_state *) chip; - unsigned char CurChn; - - for (CurChn = 0; CurChn < 16; CurChn ++) - spcm->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; - - return; -} - - -/************************************************************************** - * Generic get_info - **************************************************************************/ - -/*DEVICE_GET_INFO( segapcm ) -{ - switch (state) - { - // --- the following bits of info are returned as 64-bit signed integers --- - case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(segapcm_state); break; - - // --- the following bits of info are returned as pointers to data or functions --- - case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( segapcm ); break; - case DEVINFO_FCT_STOP: // Nothing break; - case DEVINFO_FCT_RESET: // Nothing break; - - // --- the following bits of info are returned as NULL-terminated strings --- - case DEVINFO_STR_NAME: strcpy(info->s, "Sega PCM"); break; - case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break; - case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; - case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; - case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; - } -}*/ +/*********************************************************/ +/* SEGA 16ch 8bit PCM */ +/*********************************************************/ + +#include "mamedef.h" +#include +#include +#include +//#include "sndintrf.h" +//#include "streams.h" +#include "segapcm.h" + + +typedef struct _segapcm_state segapcm_state; +struct _segapcm_state +{ + UINT8 *ram; + UINT8 low[16]; + UINT32 ROMSize; + UINT8 *rom; +#ifdef _DEBUG + UINT8 *romusage; +#endif + int bankshift; + int bankmask; + int rgnmask; + sega_pcm_interface intf; + UINT8 Muted[16]; + //sound_stream * stream; +}; + +#ifndef _DEBUG +//UINT8 SegaPCM_NewCore = 0x00; +#else +//UINT8 SegaPCM_NewCore = 0x01; +static void sega_pcm_fwrite_romusage(void *_info); +#endif + +/*INLINE segapcm_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_SEGAPCM); + return (segapcm_state *)device->token; +}*/ + +//static STREAM_UPDATE( SEGAPCM_update ) +void SEGAPCM_update(void *info, stream_sample_t **outputs, int samples) +{ + //segapcm_state *spcm = (segapcm_state *)param; + segapcm_state *spcm = (segapcm_state *)info; + int rgnmask = spcm->rgnmask; + int ch; + + /* clear the buffers */ + memset(outputs[0], 0, samples*sizeof(stream_sample_t)); + memset(outputs[1], 0, samples*sizeof(stream_sample_t)); + + // reg function + // ------------------------------------------------ + // 0x00 ? + // 0x01 ? + // 0x02 volume left + // 0x03 volume right + // 0x04 loop address (08-15) + // 0x05 loop address (16-23) + // 0x06 end address + // 0x07 address delta + // 0x80 ? + // 0x81 ? + // 0x82 ? + // 0x83 ? + // 0x84 current address (08-15), 00-07 is internal? + // 0x85 current address (16-23) + // 0x86 bit 0: channel disable? + // bit 1: loop disable + // other bits: bank + // 0x87 ? + + /* loop over channels */ + for (ch = 0; ch < 16; ch++) + { +#if 0 +//if (! SegaPCM_NewCore) +//{ + /* only process active channels */ + if (!(spcm->ram[0x86+8*ch] & 1) && ! spcm->Muted[ch]) + { + UINT8 *base = spcm->ram+8*ch; + UINT8 flags = base[0x86]; + const UINT8 *rom = spcm->rom + ((flags & spcm->bankmask) << spcm->bankshift); +#ifdef _DEBUG + UINT8 *romusage = spcm->romusage + ((flags & spcm->bankmask) << spcm->bankshift); +#endif + UINT32 addr = (base[5] << 16) | (base[4] << 8) | spcm->low[ch]; + UINT16 loop = (base[0x85] << 8) | base[0x84]; + UINT8 end = base[6] + 1; + UINT8 delta = base[7]; + UINT8 voll = base[2] & 0x7F; + UINT8 volr = base[3] & 0x7F; + int i; + + /* loop over samples on this channel */ + for (i = 0; i < samples; i++) + { + INT8 v = 0; + + /* handle looping if we've hit the end */ + if ((addr >> 16) == end) + { + if (!(flags & 2)) + addr = loop << 8; + else + { + flags |= 1; + break; + } + } + + /* fetch the sample */ + v = rom[(addr >> 8) & rgnmask] - 0x80; +#ifdef _DEBUG + if ((romusage[(addr >> 8) & rgnmask] & 0x03) == 0x02 && (voll || volr)) + printf("Access to empty ROM section! (0x%06lX)\n", + ((flags & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask); + romusage[(addr >> 8) & rgnmask] |= 0x01; +#endif + + /* apply panning and advance */ + outputs[0][i] += v * voll; + outputs[1][i] += v * volr; + addr += delta; + } + + /* store back the updated address and info */ + base[0x86] = flags; + base[4] = addr >> 8; + base[5] = addr >> 16; + spcm->low[ch] = flags & 1 ? 0 : addr; + } +//} +//else +//{ +#else + UINT8 *regs = spcm->ram+8*ch; + + /* only process active channels */ + if (!(regs[0x86] & 1) && ! spcm->Muted[ch]) + { + const UINT8 *rom = spcm->rom + ((regs[0x86] & spcm->bankmask) << spcm->bankshift); +#ifdef _DEBUG + UINT8 *romusage = spcm->romusage + ((regs[0x86] & spcm->bankmask) << spcm->bankshift); +#endif + UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | spcm->low[ch]; + UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8); + UINT8 end = regs[6] + 1; + int i; + + /* loop over samples on this channel */ + for (i = 0; i < samples; i++) + { + INT8 v = 0; + + /* handle looping if we've hit the end */ + if ((addr >> 16) == end) + { + if (regs[0x86] & 2) + { + regs[0x86] |= 1; + break; + } + else addr = loop; + } + + /* fetch the sample */ + v = rom[(addr >> 8) & rgnmask] - 0x80; +#ifdef _DEBUG + if ((romusage[(addr >> 8) & rgnmask] & 0x03) == 0x02 && (regs[2] || regs[3])) + printf("Access to empty ROM section! (0x%06lX)\n", + ((regs[0x86] & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask); + romusage[(addr >> 8) & rgnmask] |= 0x01; +#endif + + /* apply panning and advance */ + // fixed Bitmask for volume multiplication, thanks to ctr -Valley Bell + outputs[0][i] += v * (regs[2] & 0x7F); + outputs[1][i] += v * (regs[3] & 0x7F); + addr = (addr + regs[7]) & 0xffffff; + } + + /* store back the updated address */ + regs[0x84] = addr >> 8; + regs[0x85] = addr >> 16; + spcm->low[ch] = regs[0x86] & 1 ? 0 : addr; + } +//} +#endif + } +} + +//static DEVICE_START( segapcm ) +int device_start_segapcm(void **_info, int clock, int intf_bank) +{ + const UINT32 STD_ROM_SIZE = 0x80000; + //const sega_pcm_interface *intf = (const sega_pcm_interface *)device->static_config; + sega_pcm_interface *intf; + int mask, rom_mask, len; + //segapcm_state *spcm = get_safe_token(device); + segapcm_state *spcm; + + spcm = (segapcm_state *) calloc(1, sizeof(segapcm_state)); + *_info = (void *) spcm; + + intf = &spcm->intf; + intf->bank = intf_bank; + + //spcm->rom = (const UINT8 *)device->region; + //spcm->ram = auto_alloc_array(device->machine, UINT8, 0x800); + spcm->ROMSize = STD_ROM_SIZE; + spcm->rom = malloc(STD_ROM_SIZE); +#ifdef _DEBUG + spcm->romusage = malloc(STD_ROM_SIZE); +#endif + spcm->ram = (UINT8*)malloc(0x800); + +#ifndef _DEBUG + //memset(spcm->rom, 0xFF, STD_ROM_SIZE); + // filling 0xFF would actually be more true to the hardware, + // (unused ROMs have all FFs) + // but 0x80 is the effective 'zero' byte + memset(spcm->rom, 0x80, STD_ROM_SIZE); +#else + // filling with FF makes it easier to find bugs in a .wav-log + memset(spcm->rom, 0xFF, STD_ROM_SIZE); + memset(spcm->romusage, 0x02, STD_ROM_SIZE); +#endif + //memset(spcm->ram, 0xff, 0x800); // RAM Clear is done at device_reset + + spcm->bankshift = (UINT8)(intf->bank); + mask = intf->bank >> 16; + if(!mask) + mask = BANK_MASK7>>16; + + len = STD_ROM_SIZE; + spcm->rgnmask = len - 1; + for(rom_mask = 1; rom_mask < len; rom_mask *= 2); + rom_mask--; + + spcm->bankmask = mask & (rom_mask >> spcm->bankshift); + + //spcm->stream = stream_create(device, 0, 2, device->clock / 128, spcm, SEGAPCM_update); + + //state_save_register_device_item_array(device, 0, spcm->low); + //state_save_register_device_item_pointer(device, 0, spcm->ram, 0x800); + + for (mask = 0; mask < 16; mask ++) + spcm->Muted[mask] = 0x00; + + return clock / 128; +} + +//static DEVICE_STOP( segapcm ) +void device_stop_segapcm(void *_info) +{ + //segapcm_state *spcm = get_safe_token(device); + segapcm_state *spcm = (segapcm_state *)_info; + free(spcm->rom); spcm->rom = NULL; +#ifdef _DEBUG + //sega_pcm_fwrite_romusage(ChipID); + free(spcm->romusage); +#endif + free(spcm->ram); + + free(spcm); + + return; +} + +//static DEVICE_RESET( segapcm ) +void device_reset_segapcm(void *_info) +{ + //segapcm_state *spcm = get_safe_token(device); + segapcm_state *spcm = (segapcm_state *)_info; + + memset(spcm->ram, 0xFF, 0x800); + + return; +} + + +//WRITE8_DEVICE_HANDLER( sega_pcm_w ) +void sega_pcm_w(void *_info, offs_t offset, UINT8 data) +{ + //segapcm_state *spcm = get_safe_token(device); + segapcm_state *spcm = (segapcm_state *)_info; + //stream_update(spcm->stream); + + spcm->ram[offset & 0x07ff] = data; +} + +//READ8_DEVICE_HANDLER( sega_pcm_r ) +UINT8 sega_pcm_r(void *_info, offs_t offset) +{ + //segapcm_state *spcm = get_safe_token(device); + segapcm_state *spcm = (segapcm_state *)_info; + //stream_update(spcm->stream); + return spcm->ram[offset & 0x07ff]; +} + +void sega_pcm_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + segapcm_state *spcm = (segapcm_state *)_info; + + if (spcm->ROMSize != ROMSize) + { + unsigned long int mask, rom_mask; + + spcm->rom = (UINT8*)realloc(spcm->rom, ROMSize); +#ifdef _DEBUG + spcm->romusage = (UINT8*)realloc(spcm->romusage, ROMSize); +#endif + spcm->ROMSize = ROMSize; + memset(spcm->rom, 0x80, ROMSize); +#ifdef _DEBUG + memset(spcm->romusage, 0x02, ROMSize); +#endif + + // recalculate bankmask + mask = spcm->intf.bank >> 16; + if (! mask) + mask = BANK_MASK7 >> 16; + + //spcm->rgnmask = ROMSize - 1; + for (rom_mask = 1; rom_mask < ROMSize; rom_mask *= 2); + rom_mask --; + spcm->rgnmask = rom_mask; // fix for ROMs with e.g 0x60000 bytes (stupid M1) + + spcm->bankmask = mask & (rom_mask >> spcm->bankshift); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(spcm->rom + DataStart, ROMData, DataLength); +#ifdef _DEBUG + memset(spcm->romusage + DataStart, 0x00, DataLength); +#endif + + return; +} + + +#ifdef _DEBUG +static void sega_pcm_fwrite_romusage(void *_info) +{ + segapcm_state *spcm = (segapcm_state *)_info; + + FILE* hFile; + + hFile = fopen("SPCM_ROMUsage.bin", "wb"); + if (hFile == NULL) + return; + + fwrite(spcm->romusage, 0x01, spcm->ROMSize, hFile); + + fclose(hFile); + return; +} +#endif + +void segapcm_set_mute_mask(void *_info, UINT32 MuteMask) +{ + segapcm_state *spcm = (segapcm_state *)_info; + unsigned char CurChn; + + for (CurChn = 0; CurChn < 16; CurChn ++) + spcm->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( segapcm ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(segapcm_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( segapcm ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: // Nothing break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "Sega PCM"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/gme/segapcm.h b/Frameworks/GME/vgmplay/chips/segapcm.h similarity index 82% rename from Frameworks/GME/gme/segapcm.h rename to Frameworks/GME/vgmplay/chips/segapcm.h index 0053e1548..eaeea837a 100644 --- a/Frameworks/GME/gme/segapcm.h +++ b/Frameworks/GME/vgmplay/chips/segapcm.h @@ -1,48 +1,38 @@ -/*********************************************************/ -/* SEGA 8bit PCM */ -/*********************************************************/ - -#pragma once - -#include "mamedef.h" - -#define BANK_256 (11) -#define BANK_512 (12) -#define BANK_12M (13) -#define BANK_MASK7 (0x70<<16) -#define BANK_MASKF (0xf0<<16) -#define BANK_MASKF8 (0xf8<<16) - -typedef struct _sega_pcm_interface sega_pcm_interface; -struct _sega_pcm_interface -{ - int bank; -}; - -/*WRITE8_DEVICE_HANDLER( sega_pcm_w ); -READ8_DEVICE_HANDLER( sega_pcm_r ); - -DEVICE_GET_INFO( segapcm ); -#define SOUND_SEGAPCM DEVICE_GET_INFO_NAME( segapcm )*/ - -#ifdef __cplusplus -extern "C" { -#endif - -void SEGAPCM_update(void *chip, stream_sample_t **outputs, int samples); - -void * device_start_segapcm(int intf_bank); -void device_stop_segapcm(void *chip); -void device_reset_segapcm(void *chip); - -void sega_pcm_w(void *chip, offs_t offset, UINT8 data); -UINT8 sega_pcm_r(void *chip, offs_t offset); -void sega_pcm_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, - const UINT8* ROMData); - -//void sega_pcm_fwrite_romusage(UINT8 ChipID); -void segapcm_set_mute_mask(void *chip, UINT32 MuteMask); - -#ifdef __cplusplus -} -#endif +/*********************************************************/ +/* SEGA 8bit PCM */ +/*********************************************************/ + +#pragma once + +#define BANK_256 (11) +#define BANK_512 (12) +#define BANK_12M (13) +#define BANK_MASK7 (0x70<<16) +#define BANK_MASKF (0xf0<<16) +#define BANK_MASKF8 (0xf8<<16) + +typedef struct _sega_pcm_interface sega_pcm_interface; +struct _sega_pcm_interface +{ + int bank; +}; + +/*WRITE8_DEVICE_HANDLER( sega_pcm_w ); +READ8_DEVICE_HANDLER( sega_pcm_r ); + +DEVICE_GET_INFO( segapcm ); +#define SOUND_SEGAPCM DEVICE_GET_INFO_NAME( segapcm )*/ + +void SEGAPCM_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_segapcm(void **chip, int clock, int intf_bank); +void device_stop_segapcm(void *chip); +void device_reset_segapcm(void *chip); + +void sega_pcm_w(void *chip, offs_t offset, UINT8 data); +UINT8 sega_pcm_r(void *chip, offs_t offset); +void sega_pcm_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void segapcm_set_mute_mask(void *chip, UINT32 MuteMask); + diff --git a/Frameworks/GME/vgmplay/chips/sn76489.c b/Frameworks/GME/vgmplay/chips/sn76489.c new file mode 100644 index 000000000..4f9fd7f8a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/sn76489.c @@ -0,0 +1,404 @@ +/* + SN76489 emulation + by Maxim in 2001 and 2002 + converted from my original Delphi implementation + + I'm a C newbie so I'm sure there are loads of stupid things + in here which I'll come back to some day and redo + + Includes: + - Super-high quality tone channel "oversampling" by calculating fractional positions on transitions + - Noise output pattern reverse engineered from actual SMS output + - Volume levels taken from actual SMS output + + 07/08/04 Charles MacDonald + Modified for use with SMS Plus: + - Added support for multiple PSG chips. + - Added reset/config/update routines. + - Added context management routines. + - Removed SN76489_GetValues(). + - Removed some unused variables. +*/ + +#include // malloc/free +#include // for FLT_MIN +#include // for memcpy +#include "mamedef.h" +#include "sn76489.h" +#include "panning.h" + +#define NoiseInitialState 0x8000 /* Initial state of shift register */ +#define PSG_CUTOFF 0x6 /* Value below which PSG does not output */ + +static const int PSGVolumeValues[16] = { + /* These values are taken from a real SMS2's output */ +/* {892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, /* I can't remember why 892... :P some scaling I did at some point */ + /* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1) */ + /*1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0*/ +// The MAME core uses 0x2000 as maximum volume (0x1000 for bipolar output) + 4096, 3254, 2584, 2053, 1631, 1295, 1029, 817, 649, 516, 410, 325, 258, 205, 163, 0 +}; + +/*static SN76489_Context SN76489[MAX_SN76489];*/ +static SN76489_Context* LastChipInit = NULL; +//static unsigned short int FNumLimit; + + +SN76489_Context* SN76489_Init( int PSGClockValue, int SamplingRate) +{ + int i; + SN76489_Context* chip = (SN76489_Context*)malloc(sizeof(SN76489_Context)); + if(chip) + { + chip->dClock=(float)(PSGClockValue & 0x7FFFFFF)/16/SamplingRate; + + SN76489_SetMute(chip, MUTE_ALLON); + SN76489_Config(chip, /*MUTE_ALLON,*/ FB_SEGAVDP, SRW_SEGAVDP, 1); + + for( i = 0; i <= 3; i++ ) + centre_panning(chip->panning[i]); + //SN76489_Reset(chip); + + if ((PSGClockValue & 0x80000000) && LastChipInit != NULL) + { + // Activate special NeoGeoPocket Mode + LastChipInit->NgpFlags = 0x80 | 0x00; + chip->NgpFlags = 0x80 | 0x01; + chip->NgpChip2 = LastChipInit; + LastChipInit->NgpChip2 = chip; + LastChipInit = NULL; + } + else + { + chip->NgpFlags = 0x00; + chip->NgpChip2 = NULL; + LastChipInit = chip; + } + } + return chip; +} + +void SN76489_Reset(SN76489_Context* chip) +{ + int i; + + chip->PSGStereo = 0xFF; + + for( i = 0; i <= 3; i++ ) + { + /* Initialise PSG state */ + chip->Registers[2*i] = 1; /* tone freq=1 */ + chip->Registers[2*i+1] = 0xf; /* vol=off */ + chip->NoiseFreq = 0x10; + + /* Set counters to 0 */ + chip->ToneFreqVals[i] = 0; + + /* Set flip-flops to 1 */ + chip->ToneFreqPos[i] = 1; + + /* Set intermediate positions to do-not-use value */ + chip->IntermediatePos[i] = FLT_MIN; + + /* Set panning to centre */ + //centre_panning( chip->panning[i] ); + } + + chip->LatchedRegister = 0; + + /* Initialise noise generator */ + chip->NoiseShiftRegister = NoiseInitialState; + + /* Zero clock */ + chip->Clock = 0; +} + +void SN76489_Shutdown(SN76489_Context* chip) +{ + free(chip); +} + +void SN76489_Config(SN76489_Context* chip, /*int mute,*/ int feedback, int sr_width, int boost_noise) +{ + //chip->Mute = mute; + chip->WhiteNoiseFeedback = feedback; + chip->SRWidth = sr_width; +} + +/* +void SN76489_SetContext(int which, uint8 *data) +{ + memcpy( &SN76489[which], data, sizeof(SN76489_Context) ); +} + +void SN76489_GetContext(int which, uint8 *data) +{ + memcpy( data, &SN76489[which], sizeof(SN76489_Context) ); +} + +uint8 *SN76489_GetContextPtr(int which) +{ + return (uint8 *)&SN76489[which]; +} + +int SN76489_GetContextSize(void) +{ + return sizeof(SN76489_Context); +} +*/ +void SN76489_Write(SN76489_Context* chip, int data) +{ + if ( data & 0x80 ) + { + /* Latch/data byte %1 cc t dddd */ + chip->LatchedRegister = ( data >> 4 ) & 0x07; + chip->Registers[chip->LatchedRegister] = + ( chip->Registers[chip->LatchedRegister] & 0x3f0 ) /* zero low 4 bits */ + | ( data & 0xf ); /* and replace with data */ + } else { + /* Data byte %0 - dddddd */ + if ( !( chip->LatchedRegister % 2 ) && ( chip->LatchedRegister < 5 ) ) + /* Tone register */ + chip->Registers[chip->LatchedRegister] = + ( chip->Registers[chip->LatchedRegister] & 0x00f) /* zero high 6 bits */ + | ( ( data & 0x3f ) << 4 ); /* and replace with data */ + else + /* Other register */ + chip->Registers[chip->LatchedRegister]=data&0x0f; /* Replace with data */ + } + switch (chip->LatchedRegister) { + case 0: + case 2: + case 4: /* Tone channels */ + if ( chip->Registers[chip->LatchedRegister] == 0 ) + chip->Registers[chip->LatchedRegister] = 1; /* Zero frequency changed to 1 to avoid div/0 */ + break; + case 6: /* Noise */ + chip->NoiseShiftRegister = NoiseInitialState; /* reset shift register */ + chip->NoiseFreq = 0x10 << ( chip->Registers[6] & 0x3 ); /* set noise signal generator frequency */ + break; + } +} + +void SN76489_GGStereoWrite(SN76489_Context* chip, int data) +{ + chip->PSGStereo=data; +} + +//void SN76489_Update(SN76489_Context* chip, INT16 **buffer, int length) +void SN76489_Update(SN76489_Context* chip, INT32 **buffer, int length) +{ + int i, j; + int NGPMode; + SN76489_Context* chip2; + SN76489_Context* chip_t; + SN76489_Context* chip_n; + + NGPMode = (chip->NgpFlags >> 7) & 0x01; + if (NGPMode) + chip2 = (SN76489_Context*)chip->NgpChip2; + + if (! NGPMode) + { + chip_t = chip_n = chip; + } + else + { + if (! (chip->NgpFlags & 0x01)) + { + chip_t = chip; + chip_n = chip2; + } + else + { + chip_t = chip2; + chip_n = chip; + } + } + + for( j = 0; j < length; j++ ) + { + /* Tone channels */ + for ( i = 0; i <= 2; ++i ) + if ( (chip_t->Mute >> i) & 1 ) + { + if ( chip_t->IntermediatePos[i] != FLT_MIN ) + /* Intermediate position (antialiasing) */ + chip->Channels[i] = (short)( PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->IntermediatePos[i] ); + else + /* Flat (no antialiasing needed) */ + chip->Channels[i]= PSGVolumeValues[chip->Registers[2 * i + 1]] * chip_t->ToneFreqPos[i]; + } + else + /* Muted channel */ + chip->Channels[i] = 0; + + /* Noise channel */ + if ( (chip_t->Mute >> 3) & 1 ) + { + //chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * ( chip_n->NoiseShiftRegister & 0x1 ) * 2; /* double noise volume */ + // Now the noise is bipolar, too. -Valley Bell + chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * (( chip_n->NoiseShiftRegister & 0x1 ) * 2 - 1); + // due to the way the white noise works here, it seems twice as loud as it should be + if (chip->Registers[6] & 0x4 ) + chip->Channels[3] >>= 1; + } + else + chip->Channels[i] = 0; + + // Build stereo result into buffer + buffer[0][j] = 0; + buffer[1][j] = 0; + if (! chip->NgpFlags) + { + // For all 4 channels + for ( i = 0; i <= 3; ++i ) + { + if ( ( ( chip->PSGStereo >> i ) & 0x11 ) == 0x11 ) + { + // no GG stereo for this channel + if ( chip->panning[i][0] == 1.0f ) + { + buffer[0][j] += chip->Channels[i]; // left + buffer[1][j] += chip->Channels[i]; // right + } + else + { + buffer[0][j] += (INT32)( chip->panning[i][0] * chip->Channels[i] ); // left + buffer[1][j] += (INT32)( chip->panning[i][1] * chip->Channels[i] ); // right + } + } + else + { + // GG stereo overrides panning + buffer[0][j] += ( chip->PSGStereo >> (i+4) & 0x1 ) * chip->Channels[i]; // left + buffer[1][j] += ( chip->PSGStereo >> i & 0x1 ) * chip->Channels[i]; // right + } + } + } + else + { + if (! (chip->NgpFlags & 0x01)) + { + // For all 3 tone channels + for (i = 0; i < 3; i ++) + { + buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip ->Channels[i]; // left + buffer[1][j] += (chip->PSGStereo >> i & 0x1 ) * chip2->Channels[i]; // right + } + } + else + { + // noise channel + i = 3; + buffer[0][j] += (chip->PSGStereo >> (i+4) & 0x1 ) * chip2->Channels[i]; // left + buffer[1][j] += (chip->PSGStereo >> i & 0x1 ) * chip ->Channels[i]; // right + } + } + + /* Increment clock by 1 sample length */ + chip->Clock += chip->dClock; + chip->NumClocksForSample = (int)chip->Clock; /* truncate */ + chip->Clock -= chip->NumClocksForSample; /* remove integer part */ + + /* Decrement tone channel counters */ + for ( i = 0; i <= 2; ++i ) + chip->ToneFreqVals[i] -= chip->NumClocksForSample; + + /* Noise channel: match to tone2 or decrement its counter */ + if ( chip->NoiseFreq == 0x80 ) + chip->ToneFreqVals[3] = chip->ToneFreqVals[2]; + else + chip->ToneFreqVals[3] -= chip->NumClocksForSample; + + /* Tone channels: */ + for ( i = 0; i <= 2; ++i ) { + if ( chip->ToneFreqVals[i] <= 0 ) { /* If the counter gets below 0... */ + if (chip->Registers[i*2]>=PSG_CUTOFF) { + /* For tone-generating values, calculate how much of the sample is + and how much is - */ + /* This is optimised into an even more confusing state than it was in the first place... */ + chip->IntermediatePos[i] = ( chip->NumClocksForSample - chip->Clock + 2 * chip->ToneFreqVals[i] ) * chip->ToneFreqPos[i] / ( chip->NumClocksForSample + chip->Clock ); + /* Flip the flip-flop */ + chip->ToneFreqPos[i] = -chip->ToneFreqPos[i]; + } else { + /* stuck value */ + chip->ToneFreqPos[i] = 1; + chip->IntermediatePos[i] = FLT_MIN; + } + chip->ToneFreqVals[i] += chip->Registers[i*2] * ( chip->NumClocksForSample / chip->Registers[i*2] + 1 ); + } + else + /* signal no antialiasing needed */ + chip->IntermediatePos[i] = FLT_MIN; + } + + /* Noise channel */ + if ( chip->ToneFreqVals[3] <= 0 ) { + /* If the counter gets below 0... */ + /* Flip the flip-flop */ + chip->ToneFreqPos[3] = -chip->ToneFreqPos[3]; + if (chip->NoiseFreq != 0x80) + /* If not matching tone2, decrement counter */ + chip->ToneFreqVals[3] += chip->NoiseFreq * ( chip->NumClocksForSample / chip->NoiseFreq + 1 ); + if (chip->ToneFreqPos[3] == 1) { + /* On the positive edge of the square wave (only once per cycle) */ + int Feedback; + if ( chip->Registers[6] & 0x4 ) { + /* White noise */ + /* Calculate parity of fed-back bits for feedback */ + switch (chip->WhiteNoiseFeedback) { + /* Do some optimised calculations for common (known) feedback values */ + case 0x0003: /* SC-3000, BBC %00000011 */ + case 0x0009: /* SMS, GG, MD %00001001 */ + /* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */ + /* since that's (one or more bits set) && (not all bits set) */ + Feedback = ( ( chip->NoiseShiftRegister & chip->WhiteNoiseFeedback ) + && ( (chip->NoiseShiftRegister & chip->WhiteNoiseFeedback ) ^ chip->WhiteNoiseFeedback ) ); + break; + default: + /* Default handler for all other feedback values */ + /* XOR fold bits into the final bit */ + Feedback = chip->NoiseShiftRegister & chip->WhiteNoiseFeedback; + Feedback ^= Feedback >> 8; + Feedback ^= Feedback >> 4; + Feedback ^= Feedback >> 2; + Feedback ^= Feedback >> 1; + Feedback &= 1; + break; + } + } else /* Periodic noise */ + Feedback=chip->NoiseShiftRegister&1; + + chip->NoiseShiftRegister=(chip->NoiseShiftRegister>>1) | (Feedback << (chip->SRWidth-1)); + } + } + } +} + +/*void SN76489_UpdateOne(SN76489_Context* chip, int *l, int *r) +{ + INT16 tl,tr; + INT16 *buff[2] = { &tl, &tr }; + SN76489_Update( chip, buff, 1 ); + *l = tl; + *r = tr; +}*/ + + +/*int SN76489_GetMute(SN76489_Context* chip) +{ + return chip->Mute; +}*/ + +void SN76489_SetMute(SN76489_Context* chip, int val) +{ + chip->Mute=val; +} + +void SN76489_SetPanning(SN76489_Context* chip, int ch0, int ch1, int ch2, int ch3) +{ + calc_panning( chip->panning[0], ch0 ); + calc_panning( chip->panning[1], ch1 ); + calc_panning( chip->panning[2], ch2 ); + calc_panning( chip->panning[3], ch3 ); +} diff --git a/Frameworks/GME/vgmplay/chips/sn76489.h b/Frameworks/GME/vgmplay/chips/sn76489.h new file mode 100644 index 000000000..77d5ad480 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/sn76489.h @@ -0,0 +1,108 @@ +#ifndef _SN76489_H_ +#define _SN76489_H_ + +// all these defines are defined in mamedef.h, but GCC's #ifdef doesn't seem to know typedefs +/*#ifndef INT32 +#define INT32 signed long +#endif +#ifndef UINT16 +#define UINT16 unsigned short +#endif +#ifndef INT16 +#define INT16 signed short +#endif +#ifndef INT8 +#define INT8 signed char +#endif*/ +#ifndef uint8 +#define uint8 signed char +#endif + + +/*#define MAX_SN76489 4*/ + +/* + More testing is needed to find and confirm feedback patterns for + SN76489 variants and compatible chips. +*/ +enum feedback_patterns { + FB_BBCMICRO = 0x8005, /* Texas Instruments TMS SN76489N (original) from BBC Micro computer */ + FB_SC3000 = 0x0006, /* Texas Instruments TMS SN76489AN (rev. A) from SC-3000H computer */ + FB_SEGAVDP = 0x0009, /* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */ +}; + +enum sr_widths { + SRW_SC3000BBCMICRO = 15, + SRW_SEGAVDP = 16 +}; + +enum volume_modes { + VOL_TRUNC = 0, /* Volume levels 13-15 are identical */ + VOL_FULL = 1, /* Volume levels 13-15 are unique */ +}; + +enum mute_values { + MUTE_ALLOFF = 0, /* All channels muted */ + MUTE_TONE1 = 1, /* Tone 1 mute control */ + MUTE_TONE2 = 2, /* Tone 2 mute control */ + MUTE_TONE3 = 4, /* Tone 3 mute control */ + MUTE_NOISE = 8, /* Noise mute control */ + MUTE_ALLON = 15, /* All channels enabled */ +}; + +typedef struct +{ + int Mute; // per-channel muting + int BoostNoise; // double noise volume when non-zero + + /* Variables */ + float Clock; + float dClock; + int PSGStereo; + int NumClocksForSample; + int WhiteNoiseFeedback; + int SRWidth; + + /* PSG registers: */ + int Registers[8]; /* Tone, vol x4 */ + int LatchedRegister; + int NoiseShiftRegister; + int NoiseFreq; /* Noise channel signal generator frequency */ + + /* Output calculation variables */ + int ToneFreqVals[4]; /* Frequency register values (counters) */ + int ToneFreqPos[4]; /* Frequency channel flip-flops */ + int Channels[4]; /* Value of each channel, before stereo is applied */ + float IntermediatePos[4]; /* intermediate values used at boundaries between + and - (does not need double accuracy)*/ + + float panning[4][2]; /* fake stereo */ + + int NgpFlags; /* bit 7 - NGP Mode on/off, bit 0 - is 2nd NGP chip */ + void* NgpChip2; +} SN76489_Context; + +/* Function prototypes */ +SN76489_Context* SN76489_Init(int PSGClockValue, int SamplingRate); +void SN76489_Reset(SN76489_Context* chip); +void SN76489_Shutdown(SN76489_Context* chip); +void SN76489_Config(SN76489_Context* chip, /*int mute,*/ int feedback, int sw_width, int boost_noise); +/* +void SN76489_SetContext(SN76489_Context* chip, uint8 *data); +void SN76489_GetContext(SN76489_Context* chip, uint8 *data); +uint8 *SN76489_GetContextPtr(int chip); +int SN76489_GetContextSize(void);*/ +void SN76489_Write(SN76489_Context* chip, int data); +void SN76489_GGStereoWrite(SN76489_Context* chip, int data); +//void SN76489_Update(SN76489_Context* chip, INT16 **buffer, int length); +void SN76489_Update(SN76489_Context* chip, INT32 **buffer, int length); + +/* Non-standard getters and setters */ +//int SN76489_GetMute(SN76489_Context* chip); +void SN76489_SetMute(SN76489_Context* chip, int val); + +void SN76489_SetPanning(SN76489_Context* chip, int ch0, int ch1, int ch2, int ch3); + +/* and a non-standard data getter */ +//void SN76489_UpdateOne(SN76489_Context* chip, int *l, int *r); + +#endif /* _SN76489_H_ */ diff --git a/Frameworks/GME/vgmplay/chips/sn76496.c b/Frameworks/GME/vgmplay/chips/sn76496.c new file mode 100644 index 000000000..854b3e49d --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/sn76496.c @@ -0,0 +1,883 @@ +/*************************************************************************** + + sn76496.c + by Nicola Salmoria + with contributions by others + + Routines to emulate the: + Texas Instruments SN76489, SN76489A, SN76494/SN76496 + ( Also known as, or at least compatible with, the TMS9919 and SN94624.) + and the Sega 'PSG' used on the Master System, Game Gear, and Megadrive/Genesis + This chip is known as the Programmable Sound Generator, or PSG, and is a 4 + channel sound generator, with three squarewave channels and a noise/arbitrary + duty cycle channel. + + Noise emulation for all verified chips should be accurate: + + ** SN76489 uses a 15-bit shift register with taps on bits D and E, output on E, + XOR function. + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is inverted. + ** SN94624 is the same as SN76489 but lacks the /8 divider on its clock input. + ** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F, + XOR function. + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + Its output is not inverted. + ** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input. + ** SN76496 is identical in operation to the SN76489A, but the audio input is + documented. + All the TI-made PSG chips have an audio input line which is mixed with the 4 channels + of output. (It is undocumented and may not function properly on the sn76489, 76489a + and 76494; the sn76489a input is mentioned in datasheets for the tms5200) + All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is + written to the frequency register. + ** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps + on bits C and F, output on F + It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle. + (whether it uses an XOR or XNOR needs to be verified, assumed XOR) + (whether output is inverted or not needs to be verified, assumed to be inverted) + ** Sega Game Gear PSG is identical to the SMS3/MD/Genesis one except it has an + extra register for mapping which channels go to which speaker. + The register, connected to a z80 port, means: + for bits 7 6 5 4 3 2 1 0 + L3 L2 L1 L0 R3 R2 R1 R0 + Noise is an XOR function, and audio output is negated before being output. + All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written + to the frequency register. + ** NCR7496 (as used on the Tandy 1000) is similar to the SN76489 but with a + different noise LFSR patttern: taps on bits A and E, output on E + It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. + (all this chip's info needs to be verified) + + 28/03/2005 : Sebastien Chevalier + Update th SN76496Write func, according to SN76489 doc found on SMSPower. + - On write with 0x80 set to 0, when LastRegister is other then TONE, + the function is similar than update with 0x80 set to 1 + + 23/04/2007 : Lord Nightmare + Major update, implement all three different noise generation algorithms and a + set_variant call to discern among them. + + 28/04/2009 : Lord Nightmare + Add READY line readback; cleaned up struct a bit. Cleaned up comments. + Add more TODOs. Fixed some unsaved savestate related stuff. + + 04/11/2009 : Lord Nightmare + Changed the way that the invert works (it now selects between XOR and XNOR + for the taps), and added R->OldNoise to simulate the extra 0 that is always + output before the noise LFSR contents are after an LFSR reset. + This fixes SN76489/A to match chips. Added SN94624. + + 14/11/2009 : Lord Nightmare + Removed STEP mess, vastly simplifying the code. Made output bipolar rather + than always above the 0 line, but disabled that code due to pending issues. + + 16/11/2009 : Lord Nightmare + Fix screeching in regulus: When summing together four equal channels, the + size of the max amplitude per channel should be 1/4 of the max range, not + 1/3. Added NCR7496. + + 18/11/2009 : Lord Nightmare + Modify Init functions to support negating the audio output. The gamegear + psg does this. Change gamegear and sega psgs to use XOR rather than XNOR + based on testing. Got rid of R->OldNoise and fixed taps accordingly. + Added stereo support for game gear. + + 15/01/2010 : Lord Nightmare + Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods. + Note that properly emulating the noise cycle bit timing accurately may require + extensive rewriting. + + 24/01/2010: Lord Nightmare + Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively. + Thanks to PlgDavid for providing samples which helped immensely here. + Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than + the others, as in real life. + + 15/02/2010: Lord Nightmare & Michael Zapf (additional testing by PlgDavid) + Fix noise period when set to mirror channel 3 and channel 3 period is set to 0 (tested on hardware for noise, wave needs tests) - MZ + Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid + Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here. + + 23/02/2011: Lord Nightmare & Enik + Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the + frequency register, while the others have 0x400 as before. Should fix a bug + or two on sega games, particularly Vigilante on Sega Master System. Verified + on SMS hardware. + + TODO: * Implement the TMS9919 - any difference to sn94624? + * Implement the T6W28; has registers in a weird order, needs writes + to be 'sanitized' first. Also is stereo, similar to game gear. + * Test the NCR7496; Smspower says the whitenoise taps are A and E, + but this needs verification on real hardware. + * Factor out common code so that the SAA1099 can share some code. + * Convert to modern device +***************************************************************************/ + +/* Note: I patched the core to speed the emulation up (factor 8!!) + My Pentium2 233MHz was too slow for two SN76496 chips in release mode! + Now a 2xSN76496 vgm takes about 45 % CPU. */ + +#include "mamedef.h" +#ifdef _DEBUG +#include +#endif +//#include "emu.h" +//#include "streams.h" +#include +#include +#include "sn76496.h" + +#define NULL ((void *)0) + + +//#define MAX_OUTPUT 0x7fff +#define MAX_OUTPUT 0x8000 +#define NOISEMODE (R->Register[6]&4)?1:0 + + +typedef struct _sn76496_state sn76496_state; +struct _sn76496_state +{ + //sound_stream * Channel; + INT32 VolTable[16]; /* volume table (for 4-bit to db conversion)*/ + INT32 Register[8]; /* registers */ + INT32 LastRegister; /* last register written */ + INT32 Volume[4]; /* db volume of voice 0-2 and noise */ + UINT32 RNG; /* noise generator LFSR*/ + INT32 ClockDivider; /* clock divider */ + INT32 CurrentClock; + INT32 FeedbackMask; /* mask for feedback */ + INT32 WhitenoiseTap1; /* mask for white noise tap 1 (higher one, usually bit 14) */ + INT32 WhitenoiseTap2; /* mask for white noise tap 2 (lower one, usually bit 13)*/ + INT32 Negate; /* output negate flag */ + INT32 Stereo; /* whether we're dealing with stereo or not */ + INT32 StereoMask; /* the stereo output mask */ + INT32 Period[4]; /* Length of 1/2 of waveform */ + INT32 Count[4]; /* Position within the waveform */ + INT32 Output[4]; /* 1-bit output of each channel, pre-volume */ + INT32 CyclestoREADY;/* number of cycles until the READY line goes active */ + INT32 Freq0IsMax; /* flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0 */ + UINT32 MuteMsk[4]; + UINT8 NgpFlags; /* bit 7 - NGP Mode on/off, bit 0 - is 2nd NGP chip */ + sn76496_state* NgpChip2; /* Pointer to other Chip */ +}; + + +static sn76496_state* LastChipInit = NULL; +static unsigned short int FNumLimit; + +/*INLINE sn76496_state *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == SN76496 || + device->type() == SN76489 || + device->type() == SN76489A || + device->type() == SN76494 || + device->type() == SN94624 || + device->type() == NCR7496 || + device->type() == GAMEGEAR || + device->type() == SMSIII); + return (sn76496_state *)downcast(device)->token(); +}*/ + +//READ_LINE_DEVICE_HANDLER( sn76496_ready_r ) +UINT8 sn76496_ready_r(void *chip, offs_t offset) +{ + //sn76496_state *R = get_safe_token(device); + sn76496_state *R = (sn76496_state*)chip; + //stream_update(R->Channel); + return (R->CyclestoREADY? 0 : 1); +} + +//WRITE8_DEVICE_HANDLER( sn76496_stereo_w ) +void sn76496_stereo_w(void *chip, offs_t offset, UINT8 data) +{ + //sn76496_state *R = get_safe_token(device); + sn76496_state *R = (sn76496_state*)chip; + //stream_update(R->Channel); + if (R->Stereo) R->StereoMask = data; +#ifdef _DEBUG + else logerror("Call to stereo write with mono chip!\n"); +#endif +} + +//WRITE8_DEVICE_HANDLER( sn76496_w ) +void sn76496_write_reg(void *chip, offs_t offset, UINT8 data) +{ + //sn76496_state *R = get_safe_token(device); + sn76496_state *R = (sn76496_state*)chip; + int n, r, c; + + + /* update the output buffer before changing the registers */ + //stream_update(R->Channel); + + /* set number of cycles until READY is active; this is always one + 'sample', i.e. it equals the clock divider exactly; until the + clock divider is fully supported, we delay until one sample has + played. The fact that this below is '2' and not '1' is because + of a ?race condition? in the mess crvision driver, where after + any sample is played at all, no matter what, the cycles_to_ready + ends up never being not ready, unless this value is greater than + 1. Once the full clock divider stuff is written, this should no + longer be an issue. */ + R->CyclestoREADY = 2; + + if (data & 0x80) + { + r = (data & 0x70) >> 4; + R->LastRegister = r; + R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); + } + else + { + r = R->LastRegister; + } + c = r/2; + switch (r) + { + case 0: /* tone 0 : frequency */ + case 2: /* tone 1 : frequency */ + case 4: /* tone 2 : frequency */ + if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4); + if ((R->Register[r] != 0) || (R->Freq0IsMax == 0)) R->Period[c] = R->Register[r]; + else R->Period[c] = 0x400; + if (r == 4) + { + /* update noise shift frequency */ + if ((R->Register[6] & 0x03) == 0x03) + R->Period[3] = 2 * R->Period[2]; + } + break; + case 1: /* tone 0 : volume */ + case 3: /* tone 1 : volume */ + case 5: /* tone 2 : volume */ + case 7: /* noise : volume */ + R->Volume[c] = R->VolTable[data & 0x0f]; + if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); + + // // "Every volume write resets the waveform to High level.", TmEE, 2012-11-24 on SMSPower + // R->Output[c] = 1; + // R->Count[c] = R->Period[c]; + // disabled for now - sounds awful + break; + case 6: /* noise : frequency, mode */ + { +#ifdef _DEBUG + //if ((data & 0x80) == 0) logerror("sn76489: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", R->Register[6], data); +#endif + if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); + n = R->Register[6]; + /* N/512,N/1024,N/2048,Tone #3 output */ + R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (1 << (5+(n&3))); + R->RNG = R->FeedbackMask; + } + break; + } +} + +//static STREAM_UPDATE( SN76496Update ) +void SN76496Update(void *chip, stream_sample_t **outputs, int samples) +{ + int i; + //sn76496_state *R = (sn76496_state *)param; + sn76496_state *R = (sn76496_state*)chip; + sn76496_state *R2; + stream_sample_t *lbuffer = outputs[0]; + //stream_sample_t *rbuffer = (R->Stereo)?outputs[1]:NULL; + stream_sample_t *rbuffer = outputs[1]; + INT32 out = 0; + INT32 out2 = 0; + INT32 vol[4]; + UINT8 NGPMode; + INT32 ggst[2]; + + NGPMode = (R->NgpFlags >> 7) & 0x01; + if (NGPMode) + R2 = R->NgpChip2; + + if (! NGPMode) + { + // Speed Hack + out = 0; + for (i = 0; i < 3; i ++) + { + if (R->Period[i] || R->Volume[i]) + { + out = 1; + break; + } + } + if (R->Volume[3]) + out = 1; + if (! out) + { + memset(lbuffer, 0x00, sizeof(stream_sample_t) * samples); + memset(rbuffer, 0x00, sizeof(stream_sample_t) * samples); + return; + } + } + + ggst[0] = 0x01; + ggst[1] = 0x01; + while (samples > 0) + { + /* Speed Patch */ + /*// clock chip once + if (R->CurrentClock > 0) // not ready for new divided clock + { + R->CurrentClock--; + } + else // ready for new divided clock, make a new sample + { + R->CurrentClock = R->ClockDivider-1;*/ + /* decrement Cycles to READY by one */ + if (R->CyclestoREADY >0) R->CyclestoREADY--; + + // handle channels 0,1,2 + for (i = 0;i < 3;i++) + { + R->Count[i]--; + if (R->Count[i] <= 0) + { + R->Output[i] ^= 1; + R->Count[i] = R->Period[i]; + } + } + + // handle channel 3 + R->Count[3]--; + if (R->Count[3] <= 0) + { + // if noisemode is 1, both taps are enabled + // if noisemode is 0, the lower tap, whitenoisetap2, is held at 0 + if (((R->RNG & R->WhitenoiseTap1)?1:0) ^ ((((R->RNG & R->WhitenoiseTap2)?1:0))*(NOISEMODE))) + { + R->RNG >>= 1; + R->RNG |= R->FeedbackMask; + } + else + { + R->RNG >>= 1; + } + R->Output[3] = R->RNG & 1; + + R->Count[3] = R->Period[3]; + } + //} + + + /*if (R->Stereo) + { + out = (((R->StereoMask&0x10)&&R->Output[0])?R->Volume[0]:0) + + (((R->StereoMask&0x20)&&R->Output[1])?R->Volume[1]:0) + + (((R->StereoMask&0x40)&&R->Output[2])?R->Volume[2]:0) + + (((R->StereoMask&0x80)&&R->Output[3])?R->Volume[3]:0); + + out2 = (((R->StereoMask&0x1)&&R->Output[0])?R->Volume[0]:0) + + (((R->StereoMask&0x2)&&R->Output[1])?R->Volume[1]:0) + + (((R->StereoMask&0x4)&&R->Output[2])?R->Volume[2]:0) + + (((R->StereoMask&0x8)&&R->Output[3])?R->Volume[3]:0); + } + else + { + out = (R->Output[0]?R->Volume[0]:0) + +(R->Output[1]?R->Volume[1]:0) + +(R->Output[2]?R->Volume[2]:0) + +(R->Output[3]?R->Volume[3]:0); + }*/ + + // --- CUSTOM CODE START -- + out = out2 = 0; + if (! R->NgpFlags) + { + for (i = 0; i < 4; i ++) + { + // --- Preparation Start --- + // Bipolar output + vol[i] = R->Output[i] ? +1 : -1; + + // Disable high frequencies (> SampleRate / 2) for tone channels + // Freq. 0/1 isn't disabled becaus it would also disable PCM + if (i != 3) + { + if (R->Period[i] <= FNumLimit && R->Period[i] > 1) + vol[i] = 0; + } + vol[i] &= R->MuteMsk[i]; + // --- Preparation End --- + + if (R->Stereo) + { + ggst[0] = (R->StereoMask & (0x10 << i)) ? 0x01 : 0x00; + ggst[1] = (R->StereoMask & (0x01 << i)) ? 0x01 : 0x00; + } + if (R->Period[i] > 1 || i == 3) + { + out += vol[i] * R->Volume[i] * ggst[0]; + out2 += vol[i] * R->Volume[i] * ggst[1]; + } + else + { + // Make Bipolar Output with PCM possible + //out += (2 * R->Volume[i] - R->VolTable[5]) * ggst[0]; + //out2 += (2 * R->Volume[i] - R->VolTable[5]) * ggst[1]; + out += R->Volume[i] * ggst[0]; + out2 += R->Volume[i] * ggst[1]; + } + } + } + else + { + if (! (R->NgpFlags & 0x01)) + { + // Tone Channel 1-3 + if (R->Stereo) + { + ggst[0] = (R->StereoMask & (0x10 << i)) ? 0x01 : 0x00; + ggst[1] = (R->StereoMask & (0x01 << i)) ? 0x01 : 0x00; + } + for (i = 0; i < 3; i ++) + { + // --- Preparation Start --- + // Bipolar output + vol[i] = R->Output[i] ? +1 : -1; + + // Disable high frequencies (> SampleRate / 2) for tone channels + // Freq. 0 isn't disabled becaus it would also disable PCM + if (R->Period[i] <= FNumLimit && R->Period[i]) + vol[i] = 0; + vol[i] &= R->MuteMsk[i]; + // --- Preparation End --- + + //out += vol[i] * R->Volume[i]; + //out2 += vol[i] * R2->Volume[i]; + if (R->Period[i]) + { + out += vol[i] * R->Volume[i] * ggst[0]; + out2 += vol[i] * R2->Volume[i] * ggst[1]; + } + else + { + // Make Bipolar Output with PCM possible + out += R->Volume[i] * ggst[0]; + out2 += R2->Volume[i] * ggst[1]; + } + } + } + else + { + // --- Preparation Start --- + // Bipolar output + vol[i] = R->Output[i] ? +1 : -1; + + //vol[i] &= R->MuteMsk[i]; + vol[i] &= R2->MuteMsk[i]; // use MuteMask from chip 0 + // --- Preparation End --- + + // Noise Channel + if (R->Stereo) + { + ggst[0] = (R->StereoMask & 0x80) ? 0x01 : 0x00; + ggst[1] = (R->StereoMask & 0x08) ? 0x01 : 0x00; + } + else + { + ggst[0] = 0x01; + ggst[1] = 0x01; + } + //out += vol[3] * R2->Volume[3]; + //out2 += vol[3] * R->Volume[3]; + out += vol[3] * R2->Volume[3] * ggst[0]; + out2 += vol[3] * R->Volume[3] * ggst[1]; + } + } + // --- CUSTOM CODE END -- + + if(R->Negate) { out = -out; out2 = -out2; } + + *(lbuffer++) = out >> 1; // Output is Bipolar + //if (R->Stereo) *(rbuffer++) = out2; + *(rbuffer++) = out2 >> 1; + samples--; + } +} + + + +static void SN76496_set_gain(sn76496_state *R,int gain) +{ + int i; + double out; + + + gain &= 0xff; + + /* increase max output basing on gain (0.2 dB per step) */ + out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range + while (gain-- > 0) + out *= 1.023292992; /* = (10 ^ (0.2/20)) */ + + /* build volume table (2dB per step) */ + for (i = 0;i < 15;i++) + { + /* limit volume to avoid clipping */ + if (out > MAX_OUTPUT / 4) R->VolTable[i] = MAX_OUTPUT / 4; + //else R->VolTable[i] = out; + else R->VolTable[i] = (INT32)(out + 0.5); // I like rounding + + out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ + } + R->VolTable[15] = 0; +} + + + +//static int SN76496_init(running_device *device, sn76496_state *R, int stereo) +static int SN76496_init(int clock, sn76496_state *R, int stereo) +{ + int sample_rate = clock/2; + int i; + + //R->Channel = stream_create(device,0,(stereo?2:1),sample_rate,R,SN76496Update); + + for (i = 0;i < 4;i++) R->Volume[i] = 0; + + R->LastRegister = 0; + for (i = 0;i < 8;i+=2) + { + R->Register[i] = 0; + R->Register[i + 1] = 0x0f; /* volume = 0 */ + } + + for (i = 0;i < 4;i++) + { + R->Output[i] = R->Period[i] = R->Count[i] = 0; + R->MuteMsk[i] = ~0x00; + } + + /* Default is SN76489A */ + R->ClockDivider = 8; + R->FeedbackMask = 0x10000; /* mask for feedback */ + R->WhitenoiseTap1 = 0x04; /* mask for white noise tap 1*/ + R->WhitenoiseTap2 = 0x08; /* mask for white noise tap 2*/ + R->Negate = 0; /* channels are not negated */ + R->Stereo = stereo; /* depends on init */ + R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/ + R->StereoMask = 0xFF; /* all channels enabled */ + R->Freq0IsMax = 1; /* frequency set to 0 results in freq = 0x400 rather than 0 */ + + R->RNG = R->FeedbackMask; + R->Output[3] = R->RNG & 1; + + R->NgpFlags = 0x00; + R->NgpChip2 = NULL; + + //return 0; + return sample_rate; +} + + +//static void generic_start(running_device *device, int feedbackmask, int noisetap1, int noisetap2, int negate, int stereo, int clockdivider, int freq0) +static int generic_start(sn76496_state *chip, int clock, int feedbackmask, int noisetap1, int noisetap2, int negate, int stereo, int clockdivider, int freq0) +{ + int sample_rate; + + //sn76496_state *chip = get_safe_token(device); + //sn76496_state *chip; + sn76496_state *chip2; + + //if (SN76496_init(device,chip,stereo) != 0) + // fatalerror("Error creating SN76496 chip"); + sample_rate = SN76496_init(clock & 0x7FFFFFFF, chip, stereo); + if ((clock & 0x80000000) && LastChipInit != NULL) + { + // Activate special NeoGeoPocket Mode + chip2 = LastChipInit; + chip2->NgpFlags = 0x80 | 0x00; + chip->NgpFlags = 0x80 | 0x01; + chip->NgpChip2 = chip2; + chip2->NgpChip2 = chip; + LastChipInit = NULL; + } + else + { + LastChipInit = chip; + } + SN76496_set_gain(chip, 0); + + chip->FeedbackMask = feedbackmask; + chip->WhitenoiseTap1 = noisetap1; + chip->WhitenoiseTap2 = noisetap2; + chip->Negate = negate; + chip->Stereo = stereo; + if (clockdivider) + chip->ClockDivider = clockdivider; + chip->CurrentClock = clockdivider-1; + chip->Freq0IsMax = freq0; + + /* Speed Patch*/ + sample_rate /= chip->ClockDivider; + + /*state_save_register_device_item_array(device, 0, chip->VolTable); + state_save_register_device_item_array(device, 0, chip->Register); + state_save_register_device_item(device, 0, chip->LastRegister); + state_save_register_device_item_array(device, 0, chip->Volume); + state_save_register_device_item(device, 0, chip->RNG); + state_save_register_device_item(device, 0, chip->ClockDivider); + state_save_register_device_item(device, 0, chip->CurrentClock); + state_save_register_device_item(device, 0, chip->FeedbackMask); + state_save_register_device_item(device, 0, chip->WhitenoiseTap1); + state_save_register_device_item(device, 0, chip->WhitenoiseTap2); + state_save_register_device_item(device, 0, chip->Negate); + state_save_register_device_item(device, 0, chip->Stereo); + state_save_register_device_item(device, 0, chip->StereoMask); + state_save_register_device_item_array(device, 0, chip->Period); + state_save_register_device_item_array(device, 0, chip->Count); + state_save_register_device_item_array(device, 0, chip->Output); + state_save_register_device_item(device, 0, chip->CyclestoREADY);*/ + + return sample_rate; +} + +unsigned long int sn76496_start(void **chip, int clock, int shiftregwidth, int noisetaps, + int negate, int stereo, int clockdivider, int freq0) +{ + sn76496_state* sn_chip; + int ntap[2]; + int curbit; + int curtap; + + sn_chip = (sn76496_state*)malloc(sizeof(sn76496_state)); + if (sn_chip == NULL) + return 0; + memset(sn_chip, 0x00, sizeof(sn76496_state)); + *chip = sn_chip; + + // extract single noise tap bits + curtap = 0; + for (curbit = 0; curbit < 16; curbit ++) + { + if (noisetaps & (1 << curbit)) + { + ntap[curtap] = (1 << curbit); + curtap ++; + if (curtap >= 2) + break; + } + } + while(curtap < 2) + { + ntap[curtap] = ntap[0]; + curtap ++; + } + + return generic_start(sn_chip, clock, 1 << (shiftregwidth - 1), ntap[0], ntap[1], + negate, ! stereo, clockdivider ? 1 : 8, freq0); +} + +void sn76496_shutdown(void *chip) +{ + sn76496_state *R = (sn76496_state*)chip; + + free(R); + return; +} + +void sn76496_reset(void *chip) +{ + sn76496_state *R; + UINT8 i; + + R = (sn76496_state*)chip; + + for (i = 0;i < 4;i++) R->Volume[i] = 0; + + R->LastRegister = 0; + for (i = 0;i < 8;i+=2) + { + R->Register[i] = 0; + R->Register[i + 1] = 0x0f; /* volume = 0 */ + } + + for (i = 0;i < 4;i++) + { + R->Output[i] = R->Period[i] = R->Count[i] = 0; + } + + R->CyclestoREADY = 1; + R->StereoMask = 0xFF; /* all channels enabled */ + + R->RNG = R->FeedbackMask; + R->Output[3] = R->RNG & 1; + + return; +} + +void sn76496_freq_limiter(int clock, int clockdiv, int sample_rate) +{ + FNumLimit = (unsigned short int)((clock / (clockdiv ? 2.0 : 16.0)) / sample_rate); + + return; +} + +void sn76496_set_mutemask(void *chip, UINT32 MuteMask) +{ + sn76496_state *R = (sn76496_state*)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 4; CurChn ++) + R->MuteMsk[CurChn] = (MuteMask & (1 << CurChn)) ? 0 : ~0; + + return; +} + +// function parameters: device, feedback destination tap, feedback source taps, +// normal(false)/invert(true), mono(false)/stereo(true), clock divider factor + +/*static DEVICE_START( sn76489 ) +{ + generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE); // SN76489 not verified yet. todo: verify; +} + +static DEVICE_START( sn76489a ) +{ + generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE); // SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid) +} + +static DEVICE_START( sn76494 ) +{ + generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 1, TRUE); // SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider) +} + +static DEVICE_START( sn76496 ) +{ + generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE); // SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf) +} + +static DEVICE_START( sn94624 ) +{ + generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 1, TRUE); // SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid +} + +static DEVICE_START( ncr7496 ) +{ + generic_start(device, 0x8000, 0x02, 0x20, FALSE, FALSE, 8, TRUE); // NCR7496 not verified; info from smspower wiki +} + +static DEVICE_START( gamegear ) +{ + generic_start(device, 0x8000, 0x01, 0x08, TRUE, TRUE, 8, FALSE); // Verified by Justin Kerk +} + +static DEVICE_START( smsiii ) +{ + generic_start(device, 0x8000, 0x01, 0x08, TRUE, FALSE, 8, FALSE); // todo: verify; from smspower wiki, assumed to have same invert as gamegear +}*/ + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( sn76496 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(sn76496_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76496 ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: // Nothing break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "SN76496"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "TI PSG"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.1"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + +DEVICE_GET_INFO( sn76489 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76489 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "SN76489"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + +DEVICE_GET_INFO( sn76489a ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76489a ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "SN76489A"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + +DEVICE_GET_INFO( sn76494 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn76494 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "SN76494"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + +DEVICE_GET_INFO( sn94624 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn94624 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "SN94624"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + +DEVICE_GET_INFO( ncr7496 ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ncr7496 ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "NCR7496"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + +DEVICE_GET_INFO( gamegear ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( gamegear ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "Game Gear PSG"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + +DEVICE_GET_INFO( smsiii ) +{ + switch (state) + { + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( smsiii ); break; + case DEVINFO_STR_NAME: strcpy(info->s, "SMSIII PSG"); break; + default: DEVICE_GET_INFO_CALL(sn76496); break; + } +} + + +/*DEFINE_LEGACY_SOUND_DEVICE(SN76496, sn76496); +DEFINE_LEGACY_SOUND_DEVICE(SN76489, sn76489); +DEFINE_LEGACY_SOUND_DEVICE(SN76489A, sn76489a); +DEFINE_LEGACY_SOUND_DEVICE(SN76494, sn76494); +DEFINE_LEGACY_SOUND_DEVICE(SN94624, sn94624); +DEFINE_LEGACY_SOUND_DEVICE(NCR7496, ncr7496); +DEFINE_LEGACY_SOUND_DEVICE(GAMEGEAR, gamegear); +DEFINE_LEGACY_SOUND_DEVICE(SMSIII, smsiii);*/ diff --git a/Frameworks/GME/vgmplay/chips/sn76496.h b/Frameworks/GME/vgmplay/chips/sn76496.h new file mode 100644 index 000000000..176d2ba75 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/sn76496.h @@ -0,0 +1,35 @@ +#pragma once + +/*READ8_DEVICE_HANDLER( sn76496_ready_r ); +WRITE8_DEVICE_HANDLER( sn76496_w ); +WRITE8_DEVICE_HANDLER( sn76496_stereo_w ); + +DEVICE_GET_INFO( sn76496 ); +DEVICE_GET_INFO( sn76489 ); +DEVICE_GET_INFO( sn76489a ); +DEVICE_GET_INFO( sn76494 ); +DEVICE_GET_INFO( sn94624 ); +DEVICE_GET_INFO( ncr7496 ); +DEVICE_GET_INFO( gamegear ); +DEVICE_GET_INFO( smsiii ); + +#define SOUND_SN76496 DEVICE_GET_INFO_NAME( sn76496 ) +#define SOUND_SN76489 DEVICE_GET_INFO_NAME( sn76489 ) +#define SOUND_SN76489A DEVICE_GET_INFO_NAME( sn76489a ) +#define SOUND_SN76494 DEVICE_GET_INFO_NAME( sn76494 ) +#define SOUND_SN94624 DEVICE_GET_INFO_NAME( sn94624 ) +#define SOUND_NCR7496 DEVICE_GET_INFO_NAME( ncr7496 ) +#define SOUND_GAMEGEAR DEVICE_GET_INFO_NAME( gamegear ) +#define SOUND_SMSIII DEVICE_GET_INFO_NAME( smsiii )*/ + +UINT8 sn76496_ready_r(void *chip, offs_t offset); +void sn76496_write_reg(void *chip, offs_t offset, UINT8 data); +void sn76496_stereo_w(void *chip, offs_t offset, UINT8 data); + +void SN76496Update(void *chip, stream_sample_t **outputs, int samples); +unsigned long int sn76496_start(void **chip, int clock, int shiftregwidth, int noisetaps, + int negate, int stereo, int clockdivider, int freq0); +void sn76496_shutdown(void *chip); +void sn76496_reset(void *chip); +void sn76496_freq_limiter(int clock, int clockdiv, int sample_rate); +void sn76496_set_mutemask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/sn764intf.c b/Frameworks/GME/vgmplay/chips/sn764intf.c new file mode 100644 index 000000000..2177e892c --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/sn764intf.c @@ -0,0 +1,183 @@ +/**************************************************************** + + MAME / MESS functions + +****************************************************************/ + +#include +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#include "sn76496.h" +#include "sn76489.h" +#include "sn764intf.h" + + +#define EC_MAME 0x00 // SN76496 core from MAME +#ifdef ENABLE_ALL_CORES +#define EC_MAXIM 0x01 // SN76489 core by Maxim (from in_vgm) +#endif + +#define NULL ((void *)0) + +/* for stream system */ +typedef struct _sn764xx_state sn764xx_state; +struct _sn764xx_state +{ + void *chip; + int EMU_CORE; +}; + +void sn764xx_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + sn764xx_state* info = (sn764xx_state*)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + SN76496Update(info->chip, outputs, samples); + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + SN76489_Update((SN76489_Context*)info->chip, outputs, samples); + break; +#endif + } +} + +int device_start_sn764xx(void **_info, int EMU_CORE, int clock, int SampleRate, int shiftregwidth, int noisetaps, + int negate, int stereo, int clockdivider, int freq0) +{ + sn764xx_state *info; + int rate; + +#ifdef ENABLE_ALL_CORES + if (EMU_CORE >= 0x02) + EMU_CORE = EC_MAME; +#else + EMU_CORE = EC_MAME; +#endif + + info = (sn764xx_state*) calloc(1, sizeof(sn764xx_state)); + *_info = (void *) info; + /* emulator create */ + info->EMU_CORE = EMU_CORE; + switch(EMU_CORE) + { + case EC_MAME: + rate = sn76496_start(&info->chip, clock, shiftregwidth, noisetaps, + negate, stereo, clockdivider, freq0); + sn76496_freq_limiter(clock & 0x3FFFFFFF, clockdivider, SampleRate); + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + rate = SampleRate; + info->chip = SN76489_Init(clock, rate); + if (info->chip == NULL) + return 0; + SN76489_Config((SN76489_Context*)info->chip, noisetaps, shiftregwidth, 0); + break; +#endif + } + + return rate; +} + +void device_stop_sn764xx(void *_info) +{ + sn764xx_state *info = (sn764xx_state*)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + sn76496_shutdown(info->chip); + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + SN76489_Shutdown((SN76489_Context*)info->chip); + break; +#endif + } +} + +void device_reset_sn764xx(void *_info) +{ + sn764xx_state *info = (sn764xx_state*)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + sn76496_reset(info->chip); + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + SN76489_Reset((SN76489_Context*)info->chip); + break; +#endif + } +} + + +void sn764xx_w(void *_info, offs_t offset, UINT8 data) +{ + sn764xx_state *info = (sn764xx_state*)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + switch(offset) + { + case 0x00: + sn76496_write_reg(info->chip, offset & 1, data); + break; + case 0x01: + sn76496_stereo_w(info->chip, offset, data); + break; + } + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + switch(offset) + { + case 0x00: + SN76489_Write((SN76489_Context*)info->chip, data); + break; + case 0x01: + SN76489_GGStereoWrite((SN76489_Context*)info->chip, data); + break; + } + break; +#endif + } +} + +void sn764xx_set_mute_mask(void *_info, UINT32 MuteMask) +{ + sn764xx_state *info = (sn764xx_state*)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + sn76496_set_mutemask(info->chip, MuteMask); + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + SN76489_SetMute(info->chip, ~MuteMask & 0x0F); + break; +#endif + } + + return; +} + +void sn764xx_set_panning(void *_info, INT16* PanVals) +{ + sn764xx_state *info = (sn764xx_state*)_info; + switch(info->EMU_CORE) + { + case EC_MAME: + break; +#ifdef ENABLE_ALL_CORES + case EC_MAXIM: + SN76489_SetPanning(info->chip, PanVals[0x00], PanVals[0x01], PanVals[0x02], PanVals[0x03]); + break; +#endif + } + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/sn764intf.h b/Frameworks/GME/vgmplay/chips/sn764intf.h new file mode 100644 index 000000000..23fb304c1 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/sn764intf.h @@ -0,0 +1,21 @@ +#pragma once + +/*WRITE8_DEVICE_HANDLER( ym2413_w ); + +WRITE8_DEVICE_HANDLER( ym2413_register_port_w ); +WRITE8_DEVICE_HANDLER( ym2413_data_port_w ); + +DEVICE_GET_INFO( ym2413 ); +#define SOUND_YM2413 DEVICE_GET_INFO_NAME( ym2413 )*/ + +void sn764xx_stream_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_sn764xx(void **chip, int core, int clock, int samplerate, int shiftregwidth, int noisetaps, + int negate, int stereo, int clockdivider, int freq0); +void device_stop_sn764xx(void *chip); +void device_reset_sn764xx(void *chip); + +void sn764xx_w(void *chip, offs_t offset, UINT8 data); + +void sn764xx_set_mute_mask(void *chip, UINT32 MuteMask); +void sn764xx_set_panning(void *chip, INT16* PanVals); diff --git a/Frameworks/GME/vgmplay/chips/upd7759.c b/Frameworks/GME/vgmplay/chips/upd7759.c new file mode 100644 index 000000000..23e32223c --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/upd7759.c @@ -0,0 +1,936 @@ +/************************************************************ + + NEC UPD7759 ADPCM Speech Processor + by: Juergen Buchmueller, Mike Balfour, Howie Cohen, + Olivier Galibert, and Aaron Giles + +************************************************************* + + Description: + + The UPD7759 is a speech processing LSI that utilizes ADPCM to produce + speech or other sampled sounds. It can directly address up to 1Mbit + (128k) of external data ROM, or the host CPU can control the speech + data transfer. The UPD7759 is usually hooked up to a 640 kHz clock and + has one 8-bit input port, a start pin, a busy pin, and a clock output. + + The chip is composed of 3 parts: + - a clock divider + - a rom-reading engine + - an adpcm engine + - a 4-to-9 bit adpcm converter + + The clock divider takes the base 640KHz clock and divides it first + by a fixed divisor of 4 and then by a value between 9 and 32. The + result gives a clock between 5KHz and 17.78KHz. It's probably + possible, but not recommended and certainly out-of-spec, to push the + chip harder by reducing the divider. + + The rom-reading engine reads one byte every two divided clock cycles. + The factor two comes from the fact that a byte has two nibbles, i.e. + two samples. + + The apdcm engine takes bytes and interprets them as commands: + + 00000000 sample end + 00dddddd silence + 01ffffff send the 256 following nibbles to the converter + 10ffffff nnnnnnnn send the n+1 following nibbles to the converter + 11---rrr --ffffff nnnnnnnn send the n+1 following nibbles to the converter, and repeat r+1 times + + "ffffff" is sent to the clock divider to be the base clock for the + adpcm converter, i.e., it's the sampling rate. If the number of + nibbles to send is odd the last nibble is ignored. The commands + are always 8-bit aligned. + + "dddddd" is the duration of the silence. The base speed is unknown, + 1ms sounds reasonably. It does not seem linked to the adpcm clock + speed because there often is a silence before any 01 or 10 command. + + The adpcm converter converts nibbles into 9-bit DAC values. It has + an internal state of 4 bits that's used in conjunction with the + nibble to lookup which of the 256 possible steps is used. Then + the state is changed according to the nibble value. Essentially, the + higher the state, the bigger the steps are, and using big steps + increase the state. Conversely, using small steps reduces the state. + This allows the engine to be a little more adaptative than a + classical ADPCM algorithm. + + The UPD7759 can run in two modes, master (also known as standalone) + and slave. The mode is selected through the "md" pin. No known + game changes modes on the fly, and it's unsure if that's even + possible to do. + + + Master mode: + + The output of the rom reader is directly connected to the adpcm + converter. The controlling cpu only sends a sample number and the + 7759 plays it. + + The sample rom has a header at the beginning of the form + + nn 5a a5 69 55 + + where nn is the number of the last sample. This is then followed by + a vector of 2-bytes msb-first values, one per sample. Multiplying + them by two gives the sample start offset in the rom. A 0x00 marks + the end of each sample. + + It seems that the UPD7759 reads at least part of the rom header at + startup. Games doing rom banking are careful to reset the chip after + each change. + + + Slave mode: + + The rom reader is completely disconnected. The input port is + connected directly to the adpcm engine. The first write to the input + port activates the engine (the value itself is ignored). The engine + activates the clock output and waits for commands. The clock speed + is unknown, but its probably a divider of 640KHz. We use 40KHz here + because 80KHz crashes altbeast. The chip probably has an internal + fifo to the converter and suspends the clock when the fifo is full. + The first command is always 0xFF. A second 0xFF marks the end of the + sample and the engine stops. OTOH, there is a 0x00 at the end too. + Go figure. + +*************************************************************/ + +//#include "emu.h" +//#include "streams.h" +#ifdef _DEBUG +#include +#endif +#include +#include +#include "mamedef.h" +#include "upd7759.h" + +#define NULL ((void *)0) + + +#define DEBUG_STATES (0) +//#define DEBUG_METHOD mame_printf_debug +#define DEBUG_METHOD logerror + + + +/************************************************************ + + Constants + +*************************************************************/ + +/* step value fractional bits */ +#define FRAC_BITS 20 +#define FRAC_ONE (1 << FRAC_BITS) +#define FRAC_MASK (FRAC_ONE - 1) + +/* chip states */ +enum +{ + STATE_IDLE, + STATE_DROP_DRQ, + STATE_START, + STATE_FIRST_REQ, + STATE_LAST_SAMPLE, + STATE_DUMMY1, + STATE_ADDR_MSB, + STATE_ADDR_LSB, + STATE_DUMMY2, + STATE_BLOCK_HEADER, + STATE_NIBBLE_COUNT, + STATE_NIBBLE_MSN, + STATE_NIBBLE_LSN +}; + + + +/************************************************************ + + Type definitions + +*************************************************************/ + +typedef struct _upd7759_state upd7759_state; +struct _upd7759_state +{ + //running_device *device; + //sound_stream *channel; /* stream channel for playback */ + + /* internal clock to output sample rate mapping */ + UINT32 pos; /* current output sample position */ + UINT32 step; /* step value per output sample */ + //attotime clock_period; /* clock period */ + //emu_timer *timer; /* timer */ + + /* I/O lines */ + UINT8 fifo_in; /* last data written to the sound chip */ + UINT8 reset; /* current state of the RESET line */ + UINT8 start; /* current state of the START line */ + UINT8 drq; /* current state of the DRQ line */ + //void (*drqcallback)(running_device *device, int param); /* drq callback */ + //void (*drqcallback)(int param); /* drq callback */ + + /* internal state machine */ + INT8 state; /* current overall chip state */ + INT32 clocks_left; /* number of clocks left in this state */ + UINT16 nibbles_left; /* number of ADPCM nibbles left to process */ + UINT8 repeat_count; /* number of repeats remaining in current repeat block */ + INT8 post_drq_state; /* state we will be in after the DRQ line is dropped */ + INT32 post_drq_clocks; /* clocks that will be left after the DRQ line is dropped */ + UINT8 req_sample; /* requested sample number */ + UINT8 last_sample; /* last sample number available */ + UINT8 block_header; /* header byte */ + UINT8 sample_rate; /* number of UPD clocks per ADPCM nibble */ + UINT8 first_valid_header; /* did we get our first valid header yet? */ + UINT32 offset; /* current ROM offset */ + UINT32 repeat_offset; /* current ROM repeat offset */ + + /* ADPCM processing */ + INT8 adpcm_state; /* ADPCM state index */ + UINT8 adpcm_data; /* current byte of ADPCM data */ + INT16 sample; /* current sample value */ + + /* ROM access */ + UINT32 romsize; + UINT8 * rom; /* pointer to ROM data or NULL for slave mode */ + UINT8 * rombase; /* pointer to ROM data or NULL for slave mode */ + UINT32 romoffset; /* ROM offset to make save/restore easier */ + UINT8 ChipMode; // 0 - Master, 1 - Slave + + // Valley Bell: Added a FIFO buffer based on Sega Pico. + UINT8 data_buf[0x40]; + UINT8 dbuf_pos_read; + UINT8 dbuf_pos_write; +}; + + + +/************************************************************ + + Local variables + +*************************************************************/ + +static const int upd7759_step[16][16] = +{ + { 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10 }, + { 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13 }, + { 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15 }, + { 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19 }, + { 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23 }, + { 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29 }, + { 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33 }, + { 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43 }, + { 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53 }, + { 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64 }, + { 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76 }, + { 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96 }, + { 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117 }, + { 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143 }, + { 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175 }, + { 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 }, +}; + +static const int upd7759_state_table[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3 }; + + + +/*INLINE upd7759_state *get_safe_token(running_device *device) +{ + assert(device != NULL); + assert(device->type() == UPD7759); + return (upd7759_state *)downcast(device)->token(); +}*/ + + +/************************************************************ + + ADPCM sample updater + +*************************************************************/ + +INLINE void update_adpcm(upd7759_state *chip, int data) +{ + /* update the sample and the state */ + chip->sample += upd7759_step[chip->adpcm_state][data]; + chip->adpcm_state += upd7759_state_table[data]; + + /* clamp the state to 0..15 */ + if (chip->adpcm_state < 0) + chip->adpcm_state = 0; + else if (chip->adpcm_state > 15) + chip->adpcm_state = 15; +} + + + +/************************************************************ + + Master chip state machine + +*************************************************************/ + +static void get_fifo_data(upd7759_state *chip) +{ + if (chip->dbuf_pos_read == chip->dbuf_pos_write) + { + logerror("Warning: UPD7759 reading empty FIFO!\n"); + return; + } + + chip->fifo_in = chip->data_buf[chip->dbuf_pos_read]; + chip->dbuf_pos_read ++; + chip->dbuf_pos_read &= 0x3F; + + return; +} + +static void advance_state(upd7759_state *chip) +{ + switch (chip->state) + { + /* Idle state: we stick around here while there's nothing to do */ + case STATE_IDLE: + chip->clocks_left = 4; + break; + + /* drop DRQ state: update to the intended state */ + case STATE_DROP_DRQ: + chip->drq = 0; + + if (chip->ChipMode) + get_fifo_data(chip); // Slave Mode only + chip->clocks_left = chip->post_drq_clocks; + chip->state = chip->post_drq_state; + break; + + /* Start state: we begin here as soon as a sample is triggered */ + case STATE_START: + chip->req_sample = chip->rom ? chip->fifo_in : 0x10; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: req_sample = %02X\n", chip->req_sample); + + /* 35+ cycles after we get here, the /DRQ goes low + * (first byte (number of samples in ROM) should be sent in response) + * + * (35 is the minimum number of cycles I found during heavy tests. + * Depending on the state the chip was in just before the /MD was set to 0 (reset, standby + * or just-finished-playing-previous-sample) this number can range from 35 up to ~24000). + * It also varies slightly from test to test, but not much - a few cycles at most.) */ + chip->clocks_left = 70; /* 35 - breaks cotton */ + chip->state = STATE_FIRST_REQ; + break; + + /* First request state: issue a request for the first byte */ + /* The expected response will be the index of the last sample */ + case STATE_FIRST_REQ: + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: first data request\n"); + chip->drq = 1; + + /* 44 cycles later, we will latch this value and request another byte */ + chip->clocks_left = 44; + chip->state = STATE_LAST_SAMPLE; + break; + + /* Last sample state: latch the last sample value and issue a request for the second byte */ + /* The second byte read will be just a dummy */ + case STATE_LAST_SAMPLE: + chip->last_sample = chip->rom ? chip->rom[0] : chip->fifo_in; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: last_sample = %02X, requesting dummy 1\n", chip->last_sample); + chip->drq = 1; + + /* 28 cycles later, we will latch this value and request another byte */ + chip->clocks_left = 28; /* 28 - breaks cotton */ + chip->state = (chip->req_sample > chip->last_sample) ? STATE_IDLE : STATE_DUMMY1; + break; + + /* First dummy state: ignore any data here and issue a request for the third byte */ + /* The expected response will be the MSB of the sample address */ + case STATE_DUMMY1: + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: dummy1, requesting offset_hi\n"); + chip->drq = 1; + + /* 32 cycles later, we will latch this value and request another byte */ + chip->clocks_left = 32; + chip->state = STATE_ADDR_MSB; + break; + + /* Address MSB state: latch the MSB of the sample address and issue a request for the fourth byte */ + /* The expected response will be the LSB of the sample address */ + case STATE_ADDR_MSB: + chip->offset = (chip->rom ? chip->rom[chip->req_sample * 2 + 5] : chip->fifo_in) << 9; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: offset_hi = %02X, requesting offset_lo\n", chip->offset >> 9); + chip->drq = 1; + + /* 44 cycles later, we will latch this value and request another byte */ + chip->clocks_left = 44; + chip->state = STATE_ADDR_LSB; + break; + + /* Address LSB state: latch the LSB of the sample address and issue a request for the fifth byte */ + /* The expected response will be just a dummy */ + case STATE_ADDR_LSB: + chip->offset |= (chip->rom ? chip->rom[chip->req_sample * 2 + 6] : chip->fifo_in) << 1; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: offset_lo = %02X, requesting dummy 2\n", (chip->offset >> 1) & 0xff); + chip->drq = 1; + + /* 36 cycles later, we will latch this value and request another byte */ + chip->clocks_left = 36; + chip->state = STATE_DUMMY2; + break; + + /* Second dummy state: ignore any data here and issue a request for the the sixth byte */ + /* The expected response will be the first block header */ + case STATE_DUMMY2: + chip->offset++; + chip->first_valid_header = 0; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: dummy2, requesting block header\n"); + chip->drq = 1; + + /* 36?? cycles later, we will latch this value and request another byte */ + chip->clocks_left = 36; + chip->state = STATE_BLOCK_HEADER; + break; + + /* Block header state: latch the header and issue a request for the first byte afterwards */ + case STATE_BLOCK_HEADER: + + /* if we're in a repeat loop, reset the offset to the repeat point and decrement the count */ + if (chip->repeat_count) + { + chip->repeat_count--; + chip->offset = chip->repeat_offset; + } + chip->block_header = chip->rom ? chip->rom[chip->offset++ & 0x1ffff] : chip->fifo_in; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: header (@%05X) = %02X, requesting next byte\n", chip->offset, chip->block_header); + chip->drq = 1; + + /* our next step depends on the top two bits */ + switch (chip->block_header & 0xc0) + { + case 0x00: /* silence */ + chip->clocks_left = 1024 * ((chip->block_header & 0x3f) + 1); + chip->state = (chip->block_header == 0 && chip->first_valid_header) ? STATE_IDLE : STATE_BLOCK_HEADER; + chip->sample = 0; + chip->adpcm_state = 0; + break; + + case 0x40: /* 256 nibbles */ + chip->sample_rate = (chip->block_header & 0x3f) + 1; + chip->nibbles_left = 256; + chip->clocks_left = 36; /* just a guess */ + chip->state = STATE_NIBBLE_MSN; + break; + + case 0x80: /* n nibbles */ + chip->sample_rate = (chip->block_header & 0x3f) + 1; + chip->clocks_left = 36; /* just a guess */ + chip->state = STATE_NIBBLE_COUNT; + break; + + case 0xc0: /* repeat loop */ + chip->repeat_count = (chip->block_header & 7) + 1; + chip->repeat_offset = chip->offset; + chip->clocks_left = 36; /* just a guess */ + chip->state = STATE_BLOCK_HEADER; + break; + } + + /* set a flag when we get the first non-zero header */ + if (chip->block_header != 0) + chip->first_valid_header = 1; + break; + + /* Nibble count state: latch the number of nibbles to play and request another byte */ + /* The expected response will be the first data byte */ + case STATE_NIBBLE_COUNT: + chip->nibbles_left = (chip->rom ? chip->rom[chip->offset++ & 0x1ffff] : chip->fifo_in) + 1; + if (DEBUG_STATES) DEBUG_METHOD("UPD7759: nibble_count = %u, requesting next byte\n", (unsigned)chip->nibbles_left); + chip->drq = 1; + + /* 36?? cycles later, we will latch this value and request another byte */ + chip->clocks_left = 36; /* just a guess */ + chip->state = STATE_NIBBLE_MSN; + break; + + /* MSN state: latch the data for this pair of samples and request another byte */ + /* The expected response will be the next sample data or another header */ + case STATE_NIBBLE_MSN: + chip->adpcm_data = chip->rom ? chip->rom[chip->offset++ & 0x1ffff] : chip->fifo_in; + update_adpcm(chip, chip->adpcm_data >> 4); + chip->drq = 1; + + /* we stay in this state until the time for this sample is complete */ + chip->clocks_left = chip->sample_rate * 4; + if (--chip->nibbles_left == 0) + chip->state = STATE_BLOCK_HEADER; + else + chip->state = STATE_NIBBLE_LSN; + break; + + /* LSN state: process the lower nibble */ + case STATE_NIBBLE_LSN: + update_adpcm(chip, chip->adpcm_data & 15); + + /* we stay in this state until the time for this sample is complete */ + chip->clocks_left = chip->sample_rate * 4; + if (--chip->nibbles_left == 0) + chip->state = STATE_BLOCK_HEADER; + else + chip->state = STATE_NIBBLE_MSN; + break; + } + + /* if there's a DRQ, fudge the state */ + if (chip->drq) + { + chip->post_drq_state = chip->state; + chip->post_drq_clocks = chip->clocks_left - 21; + chip->state = STATE_DROP_DRQ; + chip->clocks_left = 21; + } +} + + + +/************************************************************ + + Stream callback + +*************************************************************/ + +//static STREAM_UPDATE( upd7759_update ) +void upd7759_update(void *param, stream_sample_t **outputs, int samples) +{ + upd7759_state *chip = (upd7759_state *)param; + INT32 clocks_left = chip->clocks_left; + INT16 sample = chip->sample; + UINT32 step = chip->step; + UINT32 pos = chip->pos; + stream_sample_t *buffer = outputs[0]; + stream_sample_t *buffer2 = outputs[1]; + + /* loop until done */ + if (chip->state != STATE_IDLE) + while (samples != 0) + { + /* store the current sample */ + *buffer++ = sample << 7; + *buffer2++ = sample << 7; + samples--; + + /* advance by the number of clocks/output sample */ + pos += step; + + /* handle clocks, but only in standalone mode */ + if (! chip->ChipMode) + { + while (chip->rom && pos >= FRAC_ONE) + { + int clocks_this_time = pos >> FRAC_BITS; + if (clocks_this_time > clocks_left) + clocks_this_time = clocks_left; + + /* clock once */ + pos -= clocks_this_time * FRAC_ONE; + clocks_left -= clocks_this_time; + + /* if we're out of clocks, time to handle the next state */ + if (clocks_left == 0) + { + /* advance one state; if we hit idle, bail */ + advance_state(chip); + if (chip->state == STATE_IDLE) + break; + + /* reimport the variables that we cached */ + clocks_left = chip->clocks_left; + sample = chip->sample; + } + } + } + else + { + UINT8 CntFour; + + if (! clocks_left) + { + advance_state(chip); + clocks_left = chip->clocks_left; + } + + // advance the state (4x because of Clock Divider /4) + for (CntFour = 0; CntFour < 4; CntFour ++) + { + clocks_left --; + if (! clocks_left) + { + advance_state(chip); + clocks_left = chip->clocks_left; + } + } + } + } + + /* if we got out early, just zap the rest of the buffer */ + if (samples != 0) + { + memset(buffer, 0, samples * sizeof(*buffer)); + memset(buffer2, 0, samples * sizeof(*buffer2)); + } + + /* flush the state back */ + chip->clocks_left = clocks_left; + chip->pos = pos; +} + + + +/************************************************************ + + DRQ callback + +*************************************************************/ + +/*static TIMER_CALLBACK( upd7759_slave_update ) +{ + upd7759_state *chip = (upd7759_state *)ptr; + UINT8 olddrq = chip->drq; + + // update the stream + //stream_update(chip->channel); + + // advance the state + advance_state(chip); + + // if the DRQ changed, update it + logerror("slave_update: DRQ %d->%d\n", olddrq, chip->drq); + if (olddrq != chip->drq && chip->drqcallback) + //(*chip->drqcallback)(chip->device, chip->drq); + (*chip->drqcallback)(chip->drq); + + // set a timer to go off when that is done + //if (chip->state != STATE_IDLE) + // timer_adjust_oneshot(chip->timer, attotime_mul(chip->clock_period, chip->clocks_left), 0); +}*/ + + +/************************************************************ + + Sound startup + +*************************************************************/ + +static void upd7759_reset(upd7759_state *chip) +{ + chip->pos = 0; + chip->fifo_in = 0; + chip->drq = 0; + chip->state = STATE_IDLE; + chip->clocks_left = 0; + chip->nibbles_left = 0; + chip->repeat_count = 0; + chip->post_drq_state = STATE_IDLE; + chip->post_drq_clocks = 0; + chip->req_sample = 0; + chip->last_sample = 0; + chip->block_header = 0; + chip->sample_rate = 0; + chip->first_valid_header = 0; + chip->offset = 0; + chip->repeat_offset = 0; + chip->adpcm_state = 0; + chip->adpcm_data = 0; + chip->sample = 0; + + // Valley Bell: reset buffer + chip->data_buf[0] = chip->data_buf[1] = 0x00; + chip->dbuf_pos_read = 0x00; + chip->dbuf_pos_write = 0x00; + + /* turn off any timer */ + //if (chip->timer) + // timer_adjust_oneshot(chip->timer, attotime_never, 0); + if (chip->ChipMode) + chip->clocks_left = -1; +} + + +//static DEVICE_RESET( upd7759 ) +void device_reset_upd7759(void *_info) +{ + upd7759_state *chip = (upd7759_state *)_info; + //upd7759_reset(get_safe_token(device)); + upd7759_reset(chip); +} + + +//static STATE_POSTLOAD( upd7759_postload ) +static void upd7759_postload(void* param) +{ + upd7759_state *chip = (upd7759_state *)param; + chip->rom = chip->rombase + chip->romoffset; +} + + +/*static void register_for_save(upd7759_state *chip, running_device *device) +{ + state_save_register_device_item(device, 0, chip->pos); + state_save_register_device_item(device, 0, chip->step); + + state_save_register_device_item(device, 0, chip->fifo_in); + state_save_register_device_item(device, 0, chip->reset); + state_save_register_device_item(device, 0, chip->start); + state_save_register_device_item(device, 0, chip->drq); + + state_save_register_device_item(device, 0, chip->state); + state_save_register_device_item(device, 0, chip->clocks_left); + state_save_register_device_item(device, 0, chip->nibbles_left); + state_save_register_device_item(device, 0, chip->repeat_count); + state_save_register_device_item(device, 0, chip->post_drq_state); + state_save_register_device_item(device, 0, chip->post_drq_clocks); + state_save_register_device_item(device, 0, chip->req_sample); + state_save_register_device_item(device, 0, chip->last_sample); + state_save_register_device_item(device, 0, chip->block_header); + state_save_register_device_item(device, 0, chip->sample_rate); + state_save_register_device_item(device, 0, chip->first_valid_header); + state_save_register_device_item(device, 0, chip->offset); + state_save_register_device_item(device, 0, chip->repeat_offset); + + state_save_register_device_item(device, 0, chip->adpcm_state); + state_save_register_device_item(device, 0, chip->adpcm_data); + state_save_register_device_item(device, 0, chip->sample); + + state_save_register_device_item(device, 0, chip->romoffset); + state_save_register_postload(device->machine, upd7759_postload, chip); +}*/ + + +//static DEVICE_START( upd7759 ) +int device_start_upd7759(void **_info, int clock) +{ + static const upd7759_interface defintrf = { 0 }; + //const upd7759_interface *intf = (device->baseconfig().static_config() != NULL) ? (const upd7759_interface *)device->baseconfig().static_config() : &defintrf; + const upd7759_interface *intf = &defintrf; + //upd7759_state *chip = get_safe_token(device); + upd7759_state *chip; + + chip = (upd7759_state *) calloc(1, sizeof(upd7759_state)); + *_info = (void *) chip; + + //chip->device = device; + chip->ChipMode = (clock & 0x80000000) >> 31; + clock &= 0x7FFFFFFF; + + /* allocate a stream channel */ + //chip->channel = stream_create(device, 0, 1, device->clock()/4, chip, upd7759_update); + + /* compute the stepping rate based on the chip's clock speed */ + chip->step = 4 * FRAC_ONE; + + /* compute the clock period */ + //chip->clock_period = ATTOTIME_IN_HZ(device->clock()); + + /* set the intial state */ + chip->state = STATE_IDLE; + + /* compute the ROM base or allocate a timer */ + //chip->rom = chip->rombase = *device->region(); + chip->romsize = 0x00; + chip->rom = chip->rombase = NULL; + //if (chip->rom == NULL) + // chip->timer = timer_alloc(device->machine, upd7759_slave_update, chip); + chip->romoffset = 0x00; + + /* set the DRQ callback */ + //chip->drqcallback = intf->drqcallback; + + /* assume /RESET and /START are both high */ + chip->reset = 1; + chip->start = 1; + + /* toggle the reset line to finish the reset */ + upd7759_reset(chip); + + //register_for_save(chip, device); + + return clock / 4; +} + +void device_stop_upd7759(void *_info) +{ + upd7759_state *chip = (upd7759_state *)_info; + + free(chip->rombase); chip->rombase = NULL; + + free(chip); + + return; +} + + + +/************************************************************ + + I/O handlers + +*************************************************************/ + +//void upd7759_reset_w(running_device *device, UINT8 data) +void upd7759_reset_w(void *_info, UINT8 data) +{ + /* update the reset value */ + //upd7759_state *chip = get_safe_token(device); + upd7759_state *chip = (upd7759_state *)_info; + UINT8 oldreset = chip->reset; + chip->reset = (data != 0); + + /* update the stream first */ + //stream_update(chip->channel); + + /* on the falling edge, reset everything */ + if (oldreset && !chip->reset) + upd7759_reset(chip); +} + +//void upd7759_start_w(running_device *device, UINT8 data) +void upd7759_start_w(void *_info, UINT8 data) +{ + /* update the start value */ + //upd7759_state *chip = get_safe_token(device); + upd7759_state *chip = (upd7759_state *)_info; + UINT8 oldstart = chip->start; + chip->start = (data != 0); + + if (DEBUG_STATES) logerror("upd7759_start_w: %d->%d\n", oldstart, chip->start); + + /* update the stream first */ + //stream_update(chip->channel); + + /* on the rising edge, if we're idle, start going, but not if we're held in reset */ + if (chip->state == STATE_IDLE && !oldstart && chip->start && chip->reset) + { + chip->state = STATE_START; + + /* for slave mode, start the timer going */ + //if (chip->timer) + // timer_adjust_oneshot(chip->timer, attotime_zero, 0); + chip->clocks_left = 0; + } +} + + +//WRITE8_DEVICE_HANDLER( upd7759_port_w ) +void upd7759_port_w(void *_info, offs_t offset, UINT8 data) +{ + /* update the FIFO value */ + //upd7759_state *chip = get_safe_token(device); + upd7759_state *chip = (upd7759_state *)_info; + + if (! chip->ChipMode) + { + chip->fifo_in = data; + } + else + { + // Valley Bell: added FIFO buffer for Slave mode + chip->data_buf[chip->dbuf_pos_write] = data; + chip->dbuf_pos_write ++; + chip->dbuf_pos_write &= 0x3F; + } +} + + +//int upd7759_busy_r(running_device *device) +int upd7759_busy_r(void *_info) +{ + /* return /BUSY */ + //upd7759_state *chip = get_safe_token(device); + upd7759_state *chip = (upd7759_state *)_info; + return (chip->state == STATE_IDLE); +} + + +//void upd7759_set_bank_base(running_device *device, UINT32 base) +void upd7759_set_bank_base(void *_info, UINT32 base) +{ + //upd7759_state *chip = get_safe_token(device); + upd7759_state *chip = (upd7759_state *)_info; + chip->rom = chip->rombase + base; + chip->romoffset = base; +} + +void upd7759_write(void *info, UINT8 Port, UINT8 Data) +{ + switch(Port) + { + case 0x00: + upd7759_reset_w(info, Data); + break; + case 0x01: + upd7759_start_w(info, Data); + break; + case 0x02: + upd7759_port_w(info, 0x00, Data); + break; + case 0x03: + upd7759_set_bank_base(info, Data * 0x20000); + break; + } + + return; +} + +void upd7759_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + upd7759_state *chip = (upd7759_state *)_info; + + if (chip->romsize != ROMSize) + { + chip->rombase = (UINT8*)realloc(chip->rombase, ROMSize); + chip->romsize = ROMSize; + memset(chip->rombase, 0xFF, ROMSize); + + chip->rom = chip->rombase + chip->romoffset; + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(chip->rombase + DataStart, ROMData, DataLength); + + return; +} + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( upd7759 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(upd7759_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( upd7759 ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( upd7759 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "UPD7759"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "NEC ADPCM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + + +//DEFINE_LEGACY_SOUND_DEVICE(UPD7759, upd7759); diff --git a/Frameworks/GME/vgmplay/chips/upd7759.h b/Frameworks/GME/vgmplay/chips/upd7759.h new file mode 100644 index 000000000..e2604a4db --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/upd7759.h @@ -0,0 +1,42 @@ +#pragma once + +//#include "devlegcy.h" + +/* There are two modes for the uPD7759, selected through the !MD pin. + This is the mode select input. High is stand alone, low is slave. + We're making the assumption that nobody switches modes through + software. */ + +#define UPD7759_STANDARD_CLOCK 640000 + +typedef struct _upd7759_interface upd7759_interface; +struct _upd7759_interface +{ + //void (*drqcallback)(running_device *device, int param); /* drq callback (per chip, slave mode only) */ + void (*drqcallback)(int param); /* drq callback (per chip, slave mode only) */ +}; + +void upd7759_update(void *chip, stream_sample_t **outputs, int samples); +void device_reset_upd7759(void *chip); +int device_start_upd7759(void **chip, int clock); +void device_stop_upd7759(void *chip); + +//void upd7759_set_bank_base(running_device *device, offs_t base); + +//void upd7759_reset_w(running_device *device, UINT8 data); +//void upd7759_start_w(running_device *device, UINT8 data); +//int upd7759_busy_r(running_device *device); +//WRITE8_DEVICE_HANDLER( upd7759_port_w ); + +void upd7759_set_bank_base(void *chip, offs_t base); + +void upd7759_reset_w(void *chip, UINT8 data); +void upd7759_start_w(void *chip, UINT8 data); +int upd7759_busy_r(void *chip); +void upd7759_port_w(void *chip, offs_t offset, UINT8 data); + +void upd7759_write(void *chip, UINT8 Port, UINT8 Data); +void upd7759_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +//DECLARE_LEGACY_SOUND_DEVICE(UPD7759, upd7759); diff --git a/Frameworks/GME/vgmplay/chips/vrc7tone.h b/Frameworks/GME/vgmplay/chips/vrc7tone.h new file mode 100644 index 000000000..ac53d1795 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/vrc7tone.h @@ -0,0 +1,20 @@ +/* VRC7 VOICE */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01, +0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13, +0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, +0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, +0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, +0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, +0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17, +0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47, +0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, +0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, +0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, +0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, +0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, +0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07, +0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, +0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8, +0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8, +0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55, diff --git a/Frameworks/GME/vgmplay/chips/vsu.c b/Frameworks/GME/vgmplay/chips/vsu.c new file mode 100644 index 000000000..87fab949a --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/vsu.c @@ -0,0 +1,636 @@ +/* Mednafen - Multi-system Emulator + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//#include "vb.h" +#include +#include +#include "mamedef.h" +#include "vsu.h" + +typedef struct +{ + UINT8 IntlControl[6]; + UINT8 LeftLevel[6]; + UINT8 RightLevel[6]; + UINT16 Frequency[6]; + UINT16 EnvControl[6]; // Channel 5/6 extra functionality tacked on too. + + UINT8 RAMAddress[6]; + + UINT8 SweepControl; + + UINT8 WaveData[5][0x20]; + + UINT8 ModData[0x20]; + + // + // + // + INT32 EffFreq[6]; + INT32 Envelope[6]; + + INT32 WavePos[6]; + INT32 ModWavePos; + + INT32 LatcherClockDivider[6]; + + INT32 FreqCounter[6]; + INT32 IntervalCounter[6]; + INT32 EnvelopeCounter[6]; + INT32 SweepModCounter; + + INT32 EffectsClockDivider[6]; + INT32 IntervalClockDivider[6]; + INT32 EnvelopeClockDivider[6]; + INT32 SweepModClockDivider; + + INT32 NoiseLatcherClockDivider; + UINT32 NoiseLatcher; + + UINT32 lfsr; + + //INT32 last_output[6][2]; + INT32 last_ts; + + //Blip_Buffer *sbuf[2]; + //Blip_Synth Synth; + int clock; + int smplrate; + UINT8 Muted[6]; + // values for Timing Calculation + int tm_smpl; + int tm_clk; +} vsu_state; + +static void VSU_Power(vsu_state* chip); +//void VSU_Write(UINT8 ChipID, UINT32 A, UINT8 V); + +INLINE void VSU_CalcCurrentOutput(vsu_state* chip, int ch, int* left, int* right); +static void VSU_Update(vsu_state* chip, INT32 timestamp, int* outleft, int* outright); + + +static const int Tap_LUT[8] = { 15 - 1, 11 - 1, 14 - 1, 5 - 1, 9 - 1, 7 - 1, 10 - 1, 12 - 1 }; + +static void VSU_Power(vsu_state* chip) +{ + int ch; + + chip->SweepControl = 0; + chip->SweepModCounter = 0; + chip->SweepModClockDivider = 1; + + for(ch = 0; ch < 6; ch++) + { + chip->IntlControl[ch] = 0; + chip->LeftLevel[ch] = 0; + chip->RightLevel[ch] = 0; + chip->Frequency[ch] = 0; + chip->EnvControl[ch] = 0; + chip->RAMAddress[ch] = 0; + + chip->EffFreq[ch] = 0; + chip->Envelope[ch] = 0; + chip->WavePos[ch] = 0; + chip->FreqCounter[ch] = 0; + chip->IntervalCounter[ch] = 0; + chip->EnvelopeCounter[ch] = 0; + + chip->EffectsClockDivider[ch] = 4800; + chip->IntervalClockDivider[ch] = 4; + chip->EnvelopeClockDivider[ch] = 4; + + chip->LatcherClockDivider[ch] = 120; + } + + + chip->NoiseLatcherClockDivider = 120; + chip->NoiseLatcher = 0; + + memset(chip->WaveData, 0, sizeof(chip->WaveData)); + memset(chip->ModData, 0, sizeof(chip->ModData)); + + chip->last_ts = 0; +} + +void VSU_Write(void *_info, UINT32 A, UINT8 V) +{ + vsu_state* chip = (vsu_state *)_info; + + A <<= 2; + A &= 0x7FF; + + //Update(timestamp); + + //printf("VSU Write: %d, %08x %02x\n", timestamp, A, V); + + if(A < 0x280) + chip->WaveData[A >> 7][(A >> 2) & 0x1F] = V & 0x3F; + else if(A < 0x400) + { + //if(A >= 0x300) + // printf("Modulation mirror write? %08x %02x\n", A, V); + chip->ModData[(A >> 2) & 0x1F] = V; + } + else if(A < 0x600) + { + int ch = (A >> 6) & 0xF; + + //if(ch < 6) + //printf("Ch: %d, Reg: %d, Value: %02x\n", ch, (A >> 2) & 0xF, V); + + if(ch > 5) + { + if(A == 0x580 && (V & 1)) + { + int i; + //puts("STOP, HAMMER TIME"); + for(i = 0; i < 6; i++) + chip->IntlControl[i] &= ~0x80; + } + } + else + switch((A >> 2) & 0xF) + { + case 0x0: + chip->IntlControl[ch] = V & ~0x40; + + if(V & 0x80) + { + chip->EffFreq[ch] = chip->Frequency[ch]; + if(ch == 5) + chip->FreqCounter[ch] = 10 * (2048 - chip->EffFreq[ch]); + else + chip->FreqCounter[ch] = 2048 - chip->EffFreq[ch]; + chip->IntervalCounter[ch] = (V & 0x1F) + 1; + chip->EnvelopeCounter[ch] = (chip->EnvControl[ch] & 0x7) + 1; + + if(ch == 4) + { + chip->SweepModCounter = (chip->SweepControl >> 4) & 7; + chip->SweepModClockDivider = (chip->SweepControl & 0x80) ? 8 : 1; + chip->ModWavePos = 0; + } + + chip->WavePos[ch] = 0; + + if(ch == 5) + chip->lfsr = 1; + + //if(!(chip->IntlControl[ch] & 0x80)) + // chip->Envelope[ch] = (chip->EnvControl[ch] >> 4) & 0xF; + + chip->EffectsClockDivider[ch] = 4800; + chip->IntervalClockDivider[ch] = 4; + chip->EnvelopeClockDivider[ch] = 4; + } + break; + + case 0x1: + chip->LeftLevel[ch] = (V >> 4) & 0xF; + chip->RightLevel[ch] = (V >> 0) & 0xF; + break; + + case 0x2: + chip->Frequency[ch] &= 0xFF00; + chip->Frequency[ch] |= V << 0; + chip->EffFreq[ch] &= 0xFF00; + chip->EffFreq[ch] |= V << 0; + break; + + case 0x3: + chip->Frequency[ch] &= 0x00FF; + chip->Frequency[ch] |= (V & 0x7) << 8; + chip->EffFreq[ch] &= 0x00FF; + chip->EffFreq[ch] |= (V & 0x7) << 8; + break; + + case 0x4: + chip->EnvControl[ch] &= 0xFF00; + chip->EnvControl[ch] |= V << 0; + + chip->Envelope[ch] = (V >> 4) & 0xF; + break; + + case 0x5: + chip->EnvControl[ch] &= 0x00FF; + if(ch == 4) + chip->EnvControl[ch] |= (V & 0x73) << 8; + else if(ch == 5) + chip->EnvControl[ch] |= (V & 0x73) << 8; + else + chip->EnvControl[ch] |= (V & 0x03) << 8; + break; + + case 0x6: + chip->RAMAddress[ch] = V & 0xF; + break; + + case 0x7: + if(ch == 4) + { + chip->SweepControl = V; + } + break; + } + } +} + +INLINE void VSU_CalcCurrentOutput(vsu_state* chip, int ch, int* left, int* right) +{ + int WD; + int l_ol, r_ol; + + if(!(chip->IntlControl[ch] & 0x80) || chip->Muted[ch]) + { + *left = *right = 0; + return; + } + + if(ch == 5) + WD = chip->NoiseLatcher; //(NoiseLatcher << 6) - NoiseLatcher; + else + { + if(chip->RAMAddress[ch] > 4) + WD = 0x20; //0; + else + WD = chip->WaveData[chip->RAMAddress[ch]][chip->WavePos[ch]]; // - 0x20; + } + l_ol = chip->Envelope[ch] * chip->LeftLevel[ch]; + if(l_ol) + { + l_ol >>= 3; + l_ol += 1; + } + + r_ol = chip->Envelope[ch] * chip->RightLevel[ch]; + if(r_ol) + { + r_ol >>= 3; + r_ol += 1; + } + + WD -= 0x20; + (*left) += WD * l_ol; + (*right) += WD * r_ol; + return; +} + +static void VSU_Update(vsu_state* chip, INT32 timestamp, int* outleft, int* outright) +{ + //int left, right; + int ch; + + *outleft = 0; + *outright = 0; + + //puts("VSU Start"); + for(ch = 0; ch < 6; ch++) + { + INT32 clocks = timestamp - chip->last_ts; + //INT32 running_timestamp = chip->last_ts; + + if(!(chip->IntlControl[ch] & 0x80) || chip->Muted[ch]) + continue; // channel disabled - don't add anything to output + + //// Output sound here + //VSU_CalcCurrentOutput(chip, ch, &left, &right); + //Synth.offset_inline(running_timestamp, left - chip->last_output[ch][0], sbuf[0]); + //Synth.offset_inline(running_timestamp, right - chip->last_output[ch][1], sbuf[1]); + //chip->last_output[ch][0] = left; + //chip->last_output[ch][1] = right; + + //if(!(chip->IntlControl[ch] & 0x80)) + // continue; + + while(clocks > 0) + { + INT32 chunk_clocks = clocks; + + if(chunk_clocks > chip->EffectsClockDivider[ch]) + chunk_clocks = chip->EffectsClockDivider[ch]; + + if(ch == 5) + { + if(chunk_clocks > chip->NoiseLatcherClockDivider) + chunk_clocks = chip->NoiseLatcherClockDivider; + } + else + { + if(chip->EffFreq[ch] >= 2040) + { + if(chunk_clocks > chip->LatcherClockDivider[ch]) + chunk_clocks = chip->LatcherClockDivider[ch]; + } + else + { + if(chunk_clocks > chip->FreqCounter[ch]) + chunk_clocks = chip->FreqCounter[ch]; + } + } + + if(ch == 5 && chunk_clocks > chip->NoiseLatcherClockDivider) + chunk_clocks = chip->NoiseLatcherClockDivider; + + chip->FreqCounter[ch] -= chunk_clocks; + while(chip->FreqCounter[ch] <= 0) + { + if(ch == 5) + { + int feedback = ((chip->lfsr >> 7) & 1) ^ ((chip->lfsr >> Tap_LUT[(chip->EnvControl[5] >> 12) & 0x7]) & 1); + chip->lfsr = ((chip->lfsr << 1) & 0x7FFF) | feedback; + + chip->FreqCounter[ch] += 10 * (2048 - chip->EffFreq[ch]); + } + else + { + chip->FreqCounter[ch] += 2048 - chip->EffFreq[ch]; + chip->WavePos[ch] = (chip->WavePos[ch] + 1) & 0x1F; + } + } + + chip->LatcherClockDivider[ch] -= chunk_clocks; + while(chip->LatcherClockDivider[ch] <= 0) + chip->LatcherClockDivider[ch] += 120; + + if(ch == 5) + { + chip->NoiseLatcherClockDivider -= chunk_clocks; + if(!chip->NoiseLatcherClockDivider) + { + chip->NoiseLatcherClockDivider = 120; + chip->NoiseLatcher = ((chip->lfsr & 1) << 6) - (chip->lfsr & 1); + } + } + + chip->EffectsClockDivider[ch] -= chunk_clocks; + while(chip->EffectsClockDivider[ch] <= 0) + { + chip->EffectsClockDivider[ch] += 4800; + + chip->IntervalClockDivider[ch]--; + while(chip->IntervalClockDivider[ch] <= 0) + { + chip->IntervalClockDivider[ch] += 4; + + if(chip->IntlControl[ch] & 0x20) + { + chip->IntervalCounter[ch]--; + if(!chip->IntervalCounter[ch]) + { + chip->IntlControl[ch] &= ~0x80; + } + } + + chip->EnvelopeClockDivider[ch]--; + while(chip->EnvelopeClockDivider[ch] <= 0) + { + chip->EnvelopeClockDivider[ch] += 4; + + if(chip->EnvControl[ch] & 0x0100) // Enveloping enabled? + { + chip->EnvelopeCounter[ch]--; + if(!chip->EnvelopeCounter[ch]) + { + chip->EnvelopeCounter[ch] = (chip->EnvControl[ch] & 0x7) + 1; + + if(chip->EnvControl[ch] & 0x0008) // Grow + { + if(chip->Envelope[ch] < 0xF || (chip->EnvControl[ch] & 0x200)) + chip->Envelope[ch] = (chip->Envelope[ch] + 1) & 0xF; + } + else // Decay + { + if(chip->Envelope[ch] > 0 || (chip->EnvControl[ch] & 0x200)) + chip->Envelope[ch] = (chip->Envelope[ch] - 1) & 0xF; + } + } + } + + } // end while(chip->EnvelopeClockDivider[ch] <= 0) + } // end while(chip->IntervalClockDivider[ch] <= 0) + + if(ch == 4) + { + chip->SweepModClockDivider--; + while(chip->SweepModClockDivider <= 0) + { + chip->SweepModClockDivider += (chip->SweepControl & 0x80) ? 8 : 1; + + if(((chip->SweepControl >> 4) & 0x7) && (chip->EnvControl[ch] & 0x4000)) + { + if(chip->SweepModCounter) + chip->SweepModCounter--; + + if(!chip->SweepModCounter) + { + chip->SweepModCounter = (chip->SweepControl >> 4) & 0x7; + + if(chip->EnvControl[ch] & 0x1000) // Modulation + { + if(chip->ModWavePos < 32 || (chip->EnvControl[ch] & 0x2000)) + { + chip->ModWavePos &= 0x1F; + + chip->EffFreq[ch] = (chip->EffFreq[ch] + (INT8)chip->ModData[chip->ModWavePos]); + if(chip->EffFreq[ch] < 0) + { + //puts("Underflow"); + chip->EffFreq[ch] = 0; + } + else if(chip->EffFreq[ch] > 0x7FF) + { + //puts("Overflow"); + chip->EffFreq[ch] = 0x7FF; + } + chip->ModWavePos++; + } + //puts("Mod"); + } + else // Sweep + { + INT32 delta = chip->EffFreq[ch] >> (chip->SweepControl & 0x7); + INT32 NewFreq = chip->EffFreq[ch] + ((chip->SweepControl & 0x8) ? delta : -delta); + + //printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq); + + if(NewFreq < 0) + chip->EffFreq[ch] = 0; + else if(NewFreq > 0x7FF) + { + //chip->EffFreq[ch] = 0x7FF; + chip->IntlControl[ch] &= ~0x80; + } + else + chip->EffFreq[ch] = NewFreq; + } + } + } + } // end while(chip->SweepModClockDivider <= 0) + } // end if(ch == 4) + } // end while(chip->EffectsClockDivider[ch] <= 0) + clocks -= chunk_clocks; + //running_timestamp += chunk_clocks; + + // Output sound here too. + //VSU_CalcCurrentOutput(chip, ch, &left, &right); + //Synth.offset_inline(running_timestamp, left - chip->last_output[ch][0], sbuf[0]); + //Synth.offset_inline(running_timestamp, right - chip->last_output[ch][1], sbuf[1]); + //chip->last_output[ch][0] = left; + //chip->last_output[ch][1] = right; + } + + VSU_CalcCurrentOutput(chip, ch, outleft, outright); + } + chip->last_ts = timestamp; + //puts("VSU End"); +} + +/*void VSU_EndFrame(INT32 timestamp) +{ + Update(chip, timestamp); + chip->last_ts = 0; +}*/ + +/*int VSU_StateAction(StateMem *sm, int load, int data_only) +{ + SFORMAT StateRegs[] = + { + SFARRAY(IntlControl, 6), + SFARRAY(LeftLevel, 6), + SFARRAY(RightLevel, 6), + + SFARRAY16(Frequency, 6), + SFARRAY16(EnvControl, 6), + SFARRAY(RAMAddress, 6), + SFVAR(SweepControl), + + SFARRAY(&WaveData[0][0], 5 * 0x20), + SFARRAY(ModData, 0x20), + + SFARRAY32(EffFreq, 6), + SFARRAY32(Envelope, 6), + + SFARRAY32(WavePos, 6), + + SFVAR(ModWavePos), + + SFARRAY32(LatcherClockDivider, 6), + SFARRAY32(FreqCounter, 6), + SFARRAY32(IntervalCounter, 6), + SFARRAY32(EnvelopeCounter, 6), + + SFVAR(SweepModCounter), + + SFARRAY32(EffectsClockDivider, 6), + SFARRAY32(IntervalClockDivider, 6), + SFARRAY32(EnvelopeClockDivider, 6), + + SFVAR(SweepModClockDivider), + + SFVAR(NoiseLatcherClockDivider), + SFVAR(NoiseLatcher), + SFVAR(lfsr), + SFEND + }; + + int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "VSU"); + + + if(load) + { + + } + + return(ret); +}*/ + + +void vsu_stream_update(void *_info, stream_sample_t **outputs, int samples) +{ + vsu_state* chip = (vsu_state *)_info; + int curSmpl; + + for (curSmpl = 0; curSmpl < samples; curSmpl ++) + { + chip->tm_smpl ++; + chip->tm_clk = (int)((INT64)chip->tm_smpl * chip->clock / chip->smplrate); + + VSU_Update(chip, chip->tm_clk, &outputs[0][curSmpl], &outputs[1][curSmpl]); + if (chip->last_ts >= chip->clock) + { + chip->last_ts -= chip->clock; + chip->tm_clk -= chip->clock; + chip->tm_smpl -= chip->smplrate; + } + + // Volume per channel: 0x1F (envelope/volume) * 0x3F (unsigned sample) = 0x7A1 (~0x800) + // I turned the samples into signed format (-0x20..0x1F), so the used range is +-0x400. + // 16-bit values are up to 0x8000 + // 0x8000 / 0x400 / 6 = 5.33 (possible boost without clipping) + // Because music usually doesn't use the maximum volume (SFX do), I boost by 2^3 = 8. + outputs[0][curSmpl] <<= 3; + outputs[1][curSmpl] <<= 3; + } + return; +} + +int device_start_vsu(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + vsu_state* chip; + UINT8 CurChn; + + chip = (vsu_state *) calloc(1, sizeof(vsu_state)); + *_info = (void *) chip; + + chip->clock = clock; + chip->smplrate = chip->clock / 120; // most effects run with a /120 divider + if (((CHIP_SAMPLING_MODE & 0x01) && chip->smplrate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + chip->smplrate = CHIP_SAMPLE_RATE; + + for (CurChn = 0; CurChn < 6; CurChn ++) + chip->Muted[CurChn] = 0x00; + + return chip->smplrate; +} + +void device_stop_vsu(void *chip) +{ + free(chip); +} + +void device_reset_vsu(void *_info) +{ + vsu_state* chip = (vsu_state *)_info; + + VSU_Power(chip); + chip->tm_smpl = 0; + chip->tm_clk = 0; + + return; +} + +void vsu_set_mute_mask(void *_info, UINT32 MuteMask) +{ + vsu_state* chip = (vsu_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + chip->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/vsu.h b/Frameworks/GME/vgmplay/chips/vsu.h new file mode 100644 index 000000000..fc1448407 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/vsu.h @@ -0,0 +1,15 @@ +#ifndef __VB_VSU_H +#define __VB_VSU_H + +void VSU_Write(void *chip, UINT32 A, UINT8 V); + +void vsu_stream_update(void *chip, stream_sample_t **outputs, int samples); + +int device_start_vsu(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_vsu(void *chip); +void device_reset_vsu(void *chip); + +//void vsu_set_options(UINT16 Options); +void vsu_set_mute_mask(void *chip, UINT32 MuteMask); + +#endif // __VB_VSU_H diff --git a/Frameworks/GME/vgmplay/chips/ws_audio.c b/Frameworks/GME/vgmplay/chips/ws_audio.c new file mode 100644 index 000000000..28e9b0f0d --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ws_audio.c @@ -0,0 +1,474 @@ + +#include +#include +#include // for NULL +#include "mamedef.h" + +typedef UINT8 BYTE; +typedef UINT8 byte; +typedef UINT8 uint8; + +//#include +//#include "types.h" +//#include "./nec/necintrf.h" +//#include "ws_memory.h" +#include "ws_initialIo.h" +//#include "ws_io.h" +#include "ws_audio.h" +//#include "wsr_player.h" + +#define SNDP chip->ws_ioRam[0x80] +#define SNDV chip->ws_ioRam[0x88] +#define SNDSWP chip->ws_ioRam[0x8C] +#define SWPSTP chip->ws_ioRam[0x8D] +#define NSCTL chip->ws_ioRam[0x8E] +#define WAVDTP chip->ws_ioRam[0x8F] +#define SNDMOD chip->ws_ioRam[0x90] +#define SNDOUT chip->ws_ioRam[0x91] +#define PCSRL chip->ws_ioRam[0x92] +#define PCSRH chip->ws_ioRam[0x93] +#define DMASL chip->ws_ioRam[0x40] +#define DMASH chip->ws_ioRam[0x41] +#define DMASB chip->ws_ioRam[0x42] +#define DMADB chip->ws_ioRam[0x43] +#define DMADL chip->ws_ioRam[0x44] +#define DMADH chip->ws_ioRam[0x45] +#define DMACL chip->ws_ioRam[0x46] +#define DMACH chip->ws_ioRam[0x47] +#define DMACTL chip->ws_ioRam[0x48] +#define SDMASL chip->ws_ioRam[0x4A] +#define SDMASH chip->ws_ioRam[0x4B] +#define SDMASB chip->ws_ioRam[0x4C] +#define SDMACL chip->ws_ioRam[0x4E] +#define SDMACH chip->ws_ioRam[0x4F] +#define SDMACTL chip->ws_ioRam[0x52] + +//SoundDMA ã®è»¢é€é–“éš” +// å®Ÿéš›ã®æ•°å€¤ãŒåˆ†ã‹ã‚‰ãªã„ã®ã§ã€äºˆæƒ³ã§ã™ +// サンプリング周期ã‹ã‚‰è€ƒãˆã¦ã¿ã¦ä»¥ä¸‹ã®ã‚ˆã†ã«ã—㟠+// 12KHz = 1.00HBlank = 256cyclesé–“éš” +// 16KHz = 0.75HBlank = 192cyclesé–“éš” +// 20KHz = 0.60HBlank = 154cyclesé–“éš” +// 24KHz = 0.50HBlank = 128cyclesé–“éš” +const int DMACycles[4] = { 256, 192, 154, 128 }; + +typedef struct +{ + int wave; + int lvol; + int rvol; + long offset; + long delta; + long pos; + UINT8 Muted; +} WS_AUDIO; + +typedef struct +{ + WS_AUDIO ws_audio[4]; + int sweepDelta; + int sweepOffset; + int SweepTime; + int SweepStep; + int SweepCount; + int SweepFreq; + int NoiseType; + int NoiseRng; + int MainVolume; + int PCMVolumeLeft; + int PCMVolumeRight; + //int WaveAdrs; + + UINT8 ws_ioRam[0x100]; + UINT8 *ws_internalRam; + + int clock; + int smplrate; +} wsa_state; + +#define DEFAULT_CLOCK 3072000 + + +static void ws_audio_process(wsa_state* chip); + + + +int ws_audio_init(void **_info, int clock, int SampleRate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + wsa_state* chip; + UINT8 CurChn; + + chip = (wsa_state *) calloc(1, sizeof(wsa_state)); + *_info = (void *) chip; + + chip->ws_internalRam = (UINT8*)malloc(0x4000); // actual size is 64 KB, but the audio chip can only access 16 KB + + chip->clock = clock; + chip->smplrate = SampleRate; + if (((CHIP_SAMPLING_MODE & 0x01) && chip->smplrate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + chip->smplrate = CHIP_SAMPLE_RATE; + + for (CurChn = 0; CurChn < 4; CurChn ++) + chip->ws_audio[CurChn].Muted = 0x00; + + return chip->smplrate; +} + +void ws_audio_reset(void *_info) +{ + wsa_state* chip = (wsa_state *)_info; + int i; + + memset(&chip->ws_audio, 0, sizeof(WS_AUDIO)); + chip->SweepTime = 0; + chip->SweepStep = 0; + chip->NoiseType = 0; + chip->NoiseRng = 1; + chip->MainVolume = 0x02; // 0x04 + chip->PCMVolumeLeft = 0; + chip->PCMVolumeRight = 0; + + chip->sweepDelta = chip->clock * 256 / chip->smplrate; + chip->sweepOffset = 0; + + for (i=0x80;i<0xc9;i++) + ws_audio_port_write(chip, i, initialIoValue[i]); +} + +void ws_audio_done(void *_info) +{ + wsa_state* chip = (wsa_state *)_info; + + free(chip->ws_internalRam); + chip->ws_internalRam = NULL; + + free(chip); + + return; +} + +void ws_audio_update(void *_info, stream_sample_t** buffer, int length) +{ + wsa_state* chip = (wsa_state *)_info; + stream_sample_t* bufL; + stream_sample_t* bufR; + int i, ch, cnt; + long w, l, r; + + bufL = buffer[0]; + bufR = buffer[1]; + for (i=0; isweepOffset += chip->sweepDelta; + while(chip->sweepOffset >= 0x10000) + { + chip->sweepOffset -= 0x10000; + ws_audio_process(chip); + } + l = r = 0; + + for (ch=0; ch<4; ch++) + { + if (chip->ws_audio[ch].Muted) + continue; + + if ((ch==1) && (SNDMOD&0x20)) + { + // Voice出力 + w = chip->ws_ioRam[0x89]; + w -= 0x80; + l += chip->PCMVolumeLeft * w; + r += chip->PCMVolumeRight * w; + } + else if (SNDMOD&(1<ws_audio[ch].offset += chip->ws_audio[ch].delta; + cnt = chip->ws_audio[ch].offset>>16; + chip->ws_audio[ch].offset &= 0xffff; + while (cnt > 0) + { + cnt--; + + chip->NoiseRng &= noise_bit[chip->NoiseType]-1; + if (!chip->NoiseRng) chip->NoiseRng = noise_bit[chip->NoiseType]-1; + + Masked = chip->NoiseRng & noise_mask[chip->NoiseType]; + XorReg = 0; + while (Masked) + { + XorReg ^= Masked&1; + Masked >>= 1; + } + if (XorReg) + chip->NoiseRng |= noise_bit[chip->NoiseType]; + chip->NoiseRng >>= 1; + } + + PCSRL = (byte)(chip->NoiseRng&0xff); + PCSRH = (byte)((chip->NoiseRng>>8)&0x7f); + + w = (chip->NoiseRng&1)? 0x7f:-0x80; + l += chip->ws_audio[ch].lvol * w; + r += chip->ws_audio[ch].rvol * w; + } + else + { + chip->ws_audio[ch].offset += chip->ws_audio[ch].delta; + cnt = chip->ws_audio[ch].offset>>16; + chip->ws_audio[ch].offset &= 0xffff; + chip->ws_audio[ch].pos += cnt; + chip->ws_audio[ch].pos &= 0x1f; + w = chip->ws_internalRam[(chip->ws_audio[ch].wave&0xFFF0) + (chip->ws_audio[ch].pos>>1)]; + if ((chip->ws_audio[ch].pos&1) == 0) + w = (w<<4)&0xf0; //下ä½ãƒ‹ãƒ–ル + else + w = w&0xf0; //上ä½ãƒ‹ãƒ–ル + w -= 0x80; + l += chip->ws_audio[ch].lvol * w; + r += chip->ws_audio[ch].rvol * w; + } + } + } + + /*l = l * chip->MainVolume; + if (l > 0x7fff) l = 0x7fff; + else if (l < -0x8000) l = -0x8000; + r = r * chip->MainVolume; + if (r > 0x7fff) r = 0x7fff; + else if (r < -0x8000) r = -0x8000; + + *buffer++ = (short)l; + *buffer++ = (short)r;*/ + bufL[i] = l * chip->MainVolume; + bufR[i] = r * chip->MainVolume; + } +} + +void ws_audio_port_write(void *_info, BYTE port, BYTE value) +{ + wsa_state* chip = (wsa_state *)_info; + int i; + long freq; + + //Update_SampleData(); + + chip->ws_ioRam[port]=value; + + switch (port) + { + // 0x80-0x87ã®å‘¨æ³¢æ•°ãƒ¬ã‚¸ã‚¹ã‚¿ã«ã¤ã„㦠+ // - ロックマン&フォルテã®0x0fã®æ›²ã§ã¯ã€å‘¨æ³¢æ•°=0xFFFF ã®éŸ³ãŒä¸è¦ + // - デジモンディープロジェクトã®0x0dã®æ›²ã®ãƒŽã‚¤ã‚ºã¯ 周波数=0x07FF ã§éŸ³ã‚’出㙠+ // →ã¤ã¾ã‚Šã€0xFFFF ã®æ™‚ã ã‘音を出ã•ãªã„ã£ã¦ã“ã¨ã ã‚ã†ã‹ã€‚ + // ã§ã‚‚ã€0x07FF ã®æ™‚も音を出ã•ãªã„ã‘ã©ã€ãƒŽã‚¤ã‚ºã ã‘音を出ã™ã®ã‹ã‚‚。 + case 0x80: + case 0x81: i=(((unsigned int)chip->ws_ioRam[0x81])<<8)+((unsigned int)chip->ws_ioRam[0x80]); + if (i == 0xffff) + freq = 0; + else + freq = chip->clock/(2048-(i&0x7ff)); + chip->ws_audio[0].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); + break; + case 0x82: + case 0x83: i=(((unsigned int)chip->ws_ioRam[0x83])<<8)+((unsigned int)chip->ws_ioRam[0x82]); + if (i == 0xffff) + freq = 0; + else + freq = chip->clock/(2048-(i&0x7ff)); + chip->ws_audio[1].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); + break; + case 0x84: + case 0x85: i=(((unsigned int)chip->ws_ioRam[0x85])<<8)+((unsigned int)chip->ws_ioRam[0x84]); + chip->SweepFreq = i; + if (i == 0xffff) + freq = 0; + else + freq = chip->clock/(2048-(i&0x7ff)); + chip->ws_audio[2].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); + break; + case 0x86: + case 0x87: i=(((unsigned int)chip->ws_ioRam[0x87])<<8)+((unsigned int)chip->ws_ioRam[0x86]); + if (i == 0xffff) + freq = 0; + else + freq = chip->clock/(2048-(i&0x7ff)); + chip->ws_audio[3].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); + break; + case 0x88: + chip->ws_audio[0].lvol = (value>>4)&0xf; + chip->ws_audio[0].rvol = value&0xf; + break; + case 0x89: + chip->ws_audio[1].lvol = (value>>4)&0xf; + chip->ws_audio[1].rvol = value&0xf; + break; + case 0x8A: + chip->ws_audio[2].lvol = (value>>4)&0xf; + chip->ws_audio[2].rvol = value&0xf; + break; + case 0x8B: + chip->ws_audio[3].lvol = (value>>4)&0xf; + chip->ws_audio[3].rvol = value&0xf; + break; + case 0x8C: + chip->SweepStep = (signed char)value; + break; + case 0x8D: + //Sweepã®é–“隔㯠1/375[s] = 2.666..[ms] + //CPU Clockã§è¨€ã†ã¨ 3072000/375 = 8192[cycles] + //ã“ã“ã®è¨­å®šå€¤ã‚’nã¨ã™ã‚‹ã¨ã€8192[cycles]*(n+1) é–“éš”ã§Sweepã™ã‚‹ã“ã¨ã«ãªã‚‹ + // + //ã“れを HBlank (256cycles) ã®é–“éš”ã§è¨€ã†ã¨ã€ + // 8192/256 = 32 + //ãªã®ã§ã€32[HBlank]*(n+1) é–“éš”ã¨ãªã‚‹ + chip->SweepTime = (((unsigned int)value)+1)<<5; + chip->SweepCount = chip->SweepTime; + break; + case 0x8E: + chip->NoiseType = value&7; + if (value&8) chip->NoiseRng = 1; //ノイズカウンターリセット + break; + case 0x8F: + //WaveAdrs = (unsigned int)value<<6; + chip->ws_audio[0].wave = (unsigned int)value<<6; + chip->ws_audio[1].wave = chip->ws_audio[0].wave + 0x10; + chip->ws_audio[2].wave = chip->ws_audio[1].wave + 0x10; + chip->ws_audio[3].wave = chip->ws_audio[2].wave + 0x10; + break; + case 0x90: + break; + case 0x91: + //ã“ã“ã§ã®ãƒœãƒªãƒ¥ãƒ¼ãƒ èª¿æ•´ã¯ã€å†…蔵Speakerã«å¯¾ã—ã¦ã®èª¿æ•´ã ã‘らã—ã„ã®ã§ã€ + //ヘッドフォン接続ã•れã¦ã„ã‚‹ã¨èªè­˜ã•ã›ã‚Œã°å•題無ã„らã—ã„。 + chip->ws_ioRam[port] |= 0x80; + break; + case 0x92: + case 0x93: + break; + case 0x94: + chip->PCMVolumeLeft = (value&0xc)*2; + chip->PCMVolumeRight = ((value<<2)&0xc)*2; + break; + /*case 0x52: + if (value&0x80) + ws_timer_set(2, DMACycles[value&3]); + break;*/ + } +} + +BYTE ws_audio_port_read(void *_info, BYTE port) +{ + wsa_state* chip = (wsa_state *)_info; + return (chip->ws_ioRam[port]); +} + +// HBlanké–“éš”ã§å‘¼ã°ã‚Œã‚‹ +// Note: Must be called every 256 cycles (3072000 Hz clock), i.e. at 12000 Hz +static void ws_audio_process(wsa_state* chip) +{ + long freq; + + if (chip->SweepStep && (SNDMOD&0x40)) + { + if (chip->SweepCount < 0) + { + chip->SweepCount = chip->SweepTime; + chip->SweepFreq += chip->SweepStep; + chip->SweepFreq &= 0x7FF; + + //Update_SampleData(); + + freq = chip->clock/(2048-chip->SweepFreq); + chip->ws_audio[2].delta = (long)((float)freq*(float)65536/(float)chip->smplrate); + } + chip->SweepCount--; + } +} + +/*void ws_audio_sounddma(void) +{ + int i, j, b; + + if ((SDMACTL&0x88)==0x80) + { + i=(SDMACH<<8)|SDMACL; + j=(SDMASB<<16)|(SDMASH<<8)|SDMASL; + b=cpu_readmem20(j); + + Update_SampleData(); + + ws_ioRam[0x89]=b; + i--; + j++; + if(i<32) + { + i=0; + SDMACTL&=0x7F; + } + else + { + ws_timer_set(2, DMACycles[SDMACTL&3]); + } + SDMASB=(byte)((j>>16)&0xFF); + SDMASH=(byte)((j>>8)&0xFF); + SDMASL=(byte)(j&0xFF); + SDMACH=(byte)((i>>8)&0xFF); + SDMACL=(byte)(i&0xFF); + } +}*/ + +void ws_write_ram(void *_info, UINT16 offset, UINT8 value) +{ + wsa_state* chip = (wsa_state *)_info; + + // RAM - 16 KB (WS) / 64 KB (WSC) internal RAM + if (offset >= 0x4000) + return; // We only allocated 16 KB. + + chip->ws_internalRam[offset] = value; + return; +} + +void ws_set_mute_mask(void *_info, UINT32 MuteMask) +{ + wsa_state* chip = (wsa_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 4; CurChn ++) + chip->ws_audio[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/ws_audio.h b/Frameworks/GME/vgmplay/chips/ws_audio.h new file mode 100644 index 000000000..cb17a17db --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ws_audio.h @@ -0,0 +1,16 @@ +#ifndef __WS_AUDIO_H__ +#define __WS_AUDIO_H__ + +int ws_audio_init(void **chip, int clock, int samplerate, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void ws_audio_reset(void *chip); +void ws_audio_done(void *chip); +void ws_audio_update(void *chip, stream_sample_t** buffer, int length); +void ws_audio_port_write(void *chip, UINT8 port, UINT8 value); +UINT8 ws_audio_port_read(void *chip, UINT8 port); +//void ws_audio_process(void); +//void ws_audio_sounddma(void); +void ws_write_ram(void *chip, UINT16 offset, UINT8 value); +void ws_set_mute_mask(void *chip, UINT32 MuteMask); +//extern int WaveAdrs; + +#endif diff --git a/Frameworks/GME/vgmplay/chips/ws_initialIo.h b/Frameworks/GME/vgmplay/chips/ws_initialIo.h new file mode 100644 index 000000000..f739ce052 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ws_initialIo.h @@ -0,0 +1,271 @@ +//////////////////////////////////////////////////////////////////////////////// +// Initial I/O values +//////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// +// +////////////////////////////////////////////////////////////////////////////// + +uint8 initialIoValue[256]= +{ + 0x00,//0 + 0x00,//1 + 0x9d,//2 + 0xbb,//3 + 0x00,//4 + 0x00,//5 + 0x00,//6 + 0x26,//7 + 0xfe,//8 + 0xde,//9 + 0xf9,//a + 0xfb,//b + 0xdb,//c + 0xd7,//d + 0x7f,//e + 0xf5,//f + 0x00,//10 + 0x00,//11 + 0x00,//12 + 0x00,//13 + 0x01,//14 + 0x00,//15 + 0x9e,//16 + 0x9b,//17 + 0x00,//18 + 0x00,//19 + 0x00,//1a + 0x00,//1b + 0x99,//1c + 0xfd,//1d + 0xb7,//1e + 0xdf,//1f + 0x30,//20 + 0x57,//21 + 0x75,//22 + 0x76,//23 + 0x15,//24 + 0x73,//25 + 0x77,//26 + 0x77,//27 + 0x20,//28 + 0x75,//29 + 0x50,//2a + 0x36,//2b + 0x70,//2c + 0x67,//2d + 0x50,//2e + 0x77,//2f + 0x57,//30 + 0x54,//31 + 0x75,//32 + 0x77,//33 + 0x75,//34 + 0x17,//35 + 0x37,//36 + 0x73,//37 + 0x50,//38 + 0x57,//39 + 0x60,//3a + 0x77,//3b + 0x70,//3c + 0x77,//3d + 0x10,//3e + 0x73,//3f + 0x00,//40 + 0x00,//41 + 0x00,//42 + 0x00,//43 + 0x00,//44 + 0x00,//45 + 0x00,//46 + 0x00,//47 + 0x00,//48 + 0x00,//49 + 0x00,//4a + 0x00,//4b + 0x00,//4c + 0x00,//4d + 0x00,//4e + 0x00,//4f + 0x00,//50 + 0x00,//51 + 0x00,//52 + 0x00,//53 + 0x00,//54 + 0x00,//55 + 0x00,//56 + 0x00,//57 + 0x00,//58 + 0x00,//59 + 0x00,//5a + 0x00,//5b + 0x00,//5c + 0x00,//5d + 0x00,//5e + 0x00,//5f + 0x0a,//60 + 0x00,//61 + 0x00,//62 + 0x00,//63 + 0x00,//64 + 0x00,//65 + 0x00,//66 + 0x00,//67 + 0x00,//68 + 0x00,//69 + 0x00,//6a + 0x0f,//6b + 0x00,//6c + 0x00,//6d + 0x00,//6e + 0x00,//6f + 0x00,//70 + 0x00,//71 + 0x00,//72 + 0x00,//73 + 0x00,//74 + 0x00,//75 + 0x00,//76 + 0x00,//77 + 0x00,//78 + 0x00,//79 + 0x00,//7a + 0x00,//7b + 0x00,//7c + 0x00,//7d + 0x00,//7e + 0x00,//7f + 0xFF,//80 + 0x07,//81 + 0xFF,//82 + 0x07,//83 + 0xFF,//84 + 0x07,//85 + 0xFF,//86 + 0x07,//87 + 0x00,//88 + 0x00,//89 + 0x00,//8a + 0x00,//8b + 0x00,//8c + 0x1f,//8d 1d ? + 0x00,//8e + 0x00,//8f + 0x00,//90 + 0x00,//91 + 0x00,//92 + 0x00,//93 + 0x00,//94 + 0x00,//95 + 0x00,//96 + 0x00,//97 + 0x00,//98 + 0x00,//99 + 0x00,//9a + 0x00,//9b + 0x00,//9c + 0x00,//9d + 0x03,//9e + 0x00,//9f + 0x87-2,//a0 + 0x00,//a1 + 0x00,//a2 + 0x00,//a3 + 0x0,//a4 2b + 0x0,//a5 7f + 0x4f,//a6 + 0xff,//a7 cf ? + 0x00,//a8 + 0x00,//a9 + 0x00,//aa + 0x00,//ab + 0x00,//ac + 0x00,//ad + 0x00,//ae + 0x00,//af + 0x00,//b0 + 0xdb,//b1 + 0x00,//b2 + 0x00,//b3 + 0x00,//b4 + 0x40,//b5 + 0x00,//b6 + 0x00,//b7 + 0x00,//b8 + 0x00,//b9 + 0x01,//ba + 0x00,//bb + 0x42,//bc + 0x00,//bd + 0x83,//be + 0x00,//bf + 0x2f,//c0 + 0x3f,//c1 + 0xff,//c2 + 0xff,//c3 + 0x00,//c4 + 0x00,//c5 + 0x00,//c6 + 0x00,//c7 + + 0xd1,//c8? + 0xd1,//c9 + 0xd1,//ca + 0xd1,//cb + 0xd1,//cc + 0xd1,//cd + 0xd1,//ce + 0xd1,//cf + 0xd1,//d0 + 0xd1,//d1 + 0xd1,//d2 + 0xd1,//d3 + 0xd1,//d4 + 0xd1,//d5 + 0xd1,//d6 + 0xd1,//d7 + 0xd1,//d8 + 0xd1,//d9 + 0xd1,//da + 0xd1,//db + 0xd1,//dc + 0xd1,//dd + 0xd1,//de + 0xd1,//df + 0xd1,//e0 + 0xd1,//e1 + 0xd1,//e2 + 0xd1,//e3 + 0xd1,//e4 + 0xd1,//e5 + 0xd1,//e6 + 0xd1,//e7 + 0xd1,//e8 + 0xd1,//e9 + 0xd1,//ea + 0xd1,//eb + 0xd1,//ec + 0xd1,//ed + 0xd1,//ee + 0xd1,//ef + 0xd1,//f0 + 0xd1,//f1 + 0xd1,//f2 + 0xd1,//f3 + 0xd1,//f4 + 0xd1,//f5 + 0xd1,//f6 + 0xd1,//f7 + 0xd1,//f8 + 0xd1,//f9 + 0xd1,//fa + 0xd1,//fb + 0xd1,//fc + 0xd1,//fd + 0xd1,//fe + 0xd1 //ff +}; diff --git a/Frameworks/GME/vgmplay/chips/x1_010.c b/Frameworks/GME/vgmplay/chips/x1_010.c new file mode 100644 index 000000000..5da0df5b5 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/x1_010.c @@ -0,0 +1,399 @@ +/*************************************************************************** + + -= Seta Hardware =- + + driver by Luca Elia (l.elia@tin.it) + + rewrite by Manbow-J(manbowj@hamal.freemail.ne.jp) + + X1-010 Seta Custom Sound Chip (80 Pin PQFP) + + Custom programmed Mitsubishi M60016 Gate Array, 3608 gates, 148 Max I/O ports + + The X1-010 is 16 Voices sound generator, each channel gets it's + waveform from RAM (128 bytes per waveform, 8 bit unsigned data) + or sampling PCM(8bit unsigned data). + +Registers: + 8 registers per channel (mapped to the lower bytes of 16 words on the 68K) + + Reg: Bits: Meaning: + + 0 7654 3--- + ---- -2-- PCM/Waveform repeat flag (0:Ones 1:Repeat) (*1) + ---- --1- Sound out select (0:PCM 1:Waveform) + ---- ---0 Key on / off + + 1 7654 ---- PCM Volume 1 (L?) + ---- 3210 PCM Volume 2 (R?) + Waveform No. + + 2 PCM Frequency + Waveform Pitch Lo + + 3 Waveform Pitch Hi + + 4 PCM Sample Start / 0x1000 [Start/End in bytes] + Waveform Envelope Time + + 5 PCM Sample End 0x100 - (Sample End / 0x1000) [PCM ROM is Max 1MB?] + Waveform Envelope No. + 6 Reserved + 7 Reserved + + offset 0x0000 - 0x0fff Wave form data + offset 0x1000 - 0x1fff Envelope data + + *1 : when 0 is specified, hardware interrupt is caused(allways return soon) + +***************************************************************************/ + +//#include "emu.h" +#include +#include +#include // for NULL +#include "mamedef.h" +#include "x1_010.h" + + +#define VERBOSE_SOUND 0 +#define VERBOSE_REGISTER_WRITE 0 +#define VERBOSE_REGISTER_READ 0 + +#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0) +#define LOG_REGISTER_WRITE(x) do { if (VERBOSE_REGISTER_WRITE) logerror x; } while (0) +#define LOG_REGISTER_READ(x) do { if (VERBOSE_REGISTER_READ) logerror x; } while (0) + +#define SETA_NUM_CHANNELS 16 + +#define FREQ_BASE_BITS 8 // Frequency fixed decimal shift bits +#define ENV_BASE_BITS 16 // wave form envelope fixed decimal shift bits +#define VOL_BASE (2*32*256/30) // Volume base + +/* this structure defines the parameters for a channel */ +typedef struct { + unsigned char status; + unsigned char volume; // volume / wave form no. + unsigned char frequency; // frequency / pitch lo + unsigned char pitch_hi; // reserved / pitch hi + unsigned char start; // start address / envelope time + unsigned char end; // end address / envelope no. + unsigned char reserve[2]; +} X1_010_CHANNEL; + +typedef struct _x1_010_state x1_010_state; +struct _x1_010_state +{ + /* Variables only used here */ + int rate; // Output sampling rate (Hz) + //sound_stream * stream; // Stream handle + //int address; // address eor data + //const UINT8 *region; // region name + UINT32 ROMSize; + UINT8* rom; + int sound_enable; // sound output enable/disable + UINT8 reg[0x2000]; // X1-010 Register & wave form area +// UINT8 HI_WORD_BUF[0x2000]; // X1-010 16bit access ram check avoidance work + UINT32 smp_offset[SETA_NUM_CHANNELS]; + UINT32 env_offset[SETA_NUM_CHANNELS]; + + UINT32 base_clock; + + UINT8 Muted[SETA_NUM_CHANNELS]; +}; + +/* mixer tables and internal buffers */ +//static short *mixer_buffer = NULL; + +/*INLINE x1_010_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == X1_010); + return (x1_010_state *)downcast(device)->token(); +}*/ + + +/*-------------------------------------------------------------- + generate sound to the mix buffer +--------------------------------------------------------------*/ +//static STREAM_UPDATE( seta_update ) +void seta_update(void *param, stream_sample_t **outputs, int samples) +{ + x1_010_state *info = (x1_010_state *)param; + X1_010_CHANNEL *reg; + int ch, i, volL, volR, freq, div; + register INT8 *start, *end, data; + register UINT8 *env; + register UINT32 smp_offs, smp_step, env_offs, env_step, delta; + + // mixer buffer zero clear + memset( outputs[0], 0, samples*sizeof(*outputs[0]) ); + memset( outputs[1], 0, samples*sizeof(*outputs[1]) ); + +// if( info->sound_enable == 0 ) return; + + for( ch = 0; ch < SETA_NUM_CHANNELS; ch++ ) { + reg = (X1_010_CHANNEL *)&(info->reg[ch*sizeof(X1_010_CHANNEL)]); + if( (reg->status&1) != 0 && ! info->Muted[ch]) { // Key On + stream_sample_t *bufL = outputs[0]; + stream_sample_t *bufR = outputs[1]; + div = (reg->status&0x80) ? 1 : 0; + if( (reg->status&2) == 0 ) { // PCM sampling + start = (INT8 *)(info->rom + reg->start*0x1000); + end = (INT8 *)(info->rom + (0x100-reg->end)*0x1000); + volL = ((reg->volume>>4)&0xf)*VOL_BASE; + volR = ((reg->volume>>0)&0xf)*VOL_BASE; + smp_offs = info->smp_offset[ch]; + freq = reg->frequency>>div; + // Meta Fox does write the frequency register, but this is a hack to make it "work" with the current setup + // This is broken for Arbalester (it writes 8), but that'll be fixed later. + if( freq == 0 ) freq = 4; + smp_step = (UINT32)((float)info->base_clock/8192.0 + *freq*(1<rate); + if( smp_offs == 0 ) { + LOG_SOUND(( "Play sample %p - %p, channel %X volume %d:%d freq %X step %X offset %X\n", + start, end, ch, volL, volR, freq, smp_step, smp_offs )); + } + for( i = 0; i < samples; i++ ) { + delta = smp_offs>>FREQ_BASE_BITS; + // sample ended? + if( start+delta >= end ) { + reg->status &= 0xfe; // Key off + break; + } + data = *(start+delta); + *bufL++ += (data*volL/256); + *bufR++ += (data*volR/256); + smp_offs += smp_step; + } + info->smp_offset[ch] = smp_offs; + } else { // Wave form + start = (INT8 *)&(info->reg[reg->volume*128+0x1000]); + smp_offs = info->smp_offset[ch]; + freq = ((reg->pitch_hi<<8)+reg->frequency)>>div; + smp_step = (UINT32)((float)info->base_clock/128.0/1024.0/4.0*freq*(1<rate); + + env = (UINT8 *)&(info->reg[reg->end*128]); + env_offs = info->env_offset[ch]; + env_step = (UINT32)((float)info->base_clock/128.0/1024.0/4.0*reg->start*(1<rate); + /* Print some more debug info */ + if( smp_offs == 0 ) { + LOG_SOUND(( "Play waveform %X, channel %X volume %X freq %4X step %X offset %X\n", + reg->volume, ch, reg->end, freq, smp_step, smp_offs )); + } + for( i = 0; i < samples; i++ ) { + int vol; + delta = env_offs>>ENV_BASE_BITS; + // Envelope one shot mode + if( (reg->status&4) != 0 && delta >= 0x80 ) { + reg->status &= 0xfe; // Key off + break; + } + vol = *(env+(delta&0x7f)); + volL = ((vol>>4)&0xf)*VOL_BASE; + volR = ((vol>>0)&0xf)*VOL_BASE; + data = *(start+((smp_offs>>FREQ_BASE_BITS)&0x7f)); + *bufL++ += (data*volL/256); + *bufR++ += (data*volR/256); + smp_offs += smp_step; + env_offs += env_step; + } + info->smp_offset[ch] = smp_offs; + info->env_offset[ch] = env_offs; + } + } + } +} + + + +//static DEVICE_START( x1_010 ) +int device_start_x1_010(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE) +{ + int i; + //const x1_010_interface *intf = (const x1_010_interface *)device->static_config(); + //x1_010_state *info = get_safe_token(device); + x1_010_state *info; + + info = (x1_010_state *) calloc(1, sizeof(x1_010_state)); + *_info = (void *) info; + + //info->region = *device->region(); + //info->base_clock = device->clock(); + //info->rate = device->clock() / 1024; + //info->address = intf->adr; + info->ROMSize = 0x00; + info->rom = NULL; + info->base_clock = clock; + info->rate = clock / 1024; + if (((CHIP_SAMPLING_MODE & 0x01) && info->rate < CHIP_SAMPLE_RATE) || + CHIP_SAMPLING_MODE == 0x02) + info->rate = CHIP_SAMPLE_RATE; + + for( i = 0; i < SETA_NUM_CHANNELS; i++ ) { + info->smp_offset[i] = 0; + info->env_offset[i] = 0; + } + /* Print some more debug info */ + //LOG_SOUND(("masterclock = %d rate = %d\n", device->clock(), info->rate )); + + /* get stream channels */ + //info->stream = device->machine().sound().stream_alloc(*device,0,2,info->rate,info,seta_update); + return info->rate; +} + +void device_stop_x1_010(void *_info) +{ + x1_010_state *info = (x1_010_state *)_info; + + free(info->rom); info->rom = NULL; + + free(info); + + return; +} + +void device_reset_x1_010(void *_info) +{ + x1_010_state *info = (x1_010_state *)_info; + + memset(info->reg, 0, 0x2000); + //memset(info->HI_WORD_BUF, 0, 0x2000); + memset(info->smp_offset, 0, SETA_NUM_CHANNELS * sizeof(UINT32)); + memset(info->env_offset, 0, SETA_NUM_CHANNELS * sizeof(UINT32)); + + return; +} + + +/*void seta_sound_enable_w(device_t *device, int data) +{ + x1_010_state *info = get_safe_token(device); + info->sound_enable = data; +}*/ + + + +/* Use these for 8 bit CPUs */ + + +//READ8_DEVICE_HANDLER( seta_sound_r ) +UINT8 seta_sound_r(void *_info, offs_t offset) +{ + //x1_010_state *info = get_safe_token(device); + x1_010_state *info = (x1_010_state *)_info; + //offset ^= info->address; + return info->reg[offset]; +} + + + + +//WRITE8_DEVICE_HANDLER( seta_sound_w ) +void seta_sound_w(void *_info, offs_t offset, UINT8 data) +{ + //x1_010_state *info = get_safe_token(device); + x1_010_state *info = (x1_010_state *)_info; + int channel, reg; + //offset ^= info->address; + + channel = offset/sizeof(X1_010_CHANNEL); + reg = offset%sizeof(X1_010_CHANNEL); + + if( channel < SETA_NUM_CHANNELS && reg == 0 + && (info->reg[offset]&1) == 0 && (data&1) != 0 ) { + info->smp_offset[channel] = 0; + info->env_offset[channel] = 0; + } + //LOG_REGISTER_WRITE(("%s: offset %6X : data %2X\n", device->machine().describe_context(), offset, data )); + info->reg[offset] = data; +} + +void x1_010_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + x1_010_state *info = (x1_010_state *)_info; + + if (info->ROMSize != ROMSize) + { + info->rom = (UINT8*)realloc(info->rom, ROMSize); + info->ROMSize = ROMSize; + memset(info->rom, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(info->rom + DataStart, ROMData, DataLength); + + return; +} + + +void x1_010_set_mute_mask(void *_info, UINT32 MuteMask) +{ + x1_010_state *info = (x1_010_state *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < SETA_NUM_CHANNELS; CurChn ++) + info->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; + + return; +} + + + + +/* Use these for 16 bit CPUs */ + +/*READ16_DEVICE_HANDLER( seta_sound_word_r ) +{ + //x1_010_state *info = get_safe_token(device); + x1_010_state *info = &X1010Data[ChipID]; + UINT16 ret; + + ret = info->HI_WORD_BUF[offset]<<8; + ret += (seta_sound_r( device, offset )&0xff); + LOG_REGISTER_READ(( "%s: Read X1-010 Offset:%04X Data:%04X\n", device->machine().describe_context(), offset, ret )); + return ret; +} + +WRITE16_DEVICE_HANDLER( seta_sound_word_w ) +{ + //x1_010_state *info = get_safe_token(device); + x1_010_state *info = &X1010Data[ChipID]; + info->HI_WORD_BUF[offset] = (data>>8)&0xff; + seta_sound_w( device, offset, data&0xff ); + LOG_REGISTER_WRITE(( "%s: Write X1-010 Offset:%04X Data:%04X\n", device->machine().describe_context(), offset, data )); +}*/ + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( x1_010 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(x1_010_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( x1_010 ); break; + case DEVINFO_FCT_STOP: // Nothing // break; + case DEVINFO_FCT_RESET: // Nothing // break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "X1-010"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Seta custom"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(X1_010, x1_010);*/ diff --git a/Frameworks/GME/vgmplay/chips/x1_010.h b/Frameworks/GME/vgmplay/chips/x1_010.h new file mode 100644 index 000000000..af60f0b8e --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/x1_010.h @@ -0,0 +1,38 @@ +#pragma once + +#ifndef __X1_010_H__ +#define __X1_010_H__ + +//#include "devlegcy.h" + + +/*typedef struct _x1_010_interface x1_010_interface; +struct _x1_010_interface +{ + int adr; // address +};*/ + + +void seta_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_x1_010(void **chip, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE); +void device_stop_x1_010(void *chip); +void device_reset_x1_010(void *chip); + +//READ8_DEVICE_HANDLER ( seta_sound_r ); +//WRITE8_DEVICE_HANDLER( seta_sound_w ); +UINT8 seta_sound_r(void *chip, offs_t offset); +void seta_sound_w(void *chip, offs_t offset, UINT8 data); + +//READ16_DEVICE_HANDLER ( seta_sound_word_r ); +//WRITE16_DEVICE_HANDLER( seta_sound_word_w ); + +//void seta_sound_enable_w(device_t *device, int data); + +void x1_010_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void x1_010_set_mute_mask(void *chip, UINT32 MuteMask); + +//DECLARE_LEGACY_SOUND_DEVICE(X1_010, x1_010); + +#endif /* __X1_010_H__ */ diff --git a/Frameworks/GME/gme/ym2151.c b/Frameworks/GME/vgmplay/chips/ym2151.c similarity index 72% rename from Frameworks/GME/gme/ym2151.c rename to Frameworks/GME/vgmplay/chips/ym2151.c index b17eca9ae..17b60d1c1 100644 --- a/Frameworks/GME/gme/ym2151.c +++ b/Frameworks/GME/vgmplay/chips/ym2151.c @@ -1,2010 +1,2538 @@ -/***************************************************************************** -* -* Yamaha YM2151 driver (version 2.150 final beta) -* -******************************************************************************/ - -#include "mathdefs.h" -#include -#include -#include -#include "mamedef.h" -#include "ym2151.h" - -#ifndef logerror -#define logerror (void) -#endif - - -/* struct describing a single operator */ -typedef struct -{ - UINT32 phase; /* accumulated operator phase */ - UINT32 freq; /* operator frequency count */ - INT32 dt1; /* current DT1 (detune 1 phase inc/decrement) value */ - UINT32 mul; /* frequency count multiply */ - UINT32 dt1_i; /* DT1 index * 32 */ - UINT32 dt2; /* current DT2 (detune 2) value */ - - signed int *connect; /* operator output 'direction' */ - - /* only M1 (operator 0) is filled with this data: */ - signed int *mem_connect; /* where to put the delayed sample (MEM) */ - INT32 mem_value; /* delayed sample (MEM) value */ - - /* channel specific data; note: each operator number 0 contains channel specific data */ - UINT32 fb_shift; /* feedback shift value for operators 0 in each channel */ - INT32 fb_out_curr; /* operator feedback value (used only by operators 0) */ - INT32 fb_out_prev; /* previous feedback value (used only by operators 0) */ - UINT32 kc; /* channel KC (copied to all operators) */ - UINT32 kc_i; /* just for speedup */ - UINT32 pms; /* channel PMS */ - UINT32 ams; /* channel AMS */ - /* end of channel specific data */ - - UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ - UINT32 state; /* Envelope state: 4-attack(AR) 3-decay(D1R) 2-sustain(D2R) 1-release(RR) 0-off */ - UINT8 eg_sh_ar; /* (attack state) */ - UINT8 eg_sel_ar; /* (attack state) */ - UINT32 tl; /* Total attenuation Level */ - INT32 volume; /* current envelope attenuation level */ - UINT8 eg_sh_d1r; /* (decay state) */ - UINT8 eg_sel_d1r; /* (decay state) */ - UINT32 d1l; /* envelope switches to sustain state after reaching this level */ - UINT8 eg_sh_d2r; /* (sustain state) */ - UINT8 eg_sel_d2r; /* (sustain state) */ - UINT8 eg_sh_rr; /* (release state) */ - UINT8 eg_sel_rr; /* (release state) */ - - UINT32 key; /* 0=last key was KEY OFF, 1=last key was KEY ON */ - - UINT32 ks; /* key scale */ - UINT32 ar; /* attack rate */ - UINT32 d1r; /* decay rate */ - UINT32 d2r; /* sustain rate */ - UINT32 rr; /* release rate */ - - UINT32 reserved0; /**/ - UINT32 reserved1; /**/ - -} YM2151Operator; - - -typedef struct -{ - signed int chanout[8]; - signed int m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ - signed int mem; /* one sample delay memory */ - - YM2151Operator oper[32]; /* the 32 operators */ - - UINT32 pan[16]; /* channels output masks (0xffffffff = enable) */ - - UINT32 eg_cnt; /* global envelope generator counter */ - UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */ - UINT32 eg_timer_add; /* step of eg_timer */ - UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */ - - UINT32 lfo_phase; /* accumulated LFO phase (0 to 255) */ - UINT32 lfo_timer; /* LFO timer */ - UINT32 lfo_timer_add; /* step of lfo_timer */ - UINT32 lfo_overflow; /* LFO generates new output when lfo_timer reaches this value */ - UINT32 lfo_counter; /* LFO phase increment counter */ - UINT32 lfo_counter_add; /* step of lfo_counter */ - UINT8 lfo_wsel; /* LFO waveform (0-saw, 1-square, 2-triangle, 3-random noise) */ - UINT8 amd; /* LFO Amplitude Modulation Depth */ - INT8 pmd; /* LFO Phase Modulation Depth */ - UINT32 lfa; /* LFO current AM output */ - INT32 lfp; /* LFO current PM output */ - - UINT8 test; /* TEST register */ - UINT8 ct; /* output control pins (bit1-CT2, bit0-CT1) */ - - UINT32 noise; /* noise enable/period register (bit 7 - noise enable, bits 4-0 - noise period */ - UINT32 noise_rng; /* 17 bit noise shift register */ - UINT32 noise_p; /* current noise 'phase'*/ - UINT32 noise_f; /* current noise period */ - - UINT32 csm_req; /* CSM KEY ON / KEY OFF sequence request */ - - UINT32 irq_enable; /* IRQ enable for timer B (bit 3) and timer A (bit 2); bit 7 - CSM mode (keyon to all slots, everytime timer A overflows) */ - UINT32 status; /* chip status (BUSY, IRQ Flags) */ - UINT8 connect[8]; /* channels connections */ - - /* Frequency-deltas to get the closest frequency possible. - * There are 11 octaves because of DT2 (max 950 cents over base frequency) - * and LFO phase modulation (max 800 cents below AND over base frequency) - * Summary: octave explanation - * 0 note code - LFO PM - * 1 note code - * 2 note code - * 3 note code - * 4 note code - * 5 note code - * 6 note code - * 7 note code - * 8 note code - * 9 note code + DT2 + LFO PM - * 10 note code + DT2 + LFO PM - */ - UINT32 freq[11*768]; /* 11 octaves, 768 'cents' per octave */ - - /* Frequency deltas for DT1. These deltas alter operator frequency - * after it has been taken from frequency-deltas table. - */ - INT32 dt1_freq[8*32]; /* 8 DT1 levels, 32 KC values */ - - UINT32 noise_tab[32]; /* 17bit Noise Generator periods */ - - unsigned int clock; /* chip clock in Hz (passed from 2151intf.c) */ - unsigned int sampfreq; /* sampling frequency in Hz (passed from 2151intf.c) */ - - UINT32 mask; -} YM2151; - - -#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ -#define LFO_SH 10 /* 22.10 fixed point (LFO calculations) */ -#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ - -#define FREQ_MASK ((1<>3) - -/* sin waveform table in 'decibel' scale */ -static unsigned int sin_tab[SIN_LEN]; - - -/* translate from D1L to volume index (16 D1L levels) */ -static UINT32 d1l_tab[16]; - - -#define RATE_STEPS (8) -static const UINT8 eg_inc[19*RATE_STEPS]={ - -/*cycle:0 1 2 3 4 5 6 7*/ - -/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ -/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ -/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ -/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ - -/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ -/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ -/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ -/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ - -/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ -/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ -/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ -/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ - -/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ -/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ -/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ -/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ - -/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ -/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ -/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ -}; - - -#define O(a) (a*RATE_STEPS) - -/*note that there is no O(17) in this table - it's directly in the code */ -static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ -/* 32 dummy (infinite time) rates */ -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), -O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), - -/* rates 00-11 */ -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), - -/* rate 12 */ -O( 4),O( 5),O( 6),O( 7), - -/* rate 13 */ -O( 8),O( 9),O(10),O(11), - -/* rate 14 */ -O(12),O(13),O(14),O(15), - -/* rate 15 */ -O(16),O(16),O(16),O(16), - -/* 32 dummy rates (same as 15 3) */ -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), -O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) - -}; -#undef O - -/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ -/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ -/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ - -#define O(a) (a*1) -static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ -/* 32 infinite time rates */ -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), - - -/* rates 00-11 */ -O(11),O(11),O(11),O(11), -O(10),O(10),O(10),O(10), -O( 9),O( 9),O( 9),O( 9), -O( 8),O( 8),O( 8),O( 8), -O( 7),O( 7),O( 7),O( 7), -O( 6),O( 6),O( 6),O( 6), -O( 5),O( 5),O( 5),O( 5), -O( 4),O( 4),O( 4),O( 4), -O( 3),O( 3),O( 3),O( 3), -O( 2),O( 2),O( 2),O( 2), -O( 1),O( 1),O( 1),O( 1), -O( 0),O( 0),O( 0),O( 0), - -/* rate 12 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 13 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 14 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 15 */ -O( 0),O( 0),O( 0),O( 0), - -/* 32 dummy rates (same as 15 3) */ -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) - -}; -#undef O - -/* DT2 defines offset in cents from base note -* -* This table defines offset in frequency-deltas table. -* User's Manual page 22 -* -* Values below were calculated using formula: value = orig.val / 1.5625 -* -* DT2=0 DT2=1 DT2=2 DT2=3 -* 0 600 781 950 -*/ -static const UINT32 dt2_tab[4] = { 0, 384, 500, 608 }; - -/* DT1 defines offset in Hertz from base note -* This table is converted while initialization... -* Detune table shown in YM2151 User's Manual is wrong (verified on the real chip) -*/ - -static const UINT8 dt1_tab[4*32] = { /* 4*32 DT1 values */ -/* DT1=0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -/* DT1=1 */ - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, - -/* DT1=2 */ - 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, - 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, - -/* DT1=3 */ - 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, - 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 -}; - -static const UINT16 phaseinc_rom[768]={ -1299,1300,1301,1302,1303,1304,1305,1306,1308,1309,1310,1311,1313,1314,1315,1316, -1318,1319,1320,1321,1322,1323,1324,1325,1327,1328,1329,1330,1332,1333,1334,1335, -1337,1338,1339,1340,1341,1342,1343,1344,1346,1347,1348,1349,1351,1352,1353,1354, -1356,1357,1358,1359,1361,1362,1363,1364,1366,1367,1368,1369,1371,1372,1373,1374, -1376,1377,1378,1379,1381,1382,1383,1384,1386,1387,1388,1389,1391,1392,1393,1394, -1396,1397,1398,1399,1401,1402,1403,1404,1406,1407,1408,1409,1411,1412,1413,1414, -1416,1417,1418,1419,1421,1422,1423,1424,1426,1427,1429,1430,1431,1432,1434,1435, -1437,1438,1439,1440,1442,1443,1444,1445,1447,1448,1449,1450,1452,1453,1454,1455, -1458,1459,1460,1461,1463,1464,1465,1466,1468,1469,1471,1472,1473,1474,1476,1477, -1479,1480,1481,1482,1484,1485,1486,1487,1489,1490,1492,1493,1494,1495,1497,1498, -1501,1502,1503,1504,1506,1507,1509,1510,1512,1513,1514,1515,1517,1518,1520,1521, -1523,1524,1525,1526,1528,1529,1531,1532,1534,1535,1536,1537,1539,1540,1542,1543, -1545,1546,1547,1548,1550,1551,1553,1554,1556,1557,1558,1559,1561,1562,1564,1565, -1567,1568,1569,1570,1572,1573,1575,1576,1578,1579,1580,1581,1583,1584,1586,1587, -1590,1591,1592,1593,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1609,1610, -1613,1614,1615,1616,1618,1619,1621,1622,1624,1625,1627,1628,1630,1631,1632,1633, -1637,1638,1639,1640,1642,1643,1645,1646,1648,1649,1651,1652,1654,1655,1656,1657, -1660,1661,1663,1664,1666,1667,1669,1670,1672,1673,1675,1676,1678,1679,1681,1682, -1685,1686,1688,1689,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707, -1709,1710,1712,1713,1715,1716,1718,1719,1721,1722,1724,1725,1727,1728,1730,1731, -1734,1735,1737,1738,1740,1741,1743,1744,1746,1748,1749,1751,1752,1754,1755,1757, -1759,1760,1762,1763,1765,1766,1768,1769,1771,1773,1774,1776,1777,1779,1780,1782, -1785,1786,1788,1789,1791,1793,1794,1796,1798,1799,1801,1802,1804,1806,1807,1809, -1811,1812,1814,1815,1817,1819,1820,1822,1824,1825,1827,1828,1830,1832,1833,1835, -1837,1838,1840,1841,1843,1845,1846,1848,1850,1851,1853,1854,1856,1858,1859,1861, -1864,1865,1867,1868,1870,1872,1873,1875,1877,1879,1880,1882,1884,1885,1887,1888, -1891,1892,1894,1895,1897,1899,1900,1902,1904,1906,1907,1909,1911,1912,1914,1915, -1918,1919,1921,1923,1925,1926,1928,1930,1932,1933,1935,1937,1939,1940,1942,1944, -1946,1947,1949,1951,1953,1954,1956,1958,1960,1961,1963,1965,1967,1968,1970,1972, -1975,1976,1978,1980,1982,1983,1985,1987,1989,1990,1992,1994,1996,1997,1999,2001, -2003,2004,2006,2008,2010,2011,2013,2015,2017,2019,2021,2022,2024,2026,2028,2029, -2032,2033,2035,2037,2039,2041,2043,2044,2047,2048,2050,2052,2054,2056,2058,2059, -2062,2063,2065,2067,2069,2071,2073,2074,2077,2078,2080,2082,2084,2086,2088,2089, -2092,2093,2095,2097,2099,2101,2103,2104,2107,2108,2110,2112,2114,2116,2118,2119, -2122,2123,2125,2127,2129,2131,2133,2134,2137,2139,2141,2142,2145,2146,2148,2150, -2153,2154,2156,2158,2160,2162,2164,2165,2168,2170,2172,2173,2176,2177,2179,2181, -2185,2186,2188,2190,2192,2194,2196,2197,2200,2202,2204,2205,2208,2209,2211,2213, -2216,2218,2220,2222,2223,2226,2227,2230,2232,2234,2236,2238,2239,2242,2243,2246, -2249,2251,2253,2255,2256,2259,2260,2263,2265,2267,2269,2271,2272,2275,2276,2279, -2281,2283,2285,2287,2288,2291,2292,2295,2297,2299,2301,2303,2304,2307,2308,2311, -2315,2317,2319,2321,2322,2325,2326,2329,2331,2333,2335,2337,2338,2341,2342,2345, -2348,2350,2352,2354,2355,2358,2359,2362,2364,2366,2368,2370,2371,2374,2375,2378, -2382,2384,2386,2388,2389,2392,2393,2396,2398,2400,2402,2404,2407,2410,2411,2414, -2417,2419,2421,2423,2424,2427,2428,2431,2433,2435,2437,2439,2442,2445,2446,2449, -2452,2454,2456,2458,2459,2462,2463,2466,2468,2470,2472,2474,2477,2480,2481,2484, -2488,2490,2492,2494,2495,2498,2499,2502,2504,2506,2508,2510,2513,2516,2517,2520, -2524,2526,2528,2530,2531,2534,2535,2538,2540,2542,2544,2546,2549,2552,2553,2556, -2561,2563,2565,2567,2568,2571,2572,2575,2577,2579,2581,2583,2586,2589,2590,2593 -}; - - -/* - Noise LFO waveform. - - Here are just 256 samples out of much longer data. - - It does NOT repeat every 256 samples on real chip and I wasnt able to find - the point where it repeats (even in strings as long as 131072 samples). - - I only put it here because its better than nothing and perhaps - someone might be able to figure out the real algorithm. - - - Note that (due to the way the LFO output is calculated) it is quite - possible that two values: 0x80 and 0x00 might be wrong in this table. - To be exact: - some 0x80 could be 0x81 as well as some 0x00 could be 0x01. -*/ - -static const UINT8 lfo_noise_waveform[256] = { -0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A, -0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE, -0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3, -0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2, - -0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D, -0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24, -0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA, -0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB, - -0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5, -0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F, -0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79, -0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51, - -0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE, -0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92, -0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD, -0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55 -}; - - - - -/* save output as raw 16-bit sample */ -/* #define SAVE_SAMPLE */ -/* #define SAVE_SEPARATE_CHANNELS */ -#if defined SAVE_SAMPLE || defined SAVE_SEPARATE_CHANNELS -static FILE *sample[9]; -#endif - - - - -static void init_tables(void) -{ - signed int i,x,n; - double o,m; - - for (x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to closest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - n <<= 2; /* 13 bits here (as in real chip) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; - - for (i=1; i<13; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; - } - #if 0 - logerror("tl %04i", x*2); - for (i=0; i<13; i++) - logerror(", [%02i] %4i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]); - logerror("\n"); - #endif - } - /*logerror("TL_TAB_LEN = %i (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ - /*logerror("ENV_QUIET= %i\n",ENV_QUIET );*/ - - - for (i=0; i0.0) - o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ - else - o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ - - o = o / (ENV_STEP/4); - - n = (int)(2.0*o); - if (n&1) /* round to closest */ - n = (n>>1)+1; - else - n = n>>1; - - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); - /*logerror("sin [0x%4x]= %4i (tl_tab value=%8x)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/ - } - - - /* calculate d1l_tab table */ - for (i=0; i<16; i++) - { - m = (i!=15 ? i : i+16) * (4.0/ENV_STEP); /* every 3 'dB' except for all bits = 1 = 45+48 'dB' */ - d1l_tab[i] = m; - /*logerror("d1l_tab[%02x]=%08x\n",i,d1l_tab[i] );*/ - } - -#ifdef SAVE_SAMPLE - sample[8]=fopen("sampsum.pcm","wb"); -#endif -#ifdef SAVE_SEPARATE_CHANNELS - sample[0]=fopen("samp0.pcm","wb"); - sample[1]=fopen("samp1.pcm","wb"); - sample[2]=fopen("samp2.pcm","wb"); - sample[3]=fopen("samp3.pcm","wb"); - sample[4]=fopen("samp4.pcm","wb"); - sample[5]=fopen("samp5.pcm","wb"); - sample[6]=fopen("samp6.pcm","wb"); - sample[7]=fopen("samp7.pcm","wb"); -#endif -} - - -static void init_chip_tables(YM2151 *chip) -{ - int i,j; - double mult,phaseinc,Hz; - double scaler; - - scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq ); - if ( fabs( scaler - 1.0 ) < 0.0000001 ) scaler = 1.0; - /*logerror("scaler = %20.15f\n", scaler);*/ - - - /* this loop calculates Hertz values for notes from c-0 to b-7 */ - /* including 64 'cents' (100/64 that is 1.5625 of real cent) per note */ - /* i*100/64/1200 is equal to i/768 */ - - /* real chip works with 10 bits fixed point values (10.10) */ - mult = (1<<(FREQ_SH-10)); /* -10 because phaseinc_rom table values are already in 10.10 format */ - - for (i=0; i<768; i++) - { - /* 3.4375 Hz is note A; C# is 4 semitones higher */ - Hz = 1000; -#if 0 -/* Hz is close, but not perfect */ - //Hz = scaler * 3.4375 * pow (2, (i + 4 * 64 ) / 768.0 ); - /* calculate phase increment */ - phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq; -#endif - - phaseinc = phaseinc_rom[i]; /* real chip phase increment */ - phaseinc *= scaler; /* adjust */ - - - /* octave 2 - reference octave */ - chip->freq[ 768+2*768+i ] = ((int)(phaseinc*mult)) & 0xffffffc0; /* adjust to X.10 fixed point */ - /* octave 0 and octave 1 */ - for (j=0; j<2; j++) - { - chip->freq[768 + j*768 + i] = (chip->freq[ 768+2*768+i ] >> (2-j) ) & 0xffffffc0; /* adjust to X.10 fixed point */ - } - /* octave 3 to 7 */ - for (j=3; j<8; j++) - { - chip->freq[768 + j*768 + i] = chip->freq[ 768+2*768+i ] << (j-2); - } - } - - /* octave -1 (all equal to: oct 0, _KC_00_, _KF_00_) */ - for (i=0; i<768; i++) - { - chip->freq[ 0*768 + i ] = chip->freq[1*768+0]; - } - - /* octave 8 and 9 (all equal to: oct 7, _KC_14_, _KF_63_) */ - for (j=8; j<10; j++) - { - for (i=0; i<768; i++) - { - chip->freq[768+ j*768 + i ] = chip->freq[768 + 8*768 -1]; - } - } - - mult = (1<clock/64.0) ) / (double)(1<<20); - - /*calculate phase increment*/ - phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq; - - /*positive and negative values*/ - chip->dt1_freq[ (j+0)*32 + i ] = phaseinc * mult; - chip->dt1_freq[ (j+4)*32 + i ] = -chip->dt1_freq[ (j+0)*32 + i ]; - -#if 0 - { - int x = j*32 + i; - pom = (double)chip->dt1_freq[x] / mult; - pom = pom * (double)chip->sampfreq / (double)SIN_LEN; - logerror("DT1(%03i)[%02i %02i][%08x]= real %19.15f Hz emul %19.15f Hz\n", - x, j, i, chip->dt1_freq[x], Hz, pom); - } -#endif - } - } - - - /* calculate noise periods table */ - scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq ); - if ( fabs( scaler - 1.0 ) < 0.0000001 ) scaler = 1.0; - for (i=0; i<32; i++) - { - j = (i!=31 ? i : 30); /* rate 30 and 31 are the same */ - j = 32-j; - j = (65536.0 / (double)(j*32.0)); /* number of samples per one shift of the shift register */ - /*chip->noise_tab[i] = j * 64;*/ /* number of chip clock cycles per one shift */ - chip->noise_tab[i] = j * 64 * scaler; - /*logerror("noise_tab[%02x]=%08x\n", i, chip->noise_tab[i]);*/ - } -} - -#define KEY_ON(op, key_set){ \ - if (!(op)->key) \ - { \ - (op)->phase = 0; /* clear phase */ \ - (op)->state = EG_ATT; /* KEY ON = attack */ \ - (op)->volume += (~(op)->volume * \ - (eg_inc[(op)->eg_sel_ar + ((PSG->eg_cnt>>(op)->eg_sh_ar)&7)]) \ - ) >>4; \ - if ((op)->volume <= MIN_ATT_INDEX) \ - { \ - (op)->volume = MIN_ATT_INDEX; \ - (op)->state = EG_DEC; \ - } \ - } \ - (op)->key |= key_set; \ -} - -#define KEY_OFF(op, key_clr){ \ - if ((op)->key) \ - { \ - (op)->key &= key_clr; \ - if (!(op)->key) \ - { \ - if ((op)->state>EG_REL) \ - (op)->state = EG_REL;/* KEY OFF = release */\ - } \ - } \ -} - -INLINE void envelope_KONKOFF(YM2151 *PSG, YM2151Operator * op, int v) -{ - if (v&0x08) /* M1 */ - KEY_ON (op+0, 1) - else - KEY_OFF(op+0,~1) - - if (v&0x20) /* M2 */ - KEY_ON (op+1, 1) - else - KEY_OFF(op+1,~1) - - if (v&0x10) /* C1 */ - KEY_ON (op+2, 1) - else - KEY_OFF(op+2,~1) - - if (v&0x40) /* C2 */ - KEY_ON (op+3, 1) - else - KEY_OFF(op+3,~1) -} - - -INLINE void set_connect(YM2151 *PSG, YM2151Operator *om1, int cha, int v) -{ - YM2151Operator *om2 = om1+1; - YM2151Operator *oc1 = om1+2; - - /* set connect algorithm */ - - /* MEM is simply one sample delay */ - - switch( v&7 ) - { - case 0: - /* M1---C1---MEM---M2---C2---OUT */ - om1->connect = &PSG->c1; - oc1->connect = &PSG->mem; - om2->connect = &PSG->c2; - om1->mem_connect = &PSG->m2; - break; - - case 1: - /* M1------+-MEM---M2---C2---OUT */ - /* C1-+ */ - om1->connect = &PSG->mem; - oc1->connect = &PSG->mem; - om2->connect = &PSG->c2; - om1->mem_connect = &PSG->m2; - break; - - case 2: - /* M1-----------------+-C2---OUT */ - /* C1---MEM---M2-+ */ - om1->connect = &PSG->c2; - oc1->connect = &PSG->mem; - om2->connect = &PSG->c2; - om1->mem_connect = &PSG->m2; - break; - - case 3: - /* M1---C1---MEM------+-C2---OUT */ - /* M2-+ */ - om1->connect = &PSG->c1; - oc1->connect = &PSG->mem; - om2->connect = &PSG->c2; - om1->mem_connect = &PSG->c2; - break; - - case 4: - /* M1---C1-+-OUT */ - /* M2---C2-+ */ - /* MEM: not used */ - om1->connect = &PSG->c1; - oc1->connect = &PSG->chanout[cha]; - om2->connect = &PSG->c2; - om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */ - break; - - case 5: - /* +----C1----+ */ - /* M1-+-MEM---M2-+-OUT */ - /* +----C2----+ */ - om1->connect = 0; /* special mark */ - oc1->connect = &PSG->chanout[cha]; - om2->connect = &PSG->chanout[cha]; - om1->mem_connect = &PSG->m2; - break; - - case 6: - /* M1---C1-+ */ - /* M2-+-OUT */ - /* C2-+ */ - /* MEM: not used */ - om1->connect = &PSG->c1; - oc1->connect = &PSG->chanout[cha]; - om2->connect = &PSG->chanout[cha]; - om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */ - break; - - case 7: - /* M1-+ */ - /* C1-+-OUT */ - /* M2-+ */ - /* C2-+ */ - /* MEM: not used*/ - om1->connect = &PSG->chanout[cha]; - oc1->connect = &PSG->chanout[cha]; - om2->connect = &PSG->chanout[cha]; - om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */ - break; - } -} - - -INLINE void refresh_EG(YM2151Operator * op) -{ - UINT32 kc; - UINT32 v; - - kc = op->kc; - - /* v = 32 + 2*RATE + RKS = max 126 */ - - v = kc >> op->ks; - if ((op->ar+v) < 32+62) - { - op->eg_sh_ar = eg_rate_shift [op->ar + v ]; - op->eg_sel_ar = eg_rate_select[op->ar + v ]; - } - else - { - op->eg_sh_ar = 0; - op->eg_sel_ar = 17*RATE_STEPS; - } - op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; - op->eg_sel_d1r= eg_rate_select[op->d1r + v]; - op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; - op->eg_sel_d2r= eg_rate_select[op->d2r + v]; - op->eg_sh_rr = eg_rate_shift [op->rr + v]; - op->eg_sel_rr = eg_rate_select[op->rr + v]; - - - op+=1; - - v = kc >> op->ks; - if ((op->ar+v) < 32+62) - { - op->eg_sh_ar = eg_rate_shift [op->ar + v ]; - op->eg_sel_ar = eg_rate_select[op->ar + v ]; - } - else - { - op->eg_sh_ar = 0; - op->eg_sel_ar = 17*RATE_STEPS; - } - op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; - op->eg_sel_d1r= eg_rate_select[op->d1r + v]; - op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; - op->eg_sel_d2r= eg_rate_select[op->d2r + v]; - op->eg_sh_rr = eg_rate_shift [op->rr + v]; - op->eg_sel_rr = eg_rate_select[op->rr + v]; - - op+=1; - - v = kc >> op->ks; - if ((op->ar+v) < 32+62) - { - op->eg_sh_ar = eg_rate_shift [op->ar + v ]; - op->eg_sel_ar = eg_rate_select[op->ar + v ]; - } - else - { - op->eg_sh_ar = 0; - op->eg_sel_ar = 17*RATE_STEPS; - } - op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; - op->eg_sel_d1r= eg_rate_select[op->d1r + v]; - op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; - op->eg_sel_d2r= eg_rate_select[op->d2r + v]; - op->eg_sh_rr = eg_rate_shift [op->rr + v]; - op->eg_sel_rr = eg_rate_select[op->rr + v]; - - op+=1; - - v = kc >> op->ks; - if ((op->ar+v) < 32+62) - { - op->eg_sh_ar = eg_rate_shift [op->ar + v ]; - op->eg_sel_ar = eg_rate_select[op->ar + v ]; - } - else - { - op->eg_sh_ar = 0; - op->eg_sel_ar = 17*RATE_STEPS; - } - op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; - op->eg_sel_d1r= eg_rate_select[op->d1r + v]; - op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; - op->eg_sel_d2r= eg_rate_select[op->d2r + v]; - op->eg_sh_rr = eg_rate_shift [op->rr + v]; - op->eg_sel_rr = eg_rate_select[op->rr + v]; -} - - -/* write a register on YM2151 chip number 'n' */ -void ym2151_write_reg(void *_chip, int r, int v) -{ - YM2151 *chip = (YM2151 *)_chip; - YM2151Operator *op = &chip->oper[ (r&0x07)*4+((r&0x18)>>3) ]; - - /* adjust bus to 8 bits */ - r &= 0xff; - v &= 0xff; - -#if 0 - /* There is no info on what YM2151 really does when busy flag is set */ - if ( chip->status & 0x80 ) return; - timer_set ( attotime::from_hz(chip->clock) * 64, chip, 0, timer_callback_chip_busy); - chip->status |= 0x80; /* set busy flag for 64 chip clock cycles */ -#endif - - - switch(r & 0xe0) - { - case 0x00: - switch(r){ - case 0x01: /* LFO reset(bit 1), Test Register (other bits) */ - chip->test = v; - if (v&2) chip->lfo_phase = 0; - break; - - case 0x08: - envelope_KONKOFF(chip, &chip->oper[ (v&7)*4 ], v ); - break; - - case 0x0f: /* noise mode enable, noise period */ - chip->noise = v; - chip->noise_f = chip->noise_tab[ v & 0x1f ]; - break; - - case 0x10: /* timer A hi */ - break; - - case 0x11: /* timer A low */ - break; - - case 0x12: /* timer B */ - break; - - case 0x14: /* CSM, irq flag reset, irq enable, timer start/stop */ - - chip->irq_enable = v; /* bit 3-timer B, bit 2-timer A, bit 7 - CSM */ - - break; - - case 0x18: /* LFO frequency */ - { - chip->lfo_overflow = ( 1 << ((15-(v>>4))+3) ) * (1<lfo_counter_add = 0x10 + (v & 0x0f); - } - break; - - case 0x19: /* PMD (bit 7==1) or AMD (bit 7==0) */ - if (v&0x80) - chip->pmd = v & 0x7f; - else - chip->amd = v & 0x7f; - break; - - case 0x1b: /* CT2, CT1, LFO waveform */ - chip->ct = v >> 6; - chip->lfo_wsel = v & 3; - break; - - default: - /*logerror("YM2151 Write %02x to undocumented register #%02x\n",v,r);*/ - break; - } - break; - - case 0x20: - op = &chip->oper[ (r&7) * 4 ]; - switch(r & 0x18) - { - case 0x00: /* RL enable, Feedback, Connection */ - op->fb_shift = ((v>>3)&7) ? ((v>>3)&7)+6:0; - chip->pan[ (r&7)*2 ] = (v & 0x40) ? ~0 : 0; - chip->pan[ (r&7)*2 +1 ] = (v & 0x80) ? ~0 : 0; - chip->connect[r&7] = v&7; - set_connect(chip, op, r&7, v&7); - break; - - case 0x08: /* Key Code */ - v &= 0x7f; - if (v != (int)(op->kc)) - { - UINT32 kc, kc_channel; - - kc_channel = (v - (v>>2))*64; - kc_channel += 768; - kc_channel |= (op->kc_i & 63); - - (op+0)->kc = v; - (op+0)->kc_i = kc_channel; - (op+1)->kc = v; - (op+1)->kc_i = kc_channel; - (op+2)->kc = v; - (op+2)->kc_i = kc_channel; - (op+3)->kc = v; - (op+3)->kc_i = kc_channel; - - kc = v>>2; - - (op+0)->dt1 = chip->dt1_freq[ (op+0)->dt1_i + kc ]; - (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; - - (op+1)->dt1 = chip->dt1_freq[ (op+1)->dt1_i + kc ]; - (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; - - (op+2)->dt1 = chip->dt1_freq[ (op+2)->dt1_i + kc ]; - (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; - - (op+3)->dt1 = chip->dt1_freq[ (op+3)->dt1_i + kc ]; - (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; - - refresh_EG( op ); - } - break; - - case 0x10: /* Key Fraction */ - v >>= 2; - if (v != (int)(op->kc_i & 63)) - { - UINT32 kc_channel; - - kc_channel = v; - kc_channel |= (op->kc_i & ~63); - - (op+0)->kc_i = kc_channel; - (op+1)->kc_i = kc_channel; - (op+2)->kc_i = kc_channel; - (op+3)->kc_i = kc_channel; - - (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; - (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; - (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; - (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; - } - break; - - case 0x18: /* PMS, AMS */ - op->pms = (v>>4) & 7; - op->ams = (v & 3); - break; - } - break; - - case 0x40: /* DT1, MUL */ - { - UINT32 olddt1_i = op->dt1_i; - UINT32 oldmul = op->mul; - - op->dt1_i = (v&0x70)<<1; - op->mul = (v&0x0f) ? (v&0x0f)<<1: 1; - - if (olddt1_i != op->dt1_i) - op->dt1 = chip->dt1_freq[ op->dt1_i + (op->kc>>2) ]; - - if ( (olddt1_i != op->dt1_i) || (oldmul != op->mul) ) - op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1; - } - break; - - case 0x60: /* TL */ - op->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ - break; - - case 0x80: /* KS, AR */ - { - UINT32 oldks = op->ks; - UINT32 oldar = op->ar; - - op->ks = 5-(v>>6); - op->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; - - if ( (op->ar != oldar) || (op->ks != oldks) ) - { - if ((op->ar + (op->kc>>op->ks)) < 32+62) - { - op->eg_sh_ar = eg_rate_shift [op->ar + (op->kc>>op->ks) ]; - op->eg_sel_ar = eg_rate_select[op->ar + (op->kc>>op->ks) ]; - } - else - { - op->eg_sh_ar = 0; - op->eg_sel_ar = 17*RATE_STEPS; - } - } - - if (op->ks != oldks) - { - op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ]; - op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ]; - op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ]; - op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ]; - op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ]; - op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ]; - } - } - break; - - case 0xa0: /* LFO AM enable, D1R */ - op->AMmask = (v&0x80) ? ~0 : 0; - op->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; - op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ]; - op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ]; - break; - - case 0xc0: /* DT2, D2R */ - { - UINT32 olddt2 = op->dt2; - op->dt2 = dt2_tab[ v>>6 ]; - if (op->dt2 != olddt2) - op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1; - } - op->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; - op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ]; - op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ]; - break; - - case 0xe0: /* D1L, RR */ - op->d1l = d1l_tab[ v>>4 ]; - op->rr = 34 + ((v&0x0f)<<2); - op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ]; - op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ]; - break; - } -} - - -/* -* Initialize YM2151 emulator(s). -* -* 'num' is the number of virtual YM2151's to allocate -* 'clock' is the chip clock in Hz -* 'rate' is sampling rate -*/ -void * ym2151_init(int clock, int rate) -{ - YM2151 *PSG; - - PSG = (YM2151 *) malloc(sizeof(YM2151)); - - memset(PSG, 0, sizeof(YM2151)); - - init_tables(); - - PSG->clock = clock; - /*rate = clock/64;*/ - PSG->sampfreq = rate ? rate : 44100; /* avoid division by 0 in init_chip_tables() */ - init_chip_tables( PSG ); - - PSG->lfo_timer_add = (1<sampfreq; - - PSG->eg_timer_add = (1<sampfreq; - PSG->eg_timer_overflow = ( 3 ) * (1<eg_timer_add, PSG->eg_timer_overflow);*/ - - ym2151_reset_chip(PSG); - /*logerror("YM2151[init] clock=%i sampfreq=%i\n", PSG->clock, PSG->sampfreq);*/ - - return PSG; -} - - - -void ym2151_shutdown(void *_chip) -{ - YM2151 *chip = (YM2151 *)_chip; - - free (chip); - -#ifdef SAVE_SAMPLE - fclose(sample[8]); -#endif -#ifdef SAVE_SEPARATE_CHANNELS - fclose(sample[0]); - fclose(sample[1]); - fclose(sample[2]); - fclose(sample[3]); - fclose(sample[4]); - fclose(sample[5]); - fclose(sample[6]); - fclose(sample[7]); -#endif -} - - - -/* -* Reset chip number 'n'. -*/ -void ym2151_reset_chip(void *_chip) -{ - int i; - YM2151 *chip = (YM2151 *)_chip; - - - /* initialize hardware registers */ - for (i=0; i<32; i++) - { - memset(&chip->oper[i],'\0',sizeof(YM2151Operator)); - chip->oper[i].volume = MAX_ATT_INDEX; - chip->oper[i].kc_i = 768; /* min kc_i value */ - } - - chip->eg_timer = 0; - chip->eg_cnt = 0; - - chip->lfo_timer = 0; - chip->lfo_counter= 0; - chip->lfo_phase = 0; - chip->lfo_wsel = 0; - chip->pmd = 0; - chip->amd = 0; - chip->lfa = 0; - chip->lfp = 0; - - chip->test= 0; - - chip->irq_enable = 0; - - chip->noise = 0; - chip->noise_rng = 0; - chip->noise_p = 0; - chip->noise_f = chip->noise_tab[0]; - - chip->csm_req = 0; - chip->status = 0; - - ym2151_write_reg(chip, 0x1b, 0); /* only because of CT1, CT2 output pins */ - ym2151_write_reg(chip, 0x18, 0); /* set LFO frequency */ - for (i=0x20; i<0x100; i++) /* set the operators */ - { - ym2151_write_reg(chip, i, 0); - } -} - - - -INLINE signed int op_calc(YM2151Operator * OP, unsigned int env, signed int pm) -{ - UINT32 p; - - - p = (env<<3) + sin_tab[ ( ((signed int)((OP->phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; - - if (p >= TL_TAB_LEN) - return 0; - - return tl_tab[p]; -} - -INLINE signed int op_calc1(YM2151Operator * OP, unsigned int env, signed int pm) -{ - UINT32 p; - INT32 i; - - - i = (OP->phase & ~FREQ_MASK) + pm; - -/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, OP->phase>>FREQ_SH, pm);*/ - - p = (env<<3) + sin_tab[ (i>>FREQ_SH) & SIN_MASK]; - -/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ - - if (p >= TL_TAB_LEN) - return 0; - - return tl_tab[p]; -} - - - -#define volume_calc(OP) ((OP)->tl + ((UINT32)(OP)->volume) + (AM & (OP)->AMmask)) - -INLINE void chan_calc(YM2151 *PSG, unsigned int chan) -{ - YM2151Operator *op; - unsigned int env; - UINT32 AM = 0; - - PSG->m2 = PSG->c1 = PSG->c2 = PSG->mem = 0; - op = &PSG->oper[chan*4]; /* M1 */ - - *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ - - if (op->ams) - AM = PSG->lfa << (op->ams-1); - env = volume_calc(op); - { - INT32 out = op->fb_out_prev + op->fb_out_curr; - op->fb_out_prev = op->fb_out_curr; - - if (!op->connect) - /* algorithm 5 */ - PSG->mem = PSG->c1 = PSG->c2 = op->fb_out_prev; - else - /* other algorithms */ - *op->connect = op->fb_out_prev; - - op->fb_out_curr = 0; - if (env < ENV_QUIET) - { - if (!op->fb_shift) - out=0; - op->fb_out_curr = op_calc1(op, env, (out<fb_shift) ); - } - } - - env = volume_calc(op+1); /* M2 */ - if (env < ENV_QUIET) - *(op+1)->connect += op_calc(op+1, env, PSG->m2); - - env = volume_calc(op+2); /* C1 */ - if (env < ENV_QUIET) - *(op+2)->connect += op_calc(op+2, env, PSG->c1); - - env = volume_calc(op+3); /* C2 */ - if (env < ENV_QUIET) - PSG->chanout[chan] += op_calc(op+3, env, PSG->c2); - - /* M1 */ - op->mem_value = PSG->mem; -} - -INLINE void chan7_calc(YM2151 *PSG) -{ - YM2151Operator *op; - unsigned int env; - UINT32 AM = 0; - - PSG->m2 = PSG->c1 = PSG->c2 = PSG->mem = 0; - op = &PSG->oper[7*4]; /* M1 */ - - *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ - - if (op->ams) - AM = PSG->lfa << (op->ams-1); - env = volume_calc(op); - { - INT32 out = op->fb_out_prev + op->fb_out_curr; - op->fb_out_prev = op->fb_out_curr; - - if (!op->connect) - /* algorithm 5 */ - PSG->mem = PSG->c1 = PSG->c2 = op->fb_out_prev; - else - /* other algorithms */ - *op->connect = op->fb_out_prev; - - op->fb_out_curr = 0; - if (env < ENV_QUIET) - { - if (!op->fb_shift) - out=0; - op->fb_out_curr = op_calc1(op, env, (out<fb_shift) ); - } - } - - env = volume_calc(op+1); /* M2 */ - if (env < ENV_QUIET) - *(op+1)->connect += op_calc(op+1, env, PSG->m2); - - env = volume_calc(op+2); /* C1 */ - if (env < ENV_QUIET) - *(op+2)->connect += op_calc(op+2, env, PSG->c1); - - env = volume_calc(op+3); /* C2 */ - if (PSG->noise & 0x80) - { - UINT32 noiseout; - - noiseout = 0; - if (env < 0x3ff) - noiseout = (env ^ 0x3ff) * 2; /* range of the YM2151 noise output is -2044 to 2040 */ - PSG->chanout[7] += ((PSG->noise_rng&0x10000) ? noiseout: -noiseout); /* bit 16 -> output */ - } - else - { - if (env < ENV_QUIET) - PSG->chanout[7] += op_calc(op+3, env, PSG->c2); - } - /* M1 */ - op->mem_value = PSG->mem; -} - - - - - - -/* -The 'rate' is calculated from following formula (example on decay rate): - rks = notecode after key scaling (a value from 0 to 31) - DR = value written to the chip register - rate = 2*DR + rks; (max rate = 2*31+31 = 93) -Four MSBs of the 'rate' above are the 'main' rate (from 00 to 15) -Two LSBs of the 'rate' above are the value 'x' (the shape type). -(eg. '11 2' means that 'rate' is 11*4+2=46) - -NOTE: A 'sample' in the description below is actually 3 output samples, -thats because the Envelope Generator clock is equal to internal_clock/3. - -Single '-' (minus) character in the diagrams below represents one sample -on the output; this is for rates 11 x (11 0, 11 1, 11 2 and 11 3) - -these 'main' rates: -00 x: single '-' = 2048 samples; (ie. level can change every 2048 samples) -01 x: single '-' = 1024 samples; -02 x: single '-' = 512 samples; -03 x: single '-' = 256 samples; -04 x: single '-' = 128 samples; -05 x: single '-' = 64 samples; -06 x: single '-' = 32 samples; -07 x: single '-' = 16 samples; -08 x: single '-' = 8 samples; -09 x: single '-' = 4 samples; -10 x: single '-' = 2 samples; -11 x: single '-' = 1 sample; (ie. level can change every 1 sample) - -Shapes for rates 11 x look like this: -rate: step: -11 0 01234567 - -level: -0 -- -1 -- -2 -- -3 -- - -rate: step: -11 1 01234567 - -level: -0 -- -1 -- -2 - -3 - -4 -- - -rate: step: -11 2 01234567 - -level: -0 -- -1 - -2 - -3 -- -4 - -5 - - -rate: step: -11 3 01234567 - -level: -0 -- -1 - -2 - -3 - -4 - -5 - -6 - - - -For rates 12 x, 13 x, 14 x and 15 x output level changes on every -sample - this means that the waveform looks like this: (but the level -changes by different values on different steps) -12 3 01234567 - -0 - -2 - -4 - -8 - -10 - -12 - -14 - -18 - -20 - - -Notes about the timing: ----------------------- - -1. Synchronism - -Output level of each two (or more) voices running at the same 'main' rate -(eg 11 0 and 11 1 in the diagram below) will always be changing in sync, -even if there're started with some delay. - -Note that, in the diagram below, the decay phase in channel 0 starts at -sample #2, while in channel 1 it starts at sample #6. Anyway, both channels -will always change their levels at exactly the same (following) samples. - -(S - start point of this channel, A-attack phase, D-decay phase): - -step: -01234567012345670123456 - -channel 0: - -- - | -- - | - - | - - | -- - | -- -| -- -| - -| - -| -- -AADDDDDDDDDDDDDDDD -S - -01234567012345670123456 -channel 1: - - - | - - | -- - | -- - | -- - | - - | - - | -- - | -- - | -- - AADDDDDDDDDDDDDDDD - S -01234567012345670123456 - - -2. Shifted (delayed) synchronism - -Output of each two (or more) voices running at different 'main' rate -(9 1, 10 1 and 11 1 in the diagrams below) will always be changing -in 'delayed-sync' (even if there're started with some delay as in "1.") - -Note that the shapes are delayed by exactly one sample per one 'main' rate -increment. (Normally one would expect them to start at the same samples.) - -See diagram below (* - start point of the shape). - -cycle: -0123456701234567012345670123456701234567012345670123456701234567 - -rate 09 1 -*------- - -------- - ---- - ---- - -------- - *------- - | -------- - | ---- - | ---- - | -------- -rate 10 1 | --- | - *--- | - ---- | - -- | - -- | - ---- | - *--- | - | ---- | - | -- | | <- one step (two samples) delay between 9 1 and 10 1 - | -- | | - | ----| - | *--- - | ---- - | -- - | -- - | ---- -rate 11 1 | -- | - -- | - *- | - -- | - - | - - | - -- | - *- | - -- | - - || <- one step (one sample) delay between 10 1 and 11 1 - - || - --| - *- - -- - - - - - -- - *- - -- - - - - - -- -*/ - -INLINE void advance_eg(YM2151 *PSG) -{ - YM2151Operator *op; - unsigned int i; - - - - PSG->eg_timer += PSG->eg_timer_add; - - while (PSG->eg_timer >= PSG->eg_timer_overflow) - { - PSG->eg_timer -= PSG->eg_timer_overflow; - - PSG->eg_cnt++; - - /* envelope generator */ - op = &PSG->oper[0]; /* CH 0 M1 */ - i = 32; - do - { - switch(op->state) - { - case EG_ATT: /* attack phase */ - if ( !(PSG->eg_cnt & ((1<eg_sh_ar)-1) ) ) - { - op->volume += (~op->volume * - (eg_inc[op->eg_sel_ar + ((PSG->eg_cnt>>op->eg_sh_ar)&7)]) - ) >>4; - - if (op->volume <= MIN_ATT_INDEX) - { - op->volume = MIN_ATT_INDEX; - op->state = EG_DEC; - } - - } - break; - - case EG_DEC: /* decay phase */ - if ( !(PSG->eg_cnt & ((1<eg_sh_d1r)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_d1r + ((PSG->eg_cnt>>op->eg_sh_d1r)&7)]; - - if ( op->volume >= (INT32)(op->d1l) ) - op->state = EG_SUS; - - } - break; - - case EG_SUS: /* sustain phase */ - if ( !(PSG->eg_cnt & ((1<eg_sh_d2r)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_d2r + ((PSG->eg_cnt>>op->eg_sh_d2r)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - - } - break; - - case EG_REL: /* release phase */ - if ( !(PSG->eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((PSG->eg_cnt>>op->eg_sh_rr)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - - } - break; - } - op++; - i--; - }while (i); - } -} - - -INLINE void advance(YM2151 *PSG) -{ - YM2151Operator *op; - unsigned int i; - int a,p; - - /* LFO */ - if (PSG->test&2) - PSG->lfo_phase = 0; - else - { - PSG->lfo_timer += PSG->lfo_timer_add; - if (PSG->lfo_timer >= PSG->lfo_overflow) - { - PSG->lfo_timer -= PSG->lfo_overflow; - PSG->lfo_counter += PSG->lfo_counter_add; - PSG->lfo_phase += (PSG->lfo_counter>>4); - PSG->lfo_phase &= 255; - PSG->lfo_counter &= 15; - } - } - - i = PSG->lfo_phase; - /* calculate LFO AM and PM waveform value (all verified on real chip, except for noise algorithm which is impossible to analyse)*/ - switch (PSG->lfo_wsel) - { - case 0: - /* saw */ - /* AM: 255 down to 0 */ - /* PM: 0 to 127, -127 to 0 (at PMD=127: LFP = 0 to 126, -126 to 0) */ - a = 255 - i; - if (i<128) - p = i; - else - p = i - 255; - break; - case 1: - /* square */ - /* AM: 255, 0 */ - /* PM: 128,-128 (LFP = exactly +PMD, -PMD) */ - if (i<128) - { - a = 255; - p = 128; - } - else - { - a = 0; - p = -128; - } - break; - case 2: - /* triangle */ - /* AM: 255 down to 1 step -2; 0 up to 254 step +2 */ - /* PM: 0 to 126 step +2, 127 to 1 step -2, 0 to -126 step -2, -127 to -1 step +2*/ - if (i<128) - a = 255 - (i*2); - else - a = (i*2) - 256; - - if (i<64) /* i = 0..63 */ - p = i*2; /* 0 to 126 step +2 */ - else if (i<128) /* i = 64..127 */ - p = 255 - i*2; /* 127 to 1 step -2 */ - else if (i<192) /* i = 128..191 */ - p = 256 - i*2; /* 0 to -126 step -2*/ - else /* i = 192..255 */ - p = i*2 - 511; /*-127 to -1 step +2*/ - break; - case 3: - default: /*keep the compiler happy*/ - /* random */ - /* the real algorithm is unknown !!! - We just use a snapshot of data from real chip */ - - /* AM: range 0 to 255 */ - /* PM: range -128 to 127 */ - - a = lfo_noise_waveform[i]; - p = a-128; - break; - } - PSG->lfa = a * PSG->amd / 128; - PSG->lfp = p * PSG->pmd / 128; - - - /* The Noise Generator of the YM2151 is 17-bit shift register. - * Input to the bit16 is negated (bit0 XOR bit3) (EXNOR). - * Output of the register is negated (bit0 XOR bit3). - * Simply use bit16 as the noise output. - */ - PSG->noise_p += PSG->noise_f; - i = (PSG->noise_p>>16); /* number of events (shifts of the shift register) */ - PSG->noise_p &= 0xffff; - while (i) - { - UINT32 j; - j = ( (PSG->noise_rng ^ (PSG->noise_rng>>3) ) & 1) ^ 1; - PSG->noise_rng = (j<<16) | (PSG->noise_rng>>1); - i--; - } - - - /* phase generator */ - op = &PSG->oper[0]; /* CH 0 M1 */ - i = 8; - do - { - if (op->pms) /* only when phase modulation from LFO is enabled for this channel */ - { - INT32 mod_ind = PSG->lfp; /* -128..+127 (8bits signed) */ - if (op->pms < 6) - mod_ind >>= (6 - op->pms); - else - mod_ind <<= (op->pms - 5); - - if (mod_ind) - { - UINT32 kc_channel = op->kc_i + mod_ind; - (op+0)->phase += ( (PSG->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; - (op+1)->phase += ( (PSG->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; - (op+2)->phase += ( (PSG->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; - (op+3)->phase += ( (PSG->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; - } - else /* phase modulation from LFO is equal to zero */ - { - (op+0)->phase += (op+0)->freq; - (op+1)->phase += (op+1)->freq; - (op+2)->phase += (op+2)->freq; - (op+3)->phase += (op+3)->freq; - } - } - else /* phase modulation from LFO is disabled */ - { - (op+0)->phase += (op+0)->freq; - (op+1)->phase += (op+1)->freq; - (op+2)->phase += (op+2)->freq; - (op+3)->phase += (op+3)->freq; - } - - op+=4; - i--; - }while (i); - - - /* CSM is calculated *after* the phase generator calculations (verified on real chip) - * CSM keyon line seems to be ORed with the KO line inside of the chip. - * The result is that it only works when KO (register 0x08) is off, ie. 0 - * - * Interesting effect is that when timer A is set to 1023, the KEY ON happens - * on every sample, so there is no KEY OFF at all - the result is that - * the sound played is the same as after normal KEY ON. - */ - - if (PSG->csm_req) /* CSM KEYON/KEYOFF seqeunce request */ - { - if (PSG->csm_req==2) /* KEY ON */ - { - op = &PSG->oper[0]; /* CH 0 M1 */ - i = 32; - do - { - KEY_ON(op, 2); - op++; - i--; - }while (i); - PSG->csm_req = 1; - } - else /* KEY OFF */ - { - op = &PSG->oper[0]; /* CH 0 M1 */ - i = 32; - do - { - KEY_OFF(op,~2); - op++; - i--; - }while (i); - PSG->csm_req = 0; - } - } -} - -/* first macro saves left and right channels to mono file */ -/* second macro saves left and right channels to stereo file */ -#if 0 /*MONO*/ - #ifdef SAVE_SEPARATE_CHANNELS - #define SAVE_SINGLE_CHANNEL(j) \ - { signed int pom= -(chanout[j] & PSG->pan[j*2]); \ - if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ - fputc((unsigned short)pom&0xff,sample[j]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ - } - #else - #define SAVE_SINGLE_CHANNEL(j) - #endif -#else /*STEREO*/ - #ifdef SAVE_SEPARATE_CHANNELS - #define SAVE_SINGLE_CHANNEL(j) \ - { signed int pom = -(chanout[j] & PSG->pan[j*2]); \ - if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ - fputc((unsigned short)pom&0xff,sample[j]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ - pom = -(chanout[j] & PSG->pan[j*2+1]); \ - if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ - fputc((unsigned short)pom&0xff,sample[j]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ - } - #else - #define SAVE_SINGLE_CHANNEL(j) - #endif -#endif - -/* first macro saves left and right channels to mono file */ -/* second macro saves left and right channels to stereo file */ -#if 1 /*MONO*/ - #ifdef SAVE_SAMPLE - #define SAVE_ALL_CHANNELS \ - { signed int pom = outl; \ - /*pom = acc_calc(pom);*/ \ - /*fprintf(sample[8]," %i\n",pom);*/ \ - fputc((unsigned short)pom&0xff,sample[8]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ - } - #else - #define SAVE_ALL_CHANNELS - #endif -#else /*STEREO*/ - #ifdef SAVE_SAMPLE - #define SAVE_ALL_CHANNELS \ - { signed int pom = outl; \ - fputc((unsigned short)pom&0xff,sample[8]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ - pom = outr; \ - fputc((unsigned short)pom&0xff,sample[8]); \ - fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ - } - #else - #define SAVE_ALL_CHANNELS - #endif -#endif - - -/* Generate samples for one of the YM2151's -* -* 'num' is the number of virtual YM2151 -* '**buffers' is table of pointers to the buffers: left and right -* 'length' is the number of samples that should be generated -*/ -void ym2151_update_one(void *chip, SAMP **buffers, int length) -{ - YM2151 *PSG = (YM2151 *)chip; - signed int *chanout = PSG->chanout; - int i; - UINT32 mask = ~PSG->mask; - signed int outl,outr; - SAMP *bufL, *bufR; - - bufL = buffers[0]; - bufR = buffers[1]; - - for (i=0; ipan[0]; - if (mask & 1) outr += chanout[0] & PSG->pan[1]; - if (mask & 2) outl += (chanout[1] & PSG->pan[2]); - if (mask & 2) outr += (chanout[1] & PSG->pan[3]); - if (mask & 4) outl += (chanout[2] & PSG->pan[4]); - if (mask & 4) outr += (chanout[2] & PSG->pan[5]); - if (mask & 8) outl += (chanout[3] & PSG->pan[6]); - if (mask & 8) outr += (chanout[3] & PSG->pan[7]); - if (mask & 16) outl += (chanout[4] & PSG->pan[8]); - if (mask & 16) outr += (chanout[4] & PSG->pan[9]); - if (mask & 32) outl += (chanout[5] & PSG->pan[10]); - if (mask & 32) outr += (chanout[5] & PSG->pan[11]); - if (mask & 64) outl += (chanout[6] & PSG->pan[12]); - if (mask & 64) outr += (chanout[6] & PSG->pan[13]); - if (mask & 128) outl += (chanout[7] & PSG->pan[14]); - if (mask & 128) outr += (chanout[7] & PSG->pan[15]); - - outl >>= FINAL_SH; - outr >>= FINAL_SH; - if (outl > MAXOUT) outl = MAXOUT; - else if (outl < MINOUT) outl = MINOUT; - if (outr > MAXOUT) outr = MAXOUT; - else if (outr < MINOUT) outr = MINOUT; - ((SAMP*)bufL)[i] = (SAMP)outl; - ((SAMP*)bufR)[i] = (SAMP)outr; - - SAVE_ALL_CHANNELS - - advance(PSG); - } -} - -void ym2151_set_mask(void *_chip, UINT32 mask) -{ - YM2151 *PSG = (YM2151 *)_chip; - - PSG->mask = mask; -} +/***************************************************************************** +* +* Yamaha YM2151 driver (version 2.150 final beta) +* +******************************************************************************/ + +#include +#include + +#include "mamedef.h" +#include +#include +//#include "sndintrf.h" +//#include "streams.h" +#include "ym2151.h" + +#define NULL ((void *)0) + + +/* undef this to not use MAME timer system */ +//#define USE_MAME_TIMERS + +/*#define FM_EMU*/ +#ifdef FM_EMU + #ifdef USE_MAME_TIMERS + #undef USE_MAME_TIMERS + #endif +#endif + +//static char LOG_CYM_FILE = 0x00; +//static FILE * cymfile = NULL; + + +/* struct describing a single operator */ +typedef struct{ + UINT32 phase; /* accumulated operator phase */ + UINT32 freq; /* operator frequency count */ + INT32 dt1; /* current DT1 (detune 1 phase inc/decrement) value */ + UINT32 mul; /* frequency count multiply */ + UINT32 dt1_i; /* DT1 index * 32 */ + UINT32 dt2; /* current DT2 (detune 2) value */ + + signed int *connect; /* operator output 'direction' */ + + /* only M1 (operator 0) is filled with this data: */ + signed int *mem_connect; /* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + /* channel specific data; note: each operator number 0 contains channel specific data */ + UINT32 fb_shift; /* feedback shift value for operators 0 in each channel */ + INT32 fb_out_curr; /* operator feedback value (used only by operators 0) */ + INT32 fb_out_prev; /* previous feedback value (used only by operators 0) */ + UINT32 kc; /* channel KC (copied to all operators) */ + UINT32 kc_i; /* just for speedup */ + UINT32 pms; /* channel PMS */ + UINT32 ams; /* channel AMS */ + /* end of channel specific data */ + + UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ + UINT32 state; /* Envelope state: 4-attack(AR) 3-decay(D1R) 2-sustain(D2R) 1-release(RR) 0-off */ + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT32 tl; /* Total attenuation Level */ + INT32 volume; /* current envelope attenuation level */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT32 d1l; /* envelope switches to sustain state after reaching this level */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT32 key; /* 0=last key was KEY OFF, 1=last key was KEY ON */ + + UINT32 ks; /* key scale */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + + UINT32 reserved0; /**/ + UINT32 reserved1; /**/ + +} YM2151Operator; + + +typedef struct +{ + YM2151Operator oper[32]; /* the 32 operators */ + + UINT32 pan[16]; /* channels output masks (0xffffffff = enable) */ + UINT8 Muted[8]; /* used for muting */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */ + + UINT32 lfo_phase; /* accumulated LFO phase (0 to 255) */ + UINT32 lfo_timer; /* LFO timer */ + UINT32 lfo_timer_add; /* step of lfo_timer */ + UINT32 lfo_overflow; /* LFO generates new output when lfo_timer reaches this value */ + UINT32 lfo_counter; /* LFO phase increment counter */ + UINT32 lfo_counter_add; /* step of lfo_counter */ + UINT8 lfo_wsel; /* LFO waveform (0-saw, 1-square, 2-triangle, 3-random noise) */ + UINT8 amd; /* LFO Amplitude Modulation Depth */ + INT8 pmd; /* LFO Phase Modulation Depth */ + UINT32 lfa; /* LFO current AM output */ + INT32 lfp; /* LFO current PM output */ + + UINT8 test; /* TEST register */ + UINT8 ct; /* output control pins (bit1-CT2, bit0-CT1) */ + + UINT32 noise; /* noise enable/period register (bit 7 - noise enable, bits 4-0 - noise period */ + UINT32 noise_rng; /* 17 bit noise shift register */ + UINT32 noise_p; /* current noise 'phase'*/ + UINT32 noise_f; /* current noise period */ + + UINT32 csm_req; /* CSM KEY ON / KEY OFF sequence request */ + + UINT32 irq_enable; /* IRQ enable for timer B (bit 3) and timer A (bit 2); bit 7 - CSM mode (keyon to all slots, everytime timer A overflows) */ + UINT32 status; /* chip status (BUSY, IRQ Flags) */ + UINT8 connect[8]; /* channels connections */ + +#ifdef USE_MAME_TIMERS +/* ASG 980324 -- added for tracking timers */ + emu_timer *timer_A; + emu_timer *timer_B; + attotime timer_A_time[1024]; /* timer A times for MAME */ + attotime timer_B_time[256]; /* timer B times for MAME */ + int irqlinestate; +#else + UINT8 tim_A; /* timer A enable (0-disabled) */ + UINT8 tim_B; /* timer B enable (0-disabled) */ + INT32 tim_A_val; /* current value of timer A */ + INT32 tim_B_val; /* current value of timer B */ + UINT32 tim_A_tab[1024]; /* timer A deltas */ + UINT32 tim_B_tab[256]; /* timer B deltas */ +#endif + UINT32 timer_A_index; /* timer A index */ + UINT32 timer_B_index; /* timer B index */ + UINT32 timer_A_index_old; /* timer A previous index */ + UINT32 timer_B_index_old; /* timer B previous index */ + + /* Frequency-deltas to get the closest frequency possible. + * There are 11 octaves because of DT2 (max 950 cents over base frequency) + * and LFO phase modulation (max 800 cents below AND over base frequency) + * Summary: octave explanation + * 0 note code - LFO PM + * 1 note code + * 2 note code + * 3 note code + * 4 note code + * 5 note code + * 6 note code + * 7 note code + * 8 note code + * 9 note code + DT2 + LFO PM + * 10 note code + DT2 + LFO PM + */ + UINT32 freq[11*768]; /* 11 octaves, 768 'cents' per octave */ + + /* Frequency deltas for DT1. These deltas alter operator frequency + * after it has been taken from frequency-deltas table. + */ + INT32 dt1_freq[8*32]; /* 8 DT1 levels, 32 KC values */ + + UINT32 noise_tab[32]; /* 17bit Noise Generator periods */ + + //void (*irqhandler)(const device_config *device, int irq); /* IRQ function handler */ + //write8_device_func porthandler; /* port write function handler */ + + //const device_config *device; + unsigned int clock; /* chip clock in Hz (passed from 2151intf.c) */ + unsigned int sampfreq; /* sampling frequency in Hz (passed from 2151intf.c) */ +} YM2151; + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ +#define LFO_SH 10 /* 22.10 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + + +/* translate from D1L to volume index (16 D1L levels) */ +static UINT32 d1l_tab[16]; + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 dummy (infinite time) rates */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +/* DT2 defines offset in cents from base note +* +* This table defines offset in frequency-deltas table. +* User's Manual page 22 +* +* Values below were calculated using formula: value = orig.val / 1.5625 +* +* DT2=0 DT2=1 DT2=2 DT2=3 +* 0 600 781 950 +*/ +static const UINT32 dt2_tab[4] = { 0, 384, 500, 608 }; + +/* DT1 defines offset in Hertz from base note +* This table is converted while initialization... +* Detune table shown in YM2151 User's Manual is wrong (verified on the real chip) +*/ + +static const UINT8 dt1_tab[4*32] = { /* 4*32 DT1 values */ +/* DT1=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + +/* DT1=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + +/* DT1=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, + +/* DT1=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + +static const UINT16 phaseinc_rom[768]={ +1299,1300,1301,1302,1303,1304,1305,1306,1308,1309,1310,1311,1313,1314,1315,1316, +1318,1319,1320,1321,1322,1323,1324,1325,1327,1328,1329,1330,1332,1333,1334,1335, +1337,1338,1339,1340,1341,1342,1343,1344,1346,1347,1348,1349,1351,1352,1353,1354, +1356,1357,1358,1359,1361,1362,1363,1364,1366,1367,1368,1369,1371,1372,1373,1374, +1376,1377,1378,1379,1381,1382,1383,1384,1386,1387,1388,1389,1391,1392,1393,1394, +1396,1397,1398,1399,1401,1402,1403,1404,1406,1407,1408,1409,1411,1412,1413,1414, +1416,1417,1418,1419,1421,1422,1423,1424,1426,1427,1429,1430,1431,1432,1434,1435, +1437,1438,1439,1440,1442,1443,1444,1445,1447,1448,1449,1450,1452,1453,1454,1455, +1458,1459,1460,1461,1463,1464,1465,1466,1468,1469,1471,1472,1473,1474,1476,1477, +1479,1480,1481,1482,1484,1485,1486,1487,1489,1490,1492,1493,1494,1495,1497,1498, +1501,1502,1503,1504,1506,1507,1509,1510,1512,1513,1514,1515,1517,1518,1520,1521, +1523,1524,1525,1526,1528,1529,1531,1532,1534,1535,1536,1537,1539,1540,1542,1543, +1545,1546,1547,1548,1550,1551,1553,1554,1556,1557,1558,1559,1561,1562,1564,1565, +1567,1568,1569,1570,1572,1573,1575,1576,1578,1579,1580,1581,1583,1584,1586,1587, +1590,1591,1592,1593,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1609,1610, +1613,1614,1615,1616,1618,1619,1621,1622,1624,1625,1627,1628,1630,1631,1632,1633, +1637,1638,1639,1640,1642,1643,1645,1646,1648,1649,1651,1652,1654,1655,1656,1657, +1660,1661,1663,1664,1666,1667,1669,1670,1672,1673,1675,1676,1678,1679,1681,1682, +1685,1686,1688,1689,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707, +1709,1710,1712,1713,1715,1716,1718,1719,1721,1722,1724,1725,1727,1728,1730,1731, +1734,1735,1737,1738,1740,1741,1743,1744,1746,1748,1749,1751,1752,1754,1755,1757, +1759,1760,1762,1763,1765,1766,1768,1769,1771,1773,1774,1776,1777,1779,1780,1782, +1785,1786,1788,1789,1791,1793,1794,1796,1798,1799,1801,1802,1804,1806,1807,1809, +1811,1812,1814,1815,1817,1819,1820,1822,1824,1825,1827,1828,1830,1832,1833,1835, +1837,1838,1840,1841,1843,1845,1846,1848,1850,1851,1853,1854,1856,1858,1859,1861, +1864,1865,1867,1868,1870,1872,1873,1875,1877,1879,1880,1882,1884,1885,1887,1888, +1891,1892,1894,1895,1897,1899,1900,1902,1904,1906,1907,1909,1911,1912,1914,1915, +1918,1919,1921,1923,1925,1926,1928,1930,1932,1933,1935,1937,1939,1940,1942,1944, +1946,1947,1949,1951,1953,1954,1956,1958,1960,1961,1963,1965,1967,1968,1970,1972, +1975,1976,1978,1980,1982,1983,1985,1987,1989,1990,1992,1994,1996,1997,1999,2001, +2003,2004,2006,2008,2010,2011,2013,2015,2017,2019,2021,2022,2024,2026,2028,2029, +2032,2033,2035,2037,2039,2041,2043,2044,2047,2048,2050,2052,2054,2056,2058,2059, +2062,2063,2065,2067,2069,2071,2073,2074,2077,2078,2080,2082,2084,2086,2088,2089, +2092,2093,2095,2097,2099,2101,2103,2104,2107,2108,2110,2112,2114,2116,2118,2119, +2122,2123,2125,2127,2129,2131,2133,2134,2137,2139,2141,2142,2145,2146,2148,2150, +2153,2154,2156,2158,2160,2162,2164,2165,2168,2170,2172,2173,2176,2177,2179,2181, +2185,2186,2188,2190,2192,2194,2196,2197,2200,2202,2204,2205,2208,2209,2211,2213, +2216,2218,2220,2222,2223,2226,2227,2230,2232,2234,2236,2238,2239,2242,2243,2246, +2249,2251,2253,2255,2256,2259,2260,2263,2265,2267,2269,2271,2272,2275,2276,2279, +2281,2283,2285,2287,2288,2291,2292,2295,2297,2299,2301,2303,2304,2307,2308,2311, +2315,2317,2319,2321,2322,2325,2326,2329,2331,2333,2335,2337,2338,2341,2342,2345, +2348,2350,2352,2354,2355,2358,2359,2362,2364,2366,2368,2370,2371,2374,2375,2378, +2382,2384,2386,2388,2389,2392,2393,2396,2398,2400,2402,2404,2407,2410,2411,2414, +2417,2419,2421,2423,2424,2427,2428,2431,2433,2435,2437,2439,2442,2445,2446,2449, +2452,2454,2456,2458,2459,2462,2463,2466,2468,2470,2472,2474,2477,2480,2481,2484, +2488,2490,2492,2494,2495,2498,2499,2502,2504,2506,2508,2510,2513,2516,2517,2520, +2524,2526,2528,2530,2531,2534,2535,2538,2540,2542,2544,2546,2549,2552,2553,2556, +2561,2563,2565,2567,2568,2571,2572,2575,2577,2579,2581,2583,2586,2589,2590,2593 +}; + + +/* + Noise LFO waveform. + + Here are just 256 samples out of much longer data. + + It does NOT repeat every 256 samples on real chip and I wasnt able to find + the point where it repeats (even in strings as long as 131072 samples). + + I only put it here because its better than nothing and perhaps + someone might be able to figure out the real algorithm. + + + Note that (due to the way the LFO output is calculated) it is quite + possible that two values: 0x80 and 0x00 might be wrong in this table. + To be exact: + some 0x80 could be 0x81 as well as some 0x00 could be 0x01. +*/ + +static const UINT8 lfo_noise_waveform[256] = { +0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A, +0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE, +0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3, +0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2, + +0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D, +0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24, +0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA, +0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB, + +0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5, +0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F, +0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79, +0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51, + +0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE, +0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92, +0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD, +0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55 +}; + + + +/* these variables stay here for speedup purposes only */ +static YM2151 * PSG; +static signed int chanout[8]; +static signed int m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ +static signed int mem; /* one sample delay memory */ + + +/* save output as raw 16-bit sample */ +// #define SAVE_SAMPLE +// #define SAVE_SEPARATE_CHANNELS +#if defined SAVE_SAMPLE || defined SAVE_SEPARATE_CHANNELS +#include + +static FILE *sample[9]; +#endif + + + + +static void init_tables(void) +{ + signed int i,x,n; + double o,m; + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to closest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %4i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]); + logerror("\n"); + #endif + } + /*logerror("TL_TAB_LEN = %i (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + /*logerror("ENV_QUIET= %i\n",ENV_QUIET );*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to closest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + /*logerror("sin [0x%4x]= %4i (tl_tab value=%8x)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/ + } + + + /* calculate d1l_tab table */ + for (i=0; i<16; i++) + { + m = (i!=15 ? i : i+16) * (4.0/ENV_STEP); /* every 3 'dB' except for all bits = 1 = 45+48 'dB' */ + d1l_tab[i] = m; + /*logerror("d1l_tab[%02x]=%08x\n",i,d1l_tab[i] );*/ + } + +#ifdef SAVE_SAMPLE + sample[8]=fopen("sampsum.pcm","wb"); +#endif +#ifdef SAVE_SEPARATE_CHANNELS + sample[0]=fopen("samp0.pcm","wb"); + sample[1]=fopen("samp1.pcm","wb"); + sample[2]=fopen("samp2.pcm","wb"); + sample[3]=fopen("samp3.pcm","wb"); + sample[4]=fopen("samp4.pcm","wb"); + sample[5]=fopen("samp5.pcm","wb"); + sample[6]=fopen("samp6.pcm","wb"); + sample[7]=fopen("samp7.pcm","wb"); +#endif +} + + +static void init_chip_tables(YM2151 *chip) +{ + int i,j; + double mult,phaseinc,Hz; + double scaler; + //attotime pom; + double pom; + + scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq ); + /*logerror("scaler = %20.15f\n", scaler);*/ + + + /* this loop calculates Hertz values for notes from c-0 to b-7 */ + /* including 64 'cents' (100/64 that is 1.5625 of real cent) per note */ + /* i*100/64/1200 is equal to i/768 */ + + /* real chip works with 10 bits fixed point values (10.10) */ + mult = (1<<(FREQ_SH-10)); /* -10 because phaseinc_rom table values are already in 10.10 format */ + + for (i=0; i<768; i++) + { + /* 3.4375 Hz is note A; C# is 4 semitones higher */ + Hz = 1000; +#if 0 +/* Hz is close, but not perfect */ + //Hz = scaler * 3.4375 * pow (2, (i + 4 * 64 ) / 768.0 ); + /* calculate phase increment */ + phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq; +#endif + + phaseinc = phaseinc_rom[i]; /* real chip phase increment */ + phaseinc *= scaler; /* adjust */ + + + /* octave 2 - reference octave */ + chip->freq[ 768+2*768+i ] = ((int)(phaseinc*mult)) & 0xffffffc0; /* adjust to X.10 fixed point */ + /* octave 0 and octave 1 */ + for (j=0; j<2; j++) + { + chip->freq[768 + j*768 + i] = (chip->freq[ 768+2*768+i ] >> (2-j) ) & 0xffffffc0; /* adjust to X.10 fixed point */ + } + /* octave 3 to 7 */ + for (j=3; j<8; j++) + { + chip->freq[768 + j*768 + i] = chip->freq[ 768+2*768+i ] << (j-2); + } + + #if 0 + pom = (double)chip->freq[ 768+2*768+i ] / ((double)(1<sampfreq / (double)SIN_LEN; + logerror("1freq[%4i][%08x]= real %20.15f Hz emul %20.15f Hz\n", i, chip->freq[ 768+2*768+i ], Hz, pom); + #endif + } + + /* octave -1 (all equal to: oct 0, _KC_00_, _KF_00_) */ + for (i=0; i<768; i++) + { + chip->freq[ 0*768 + i ] = chip->freq[1*768+0]; + } + + /* octave 8 and 9 (all equal to: oct 7, _KC_14_, _KF_63_) */ + for (j=8; j<10; j++) + { + for (i=0; i<768; i++) + { + chip->freq[768+ j*768 + i ] = chip->freq[768 + 8*768 -1]; + } + } + +#if 0 + for (i=0; i<11*768; i++) + { + pom = (double)chip->freq[i] / ((double)(1<sampfreq / (double)SIN_LEN; + logerror("freq[%4i][%08x]= emul %20.15f Hz\n", i, chip->freq[i], pom); + } +#endif + + mult = (1<clock/64.0) ) / (double)(1<<20); + + /*calculate phase increment*/ + phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq; + + /*positive and negative values*/ + chip->dt1_freq[ (j+0)*32 + i ] = phaseinc * mult; + chip->dt1_freq[ (j+4)*32 + i ] = -chip->dt1_freq[ (j+0)*32 + i ]; + +#if 0 + { + int x = j*32 + i; + pom = (double)chip->dt1_freq[x] / mult; + pom = pom * (double)chip->sampfreq / (double)SIN_LEN; + logerror("DT1(%03i)[%02i %02i][%08x]= real %19.15f Hz emul %19.15f Hz\n", + x, j, i, chip->dt1_freq[x], Hz, pom); + } +#endif + } + } + + + /* calculate timers' deltas */ + /* User's Manual pages 15,16 */ + mult = (1<clock), 64 * (1024 - i)); + pom = ((double)64 * (1024 - i) / chip->clock); + #ifdef USE_MAME_TIMERS + chip->timer_A_time[i] = pom; + #else + //chip->tim_A_tab[i] = attotime_to_double(pom) * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ + chip->tim_A_tab[i] = pom * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ + #endif + } + for (i=0; i<256; i++) + { + /* ASG 980324: changed to compute both tim_B_tab and timer_B_time */ + //pom= attotime_mul(ATTOTIME_IN_HZ(chip->clock), 1024 * (256 - i)); + pom = ((double)1024 * (256 - i) / chip->clock); + #ifdef USE_MAME_TIMERS + chip->timer_B_time[i] = pom; + #else + //chip->tim_B_tab[i] = attotime_to_double(pom) * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ + chip->tim_B_tab[i] = pom * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */ + #endif + } + + /* calculate noise periods table */ + scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq ); + for (i=0; i<32; i++) + { + j = (i!=31 ? i : 30); /* rate 30 and 31 are the same */ + j = 32-j; + j = (65536.0 / (double)(j*32.0)); /* number of samples per one shift of the shift register */ + /*chip->noise_tab[i] = j * 64;*/ /* number of chip clock cycles per one shift */ + chip->noise_tab[i] = j * 64 * scaler; + /*logerror("noise_tab[%02x]=%08x\n", i, chip->noise_tab[i]);*/ + } +} + +#define KEY_ON(op, key_set){ \ + if (!(op)->key) \ + { \ + (op)->phase = 0; /* clear phase */ \ + (op)->state = EG_ATT; /* KEY ON = attack */ \ + (op)->volume += (~(op)->volume * \ + (eg_inc[(op)->eg_sel_ar + ((PSG->eg_cnt>>(op)->eg_sh_ar)&7)]) \ + ) >>4; \ + if ((op)->volume <= MIN_ATT_INDEX) \ + { \ + (op)->volume = MIN_ATT_INDEX; \ + (op)->state = EG_DEC; \ + } \ + } \ + (op)->key |= key_set; \ +} + +#define KEY_OFF(op, key_clr){ \ + if ((op)->key) \ + { \ + (op)->key &= key_clr; \ + if (!(op)->key) \ + { \ + if ((op)->state>EG_REL) \ + (op)->state = EG_REL;/* KEY OFF = release */\ + } \ + } \ +} + +INLINE void envelope_KONKOFF(YM2151Operator * op, int v) +{ + if (v&0x08) /* M1 */ + KEY_ON (op+0, 1) + else + KEY_OFF(op+0,~1) + + if (v&0x20) /* M2 */ + KEY_ON (op+1, 1) + else + KEY_OFF(op+1,~1) + + if (v&0x10) /* C1 */ + KEY_ON (op+2, 1) + else + KEY_OFF(op+2,~1) + + if (v&0x40) /* C2 */ + KEY_ON (op+3, 1) + else + KEY_OFF(op+3,~1) +} + + +#ifdef USE_MAME_TIMERS + +/*static TIMER_CALLBACK( irqAon_callback ) +{ + YM2151 *chip = (YM2151 *)ptr; + int oldstate = chip->irqlinestate; + + chip->irqlinestate |= 1; + + if (oldstate == 0 && chip->irqhandler) (*chip->irqhandler)(chip->device, 1); +} + +static TIMER_CALLBACK( irqBon_callback ) +{ + YM2151 *chip = (YM2151 *)ptr; + int oldstate = chip->irqlinestate; + + chip->irqlinestate |= 2; + + if (oldstate == 0 && chip->irqhandler) (*chip->irqhandler)(chip->device, 1); +} + +static TIMER_CALLBACK( irqAoff_callback ) +{ + YM2151 *chip = (YM2151 *)ptr; + int oldstate = chip->irqlinestate; + + chip->irqlinestate &= ~1; + + if (oldstate == 1 && chip->irqhandler) (*chip->irqhandler)(chip->device, 0); +} + +static TIMER_CALLBACK( irqBoff_callback ) +{ + YM2151 *chip = (YM2151 *)ptr; + int oldstate = chip->irqlinestate; + + chip->irqlinestate &= ~2; + + if (oldstate == 2 && chip->irqhandler) (*chip->irqhandler)(chip->device, 0); +} + +static TIMER_CALLBACK( timer_callback_a ) +{ + YM2151 *chip = (YM2151 *)ptr; + timer_adjust_oneshot(chip->timer_A, chip->timer_A_time[ chip->timer_A_index ], 0); + chip->timer_A_index_old = chip->timer_A_index; + if (chip->irq_enable & 0x04) + { + chip->status |= 1; + timer_set(machine, attotime_zero,chip,0,irqAon_callback); + } + if (chip->irq_enable & 0x80) + chip->csm_req = 2; // request KEY ON / KEY OFF sequence +} +static TIMER_CALLBACK( timer_callback_b ) +{ + YM2151 *chip = (YM2151 *)ptr; + timer_adjust_oneshot(chip->timer_B, chip->timer_B_time[ chip->timer_B_index ], 0); + chip->timer_B_index_old = chip->timer_B_index; + if (chip->irq_enable & 0x08) + { + chip->status |= 2; + timer_set(machine, attotime_zero,chip,0,irqBon_callback); + } +}*/ +#if 0 +static TIMER_CALLBACK( timer_callback_chip_busy ) +{ + YM2151 *chip = (YM2151 *)ptr; + chip->status &= 0x7f; /* reset busy flag */ +} +#endif +#endif + + + + + + +INLINE void set_connect( YM2151Operator *om1, int cha, int v) +{ + YM2151Operator *om2 = om1+1; + YM2151Operator *oc1 = om1+2; + + /* set connect algorithm */ + + /* MEM is simply one sample delay */ + + switch( v&7 ) + { + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + om1->connect = &c1; + oc1->connect = &mem; + om2->connect = &c2; + om1->mem_connect = &m2; + break; + + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + om1->connect = &mem; + oc1->connect = &mem; + om2->connect = &c2; + om1->mem_connect = &m2; + break; + + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + om1->connect = &c2; + oc1->connect = &mem; + om2->connect = &c2; + om1->mem_connect = &m2; + break; + + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + om1->connect = &c1; + oc1->connect = &mem; + om2->connect = &c2; + om1->mem_connect = &c2; + break; + + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + om1->connect = &c1; + oc1->connect = &chanout[cha]; + om2->connect = &c2; + om1->mem_connect = &mem; /* store it anywhere where it will not be used */ + break; + + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + om1->connect = 0; /* special mark */ + oc1->connect = &chanout[cha]; + om2->connect = &chanout[cha]; + om1->mem_connect = &m2; + break; + + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + om1->connect = &c1; + oc1->connect = &chanout[cha]; + om2->connect = &chanout[cha]; + om1->mem_connect = &mem; /* store it anywhere where it will not be used */ + break; + + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + om1->connect = &chanout[cha]; + oc1->connect = &chanout[cha]; + om2->connect = &chanout[cha]; + om1->mem_connect = &mem; /* store it anywhere where it will not be used */ + break; + } +} + + +INLINE void refresh_EG(YM2151Operator * op) +{ + UINT32 kc; + UINT32 v; + + kc = op->kc; + + /* v = 32 + 2*RATE + RKS = max 126 */ + + v = kc >> op->ks; + if ((op->ar+v) < 32+62) + { + op->eg_sh_ar = eg_rate_shift [op->ar + v ]; + op->eg_sel_ar = eg_rate_select[op->ar + v ]; + } + else + { + op->eg_sh_ar = 0; + op->eg_sel_ar = 17*RATE_STEPS; + } + op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; + op->eg_sel_d1r= eg_rate_select[op->d1r + v]; + op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; + op->eg_sel_d2r= eg_rate_select[op->d2r + v]; + op->eg_sh_rr = eg_rate_shift [op->rr + v]; + op->eg_sel_rr = eg_rate_select[op->rr + v]; + + + op+=1; + + v = kc >> op->ks; + if ((op->ar+v) < 32+62) + { + op->eg_sh_ar = eg_rate_shift [op->ar + v ]; + op->eg_sel_ar = eg_rate_select[op->ar + v ]; + } + else + { + op->eg_sh_ar = 0; + op->eg_sel_ar = 17*RATE_STEPS; + } + op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; + op->eg_sel_d1r= eg_rate_select[op->d1r + v]; + op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; + op->eg_sel_d2r= eg_rate_select[op->d2r + v]; + op->eg_sh_rr = eg_rate_shift [op->rr + v]; + op->eg_sel_rr = eg_rate_select[op->rr + v]; + + op+=1; + + v = kc >> op->ks; + if ((op->ar+v) < 32+62) + { + op->eg_sh_ar = eg_rate_shift [op->ar + v ]; + op->eg_sel_ar = eg_rate_select[op->ar + v ]; + } + else + { + op->eg_sh_ar = 0; + op->eg_sel_ar = 17*RATE_STEPS; + } + op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; + op->eg_sel_d1r= eg_rate_select[op->d1r + v]; + op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; + op->eg_sel_d2r= eg_rate_select[op->d2r + v]; + op->eg_sh_rr = eg_rate_shift [op->rr + v]; + op->eg_sel_rr = eg_rate_select[op->rr + v]; + + op+=1; + + v = kc >> op->ks; + if ((op->ar+v) < 32+62) + { + op->eg_sh_ar = eg_rate_shift [op->ar + v ]; + op->eg_sel_ar = eg_rate_select[op->ar + v ]; + } + else + { + op->eg_sh_ar = 0; + op->eg_sel_ar = 17*RATE_STEPS; + } + op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; + op->eg_sel_d1r= eg_rate_select[op->d1r + v]; + op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; + op->eg_sel_d2r= eg_rate_select[op->d2r + v]; + op->eg_sh_rr = eg_rate_shift [op->rr + v]; + op->eg_sel_rr = eg_rate_select[op->rr + v]; +} + + +/* write a register on YM2151 chip number 'n' */ +void ym2151_write_reg(void *_chip, int r, int v) +{ + YM2151 *chip = (YM2151 *)_chip; + YM2151Operator *op = &chip->oper[ (r&0x07)*4+((r&0x18)>>3) ]; + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + +#if 0 + /* There is no info on what YM2151 really does when busy flag is set */ + if ( chip->status & 0x80 ) return; + timer_set ( attotime_mul(ATTOTIME_IN_HZ(chip->clock), 64), chip, 0, timer_callback_chip_busy); + chip->status |= 0x80; /* set busy flag for 64 chip clock cycles */ +#endif + + /*if (LOG_CYM_FILE && (cymfile) && (r!=0) ) + { + fputc( (unsigned char)r, cymfile ); + fputc( (unsigned char)v, cymfile ); + }*/ + + + switch(r & 0xe0){ + case 0x00: + switch(r){ + case 0x01: /* LFO reset(bit 1), Test Register (other bits) */ + chip->test = v; + if (v&2) chip->lfo_phase = 0; + break; + + case 0x08: + PSG = chip; /* PSG is used in KEY_ON macro */ + envelope_KONKOFF(&chip->oper[ (v&7)*4 ], v ); + break; + + case 0x0f: /* noise mode enable, noise period */ + chip->noise = v; + chip->noise_f = chip->noise_tab[ v & 0x1f ]; + break; + + case 0x10: /* timer A hi */ + chip->timer_A_index = (chip->timer_A_index & 0x003) | (v<<2); + break; + + case 0x11: /* timer A low */ + chip->timer_A_index = (chip->timer_A_index & 0x3fc) | (v & 3); + break; + + case 0x12: /* timer B */ + chip->timer_B_index = v; + break; + + case 0x14: /* CSM, irq flag reset, irq enable, timer start/stop */ + + chip->irq_enable = v; /* bit 3-timer B, bit 2-timer A, bit 7 - CSM */ + + if (v&0x10) /* reset timer A irq flag */ + { +#ifdef USE_MAME_TIMERS + chip->status &= ~1; + timer_set(chip->device->machine, attotime_zero,chip,0,irqAoff_callback); +#else + int oldstate = chip->status & 3; + chip->status &= ~1; + //if ((oldstate==1) && (chip->irqhandler)) (*chip->irqhandler)(chip->device, 0); +#endif + } + + if (v&0x20) /* reset timer B irq flag */ + { +#ifdef USE_MAME_TIMERS + chip->status &= ~2; + timer_set(chip->device->machine, attotime_zero,chip,0,irqBoff_callback); +#else + int oldstate = chip->status & 3; + chip->status &= ~2; + //if ((oldstate==2) && (chip->irqhandler)) (*chip->irqhandler)(chip->device, 0); +#endif + } + + if (v&0x02){ /* load and start timer B */ + #ifdef USE_MAME_TIMERS + /* ASG 980324: added a real timer */ + /* start timer _only_ if it wasn't already started (it will reload time value next round) */ + if (!timer_enable(chip->timer_B, 1)) + { + timer_adjust_oneshot(chip->timer_B, chip->timer_B_time[ chip->timer_B_index ], 0); + chip->timer_B_index_old = chip->timer_B_index; + } + #else + if (!chip->tim_B) + { + chip->tim_B = 1; + chip->tim_B_val = chip->tim_B_tab[ chip->timer_B_index ]; + } + #endif + }else{ /* stop timer B */ + #ifdef USE_MAME_TIMERS + /* ASG 980324: added a real timer */ + timer_enable(chip->timer_B, 0); + #else + chip->tim_B = 0; + #endif + } + + if (v&0x01){ /* load and start timer A */ + #ifdef USE_MAME_TIMERS + /* ASG 980324: added a real timer */ + /* start timer _only_ if it wasn't already started (it will reload time value next round) */ + if (!timer_enable(chip->timer_A, 1)) + { + timer_adjust_oneshot(chip->timer_A, chip->timer_A_time[ chip->timer_A_index ], 0); + chip->timer_A_index_old = chip->timer_A_index; + } + #else + if (!chip->tim_A) + { + chip->tim_A = 1; + chip->tim_A_val = chip->tim_A_tab[ chip->timer_A_index ]; + } + #endif + }else{ /* stop timer A */ + #ifdef USE_MAME_TIMERS + /* ASG 980324: added a real timer */ + timer_enable(chip->timer_A, 0); + #else + chip->tim_A = 0; + #endif + } + break; + + case 0x18: /* LFO frequency */ + { + chip->lfo_overflow = ( 1 << ((15-(v>>4))+3) ) * (1<lfo_counter_add = 0x10 + (v & 0x0f); + } + break; + + case 0x19: /* PMD (bit 7==1) or AMD (bit 7==0) */ + if (v&0x80) + chip->pmd = v & 0x7f; + else + chip->amd = v & 0x7f; + break; + + case 0x1b: /* CT2, CT1, LFO waveform */ + chip->ct = v >> 6; + chip->lfo_wsel = v & 3; + //if (chip->porthandler) (*chip->porthandler)(chip->device, 0 , chip->ct ); + break; + + default: +#ifdef _DEBUG + //logerror("YM2151 Write %02x to undocumented register #%02x\n",v,r); +#endif + break; + } + break; + + case 0x20: + op = &chip->oper[ (r&7) * 4 ]; + switch(r & 0x18){ + case 0x00: /* RL enable, Feedback, Connection */ + op->fb_shift = ((v>>3)&7) ? ((v>>3)&7)+6:0; + chip->pan[ (r&7)*2 ] = (v & 0x40) ? ~0 : 0; + chip->pan[ (r&7)*2 +1 ] = (v & 0x80) ? ~0 : 0; + chip->connect[r&7] = v&7; + set_connect(op, r&7, v&7); + break; + + case 0x08: /* Key Code */ + v &= 0x7f; + if (v != op->kc) + { + UINT32 kc, kc_channel; + + kc_channel = (v - (v>>2))*64; + kc_channel += 768; + kc_channel |= (op->kc_i & 63); + + (op+0)->kc = v; + (op+0)->kc_i = kc_channel; + (op+1)->kc = v; + (op+1)->kc_i = kc_channel; + (op+2)->kc = v; + (op+2)->kc_i = kc_channel; + (op+3)->kc = v; + (op+3)->kc_i = kc_channel; + + kc = v>>2; + + (op+0)->dt1 = chip->dt1_freq[ (op+0)->dt1_i + kc ]; + (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; + + (op+1)->dt1 = chip->dt1_freq[ (op+1)->dt1_i + kc ]; + (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; + + (op+2)->dt1 = chip->dt1_freq[ (op+2)->dt1_i + kc ]; + (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; + + (op+3)->dt1 = chip->dt1_freq[ (op+3)->dt1_i + kc ]; + (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; + + refresh_EG( op ); + } + break; + + case 0x10: /* Key Fraction */ + v >>= 2; + if (v != (op->kc_i & 63)) + { + UINT32 kc_channel; + + kc_channel = v; + kc_channel |= (op->kc_i & ~63); + + (op+0)->kc_i = kc_channel; + (op+1)->kc_i = kc_channel; + (op+2)->kc_i = kc_channel; + (op+3)->kc_i = kc_channel; + + (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; + (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; + (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; + (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; + } + break; + + case 0x18: /* PMS, AMS */ + op->pms = (v>>4) & 7; + op->ams = (v & 3); + break; + } + break; + + case 0x40: /* DT1, MUL */ + { + UINT32 olddt1_i = op->dt1_i; + UINT32 oldmul = op->mul; + + op->dt1_i = (v&0x70)<<1; + op->mul = (v&0x0f) ? (v&0x0f)<<1: 1; + + if (olddt1_i != op->dt1_i) + op->dt1 = chip->dt1_freq[ op->dt1_i + (op->kc>>2) ]; + + if ( (olddt1_i != op->dt1_i) || (oldmul != op->mul) ) + op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1; + } + break; + + case 0x60: /* TL */ + op->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + break; + + case 0x80: /* KS, AR */ + { + UINT32 oldks = op->ks; + UINT32 oldar = op->ar; + + op->ks = 5-(v>>6); + op->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + if ( (op->ar != oldar) || (op->ks != oldks) ) + { + if ((op->ar + (op->kc>>op->ks)) < 32+62) + { + op->eg_sh_ar = eg_rate_shift [op->ar + (op->kc>>op->ks) ]; + op->eg_sel_ar = eg_rate_select[op->ar + (op->kc>>op->ks) ]; + } + else + { + op->eg_sh_ar = 0; + op->eg_sel_ar = 17*RATE_STEPS; + } + } + + if (op->ks != oldks) + { + op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ]; + op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ]; + op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ]; + op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ]; + op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ]; + op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ]; + } + } + break; + + case 0xa0: /* LFO AM enable, D1R */ + op->AMmask = (v&0x80) ? ~0 : 0; + op->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ]; + op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ]; + break; + + case 0xc0: /* DT2, D2R */ + { + UINT32 olddt2 = op->dt2; + op->dt2 = dt2_tab[ v>>6 ]; + if (op->dt2 != olddt2) + op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1; + } + op->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ]; + op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ]; + break; + + case 0xe0: /* D1L, RR */ + op->d1l = d1l_tab[ v>>4 ]; + op->rr = 34 + ((v&0x0f)<<2); + op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ]; + op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ]; + break; + } +} + + +/*static TIMER_CALLBACK( cymfile_callback ) +{ + if (cymfile) + fputc( (unsigned char)0, cymfile ); +}*/ + + +int ym2151_read_status( void *_chip ) +{ + YM2151 *chip = (YM2151 *)_chip; + return chip->status; +} + + + +//#ifdef USE_MAME_TIMERS +#if 1 // disabled for now due to crashing with winalloc.c (ERROR_NOT_ENOUGH_MEMORY) +/* +* state save support for MAME +*/ +//STATE_POSTLOAD( ym2151_postload ) +/*void ym2151_postload(void *param) +{ + YM2151 *YM2151_chip = (YM2151 *)param; + int j; + + for (j=0; j<8; j++) + set_connect(&YM2151_chip->oper[j*4], j, YM2151_chip->connect[j]); +} + +static void ym2151_state_save_register( YM2151 *chip, const device_config *device ) +{ + int j; + + // save all 32 operators of chip #i + for (j=0; j<32; j++) + { + YM2151Operator *op; + + op = &chip->oper[(j&7)*4+(j>>3)]; + + state_save_register_device_item(device, j, op->phase); + state_save_register_device_item(device, j, op->freq); + state_save_register_device_item(device, j, op->dt1); + state_save_register_device_item(device, j, op->mul); + state_save_register_device_item(device, j, op->dt1_i); + state_save_register_device_item(device, j, op->dt2); + // operators connection is saved in chip data block + state_save_register_device_item(device, j, op->mem_value); + + state_save_register_device_item(device, j, op->fb_shift); + state_save_register_device_item(device, j, op->fb_out_curr); + state_save_register_device_item(device, j, op->fb_out_prev); + state_save_register_device_item(device, j, op->kc); + state_save_register_device_item(device, j, op->kc_i); + state_save_register_device_item(device, j, op->pms); + state_save_register_device_item(device, j, op->ams); + state_save_register_device_item(device, j, op->AMmask); + + state_save_register_device_item(device, j, op->state); + state_save_register_device_item(device, j, op->eg_sh_ar); + state_save_register_device_item(device, j, op->eg_sel_ar); + state_save_register_device_item(device, j, op->tl); + state_save_register_device_item(device, j, op->volume); + state_save_register_device_item(device, j, op->eg_sh_d1r); + state_save_register_device_item(device, j, op->eg_sel_d1r); + state_save_register_device_item(device, j, op->d1l); + state_save_register_device_item(device, j, op->eg_sh_d2r); + state_save_register_device_item(device, j, op->eg_sel_d2r); + state_save_register_device_item(device, j, op->eg_sh_rr); + state_save_register_device_item(device, j, op->eg_sel_rr); + + state_save_register_device_item(device, j, op->key); + state_save_register_device_item(device, j, op->ks); + state_save_register_device_item(device, j, op->ar); + state_save_register_device_item(device, j, op->d1r); + state_save_register_device_item(device, j, op->d2r); + state_save_register_device_item(device, j, op->rr); + + state_save_register_device_item(device, j, op->reserved0); + state_save_register_device_item(device, j, op->reserved1); + } + + state_save_register_device_item_array(device, 0, chip->pan); + + state_save_register_device_item(device, 0, chip->eg_cnt); + state_save_register_device_item(device, 0, chip->eg_timer); + state_save_register_device_item(device, 0, chip->eg_timer_add); + state_save_register_device_item(device, 0, chip->eg_timer_overflow); + + state_save_register_device_item(device, 0, chip->lfo_phase); + state_save_register_device_item(device, 0, chip->lfo_timer); + state_save_register_device_item(device, 0, chip->lfo_timer_add); + state_save_register_device_item(device, 0, chip->lfo_overflow); + state_save_register_device_item(device, 0, chip->lfo_counter); + state_save_register_device_item(device, 0, chip->lfo_counter_add); + state_save_register_device_item(device, 0, chip->lfo_wsel); + state_save_register_device_item(device, 0, chip->amd); + state_save_register_device_item(device, 0, chip->pmd); + state_save_register_device_item(device, 0, chip->lfa); + state_save_register_device_item(device, 0, chip->lfp); + + state_save_register_device_item(device, 0, chip->test); + state_save_register_device_item(device, 0, chip->ct); + + state_save_register_device_item(device, 0, chip->noise); + state_save_register_device_item(device, 0, chip->noise_rng); + state_save_register_device_item(device, 0, chip->noise_p); + state_save_register_device_item(device, 0, chip->noise_f); + + state_save_register_device_item(device, 0, chip->csm_req); + state_save_register_device_item(device, 0, chip->irq_enable); + state_save_register_device_item(device, 0, chip->status); + + state_save_register_device_item(device, 0, chip->timer_A_index); + state_save_register_device_item(device, 0, chip->timer_B_index); + state_save_register_device_item(device, 0, chip->timer_A_index_old); + state_save_register_device_item(device, 0, chip->timer_B_index_old); + +#ifdef USE_MAME_TIMERS + state_save_register_device_item(device, 0, chip->irqlinestate); +#endif + + state_save_register_device_item_array(device, 0, chip->connect); + + state_save_register_postload(device->machine, ym2151_postload, chip); +}*/ +#else +/*STATE_POSTLOAD( ym2151_postload ) +{ +} + +static void ym2151_state_save_register( YM2151 *chip, const device_config *device ) +{ +}*/ +#endif + + +/* +* Initialize YM2151 emulator(s). +* +* 'num' is the number of virtual YM2151's to allocate +* 'clock' is the chip clock in Hz +* 'rate' is sampling rate +*/ +void * ym2151_init(int clock, int rate) +{ + YM2151 *PSG; + int chn; + + PSG = (YM2151 *)malloc(sizeof(YM2151)); + if (PSG == NULL) + return NULL; + + memset(PSG, 0, sizeof(YM2151)); + + //ym2151_state_save_register( PSG, device ); + + init_tables(); + + //PSG->device = device; + PSG->clock = clock; + /*rate = clock/64;*/ + PSG->sampfreq = rate ? rate : 44100; /* avoid division by 0 in init_chip_tables() */ + //PSG->irqhandler = NULL; /* interrupt handler */ + //PSG->porthandler = NULL; /* port write handler */ + init_chip_tables( PSG ); + + PSG->lfo_timer_add = (1<sampfreq; + + PSG->eg_timer_add = (1<sampfreq; + PSG->eg_timer_overflow = ( 3 ) * (1<eg_timer_add, PSG->eg_timer_overflow);*/ + +#ifdef USE_MAME_TIMERS +/* this must be done _before_ a call to ym2151_reset_chip() */ + PSG->timer_A = timer_alloc(device->machine, timer_callback_a, PSG); + PSG->timer_B = timer_alloc(device->machine, timer_callback_b, PSG); +#else + PSG->tim_A = 0; + PSG->tim_B = 0; +#endif + for (chn = 0; chn < 8; chn ++) + PSG->Muted[chn] = 0x00; + //ym2151_reset_chip(PSG); + /*logerror("YM2151[init] clock=%i sampfreq=%i\n", PSG->clock, PSG->sampfreq);*/ + + /*LOG_CYM_FILE = (options_get_int(mame_options(), OPTION_VGMWRITE) > 0x00); + if (LOG_CYM_FILE) + { + cymfile = fopen("2151_.cym","wb"); + if (cymfile) + timer_pulse ( device->machine, ATTOTIME_IN_HZ(60), NULL, 0, cymfile_callback); //110 Hz pulse timer + else + logerror("Could not create file 2151_.cym\n"); + }*/ + + return PSG; +} + + + +void ym2151_shutdown(void *_chip) +{ + YM2151 *chip = (YM2151 *)_chip; + + free (chip); + + /*if (cymfile) + fclose (cymfile); + cymfile = NULL;*/ + +#ifdef SAVE_SAMPLE + fclose(sample[8]); +#endif +#ifdef SAVE_SEPARATE_CHANNELS + fclose(sample[0]); + fclose(sample[1]); + fclose(sample[2]); + fclose(sample[3]); + fclose(sample[4]); + fclose(sample[5]); + fclose(sample[6]); + fclose(sample[7]); +#endif +} + + + +/* +* Reset chip number 'n'. +*/ +void ym2151_reset_chip(void *_chip) +{ + int i; + YM2151 *chip = (YM2151 *)_chip; + + + /* initialize hardware registers */ + for (i=0; i<32; i++) + { + memset(&chip->oper[i],'\0',sizeof(YM2151Operator)); + chip->oper[i].volume = MAX_ATT_INDEX; + chip->oper[i].kc_i = 768; /* min kc_i value */ + } + + chip->eg_timer = 0; + chip->eg_cnt = 0; + + chip->lfo_timer = 0; + chip->lfo_counter= 0; + chip->lfo_phase = 0; + chip->lfo_wsel = 0; + chip->pmd = 0; + chip->amd = 0; + chip->lfa = 0; + chip->lfp = 0; + + chip->test= 0; + + chip->irq_enable = 0; +#ifdef USE_MAME_TIMERS + /* ASG 980324 -- reset the timers before writing to the registers */ + timer_enable(chip->timer_A, 0); + timer_enable(chip->timer_B, 0); +#else + chip->tim_A = 0; + chip->tim_B = 0; + chip->tim_A_val = 0; + chip->tim_B_val = 0; +#endif + chip->timer_A_index = 0; + chip->timer_B_index = 0; + chip->timer_A_index_old = 0; + chip->timer_B_index_old = 0; + + chip->noise = 0; + chip->noise_rng = 0; + chip->noise_p = 0; + chip->noise_f = chip->noise_tab[0]; + + chip->csm_req = 0; + chip->status = 0; + + ym2151_write_reg(chip, 0x1b, 0); /* only because of CT1, CT2 output pins */ + ym2151_write_reg(chip, 0x18, 0); /* set LFO frequency */ + for (i=0x20; i<0x100; i++) /* set the operators */ + { + ym2151_write_reg(chip, i, 0); + } +} + + + +INLINE signed int op_calc(YM2151Operator * OP, unsigned int env, signed int pm) +{ + UINT32 p; + + + p = (env<<3) + sin_tab[ ( ((signed int)((OP->phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + + return tl_tab[p]; +} + +INLINE signed int op_calc1(YM2151Operator * OP, unsigned int env, signed int pm) +{ + UINT32 p; + INT32 i; + + + i = (OP->phase & ~FREQ_MASK) + pm; + +/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, OP->phase>>FREQ_SH, pm);*/ + + p = (env<<3) + sin_tab[ (i>>FREQ_SH) & SIN_MASK]; + +/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ + + if (p >= TL_TAB_LEN) + return 0; + + return tl_tab[p]; +} + + + +#define volume_calc(OP) ((OP)->tl + ((UINT32)(OP)->volume) + (AM & (OP)->AMmask)) + +INLINE void chan_calc(unsigned int chan) +{ + YM2151Operator *op; + unsigned int env; + UINT32 AM = 0; + + if (PSG->Muted[chan]) + return; + + m2 = c1 = c2 = mem = 0; + op = &PSG->oper[chan*4]; /* M1 */ + + *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + if (op->ams) + AM = PSG->lfa << (op->ams-1); + env = volume_calc(op); + { + INT32 out = op->fb_out_prev + op->fb_out_curr; + op->fb_out_prev = op->fb_out_curr; + + if (!op->connect) + /* algorithm 5 */ + mem = c1 = c2 = op->fb_out_prev; + else + /* other algorithms */ + *op->connect = op->fb_out_prev; + + op->fb_out_curr = 0; + if (env < ENV_QUIET) + { + if (!op->fb_shift) + out=0; + op->fb_out_curr = op_calc1(op, env, (out<fb_shift) ); + } + } + + env = volume_calc(op+1); /* M2 */ + if (env < ENV_QUIET) + *(op+1)->connect += op_calc(op+1, env, m2); + + env = volume_calc(op+2); /* C1 */ + if (env < ENV_QUIET) + *(op+2)->connect += op_calc(op+2, env, c1); + + env = volume_calc(op+3); /* C2 */ + if (env < ENV_QUIET) + chanout[chan] += op_calc(op+3, env, c2); + if (chanout[chan] > +8192) chanout[chan] = +8192; + else if (chanout[chan] < -8192) chanout[chan] = -8192; + + /* M1 */ + op->mem_value = mem; +} +INLINE void chan7_calc(void) +{ + YM2151Operator *op; + unsigned int env; + UINT32 AM = 0; + + if (PSG->Muted[7]) + return; + + m2 = c1 = c2 = mem = 0; + op = &PSG->oper[7*4]; /* M1 */ + + *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + if (op->ams) + AM = PSG->lfa << (op->ams-1); + env = volume_calc(op); + { + INT32 out = op->fb_out_prev + op->fb_out_curr; + op->fb_out_prev = op->fb_out_curr; + + if (!op->connect) + /* algorithm 5 */ + mem = c1 = c2 = op->fb_out_prev; + else + /* other algorithms */ + *op->connect = op->fb_out_prev; + + op->fb_out_curr = 0; + if (env < ENV_QUIET) + { + if (!op->fb_shift) + out=0; + op->fb_out_curr = op_calc1(op, env, (out<fb_shift) ); + } + } + + env = volume_calc(op+1); /* M2 */ + if (env < ENV_QUIET) + *(op+1)->connect += op_calc(op+1, env, m2); + + env = volume_calc(op+2); /* C1 */ + if (env < ENV_QUIET) + *(op+2)->connect += op_calc(op+2, env, c1); + + env = volume_calc(op+3); /* C2 */ + if (PSG->noise & 0x80) + { + INT32 noiseout; + + noiseout = 0; + if (env < 0x3ff) + noiseout = (env ^ 0x3ff) * 2; /* range of the YM2151 noise output is -2044 to 2040 */ + chanout[7] += ((PSG->noise_rng&0x10000) ? noiseout: -noiseout); /* bit 16 -> output */ + } + else + { + if (env < ENV_QUIET) + chanout[7] += op_calc(op+3, env, c2); + } + if (chanout[7] > +8192) chanout[7] = +8192; + else if (chanout[7] < -8192) chanout[7] = -8192; + /* M1 */ + op->mem_value = mem; +} + + + + + + +/* +The 'rate' is calculated from following formula (example on decay rate): + rks = notecode after key scaling (a value from 0 to 31) + DR = value written to the chip register + rate = 2*DR + rks; (max rate = 2*31+31 = 93) +Four MSBs of the 'rate' above are the 'main' rate (from 00 to 15) +Two LSBs of the 'rate' above are the value 'x' (the shape type). +(eg. '11 2' means that 'rate' is 11*4+2=46) + +NOTE: A 'sample' in the description below is actually 3 output samples, +thats because the Envelope Generator clock is equal to internal_clock/3. + +Single '-' (minus) character in the diagrams below represents one sample +on the output; this is for rates 11 x (11 0, 11 1, 11 2 and 11 3) + +these 'main' rates: +00 x: single '-' = 2048 samples; (ie. level can change every 2048 samples) +01 x: single '-' = 1024 samples; +02 x: single '-' = 512 samples; +03 x: single '-' = 256 samples; +04 x: single '-' = 128 samples; +05 x: single '-' = 64 samples; +06 x: single '-' = 32 samples; +07 x: single '-' = 16 samples; +08 x: single '-' = 8 samples; +09 x: single '-' = 4 samples; +10 x: single '-' = 2 samples; +11 x: single '-' = 1 sample; (ie. level can change every 1 sample) + +Shapes for rates 11 x look like this: +rate: step: +11 0 01234567 + +level: +0 -- +1 -- +2 -- +3 -- + +rate: step: +11 1 01234567 + +level: +0 -- +1 -- +2 - +3 - +4 -- + +rate: step: +11 2 01234567 + +level: +0 -- +1 - +2 - +3 -- +4 - +5 - + +rate: step: +11 3 01234567 + +level: +0 -- +1 - +2 - +3 - +4 - +5 - +6 - + + +For rates 12 x, 13 x, 14 x and 15 x output level changes on every +sample - this means that the waveform looks like this: (but the level +changes by different values on different steps) +12 3 01234567 + +0 - +2 - +4 - +8 - +10 - +12 - +14 - +18 - +20 - + +Notes about the timing: +---------------------- + +1. Synchronism + +Output level of each two (or more) voices running at the same 'main' rate +(eg 11 0 and 11 1 in the diagram below) will always be changing in sync, +even if there're started with some delay. + +Note that, in the diagram below, the decay phase in channel 0 starts at +sample #2, while in channel 1 it starts at sample #6. Anyway, both channels +will always change their levels at exactly the same (following) samples. + +(S - start point of this channel, A-attack phase, D-decay phase): + +step: +01234567012345670123456 + +channel 0: + -- + | -- + | - + | - + | -- + | -- +| -- +| - +| - +| -- +AADDDDDDDDDDDDDDDD +S + +01234567012345670123456 +channel 1: + - + | - + | -- + | -- + | -- + | - + | - + | -- + | -- + | -- + AADDDDDDDDDDDDDDDD + S +01234567012345670123456 + + +2. Shifted (delayed) synchronism + +Output of each two (or more) voices running at different 'main' rate +(9 1, 10 1 and 11 1 in the diagrams below) will always be changing +in 'delayed-sync' (even if there're started with some delay as in "1.") + +Note that the shapes are delayed by exactly one sample per one 'main' rate +increment. (Normally one would expect them to start at the same samples.) + +See diagram below (* - start point of the shape). + +cycle: +0123456701234567012345670123456701234567012345670123456701234567 + +rate 09 1 +*------- + -------- + ---- + ---- + -------- + *------- + | -------- + | ---- + | ---- + | -------- +rate 10 1 | +-- | + *--- | + ---- | + -- | + -- | + ---- | + *--- | + | ---- | + | -- | | <- one step (two samples) delay between 9 1 and 10 1 + | -- | | + | ----| + | *--- + | ---- + | -- + | -- + | ---- +rate 11 1 | +- | + -- | + *- | + -- | + - | + - | + -- | + *- | + -- | + - || <- one step (one sample) delay between 10 1 and 11 1 + - || + --| + *- + -- + - + - + -- + *- + -- + - + - + -- +*/ + +INLINE void advance_eg(void) +{ + YM2151Operator *op; + unsigned int i; + + + + PSG->eg_timer += PSG->eg_timer_add; + + while (PSG->eg_timer >= PSG->eg_timer_overflow) + { + PSG->eg_timer -= PSG->eg_timer_overflow; + + PSG->eg_cnt++; + + /* envelope generator */ + op = &PSG->oper[0]; /* CH 0 M1 */ + i = 32; + do + { + switch(op->state) + { + case EG_ATT: /* attack phase */ + if ( !(PSG->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((PSG->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>4; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(PSG->eg_cnt & ((1<eg_sh_d1r)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_d1r + ((PSG->eg_cnt>>op->eg_sh_d1r)&7)]; + + if ( op->volume >= op->d1l ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + if ( !(PSG->eg_cnt & ((1<eg_sh_d2r)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_d2r + ((PSG->eg_cnt>>op->eg_sh_d2r)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + case EG_REL: /* release phase */ + if ( !(PSG->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((PSG->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + } + op++; + i--; + }while (i); + } +} + + +INLINE void advance(void) +{ + YM2151Operator *op; + unsigned int i; + int a,p; + + /* LFO */ + if (PSG->test&2) + PSG->lfo_phase = 0; + else + { + PSG->lfo_timer += PSG->lfo_timer_add; + if (PSG->lfo_timer >= PSG->lfo_overflow) + { + PSG->lfo_timer -= PSG->lfo_overflow; + PSG->lfo_counter += PSG->lfo_counter_add; + PSG->lfo_phase += (PSG->lfo_counter>>4); + PSG->lfo_phase &= 255; + PSG->lfo_counter &= 15; + } + } + + i = PSG->lfo_phase; + /* calculate LFO AM and PM waveform value (all verified on real chip, except for noise algorithm which is impossible to analyse)*/ + switch (PSG->lfo_wsel) + { + case 0: + /* saw */ + /* AM: 255 down to 0 */ + /* PM: 0 to 127, -127 to 0 (at PMD=127: LFP = 0 to 126, -126 to 0) */ + a = 255 - i; + if (i<128) + p = i; + else + p = i - 255; + break; + case 1: + /* square */ + /* AM: 255, 0 */ + /* PM: 128,-128 (LFP = exactly +PMD, -PMD) */ + if (i<128){ + a = 255; + p = 128; + }else{ + a = 0; + p = -128; + } + break; + case 2: + /* triangle */ + /* AM: 255 down to 1 step -2; 0 up to 254 step +2 */ + /* PM: 0 to 126 step +2, 127 to 1 step -2, 0 to -126 step -2, -127 to -1 step +2*/ + if (i<128) + a = 255 - (i*2); + else + a = (i*2) - 256; + + if (i<64) /* i = 0..63 */ + p = i*2; /* 0 to 126 step +2 */ + else if (i<128) /* i = 64..127 */ + p = 255 - i*2; /* 127 to 1 step -2 */ + else if (i<192) /* i = 128..191 */ + p = 256 - i*2; /* 0 to -126 step -2*/ + else /* i = 192..255 */ + p = i*2 - 511; /*-127 to -1 step +2*/ + break; + case 3: + default: /*keep the compiler happy*/ + /* random */ + /* the real algorithm is unknown !!! + We just use a snapshot of data from real chip */ + + /* AM: range 0 to 255 */ + /* PM: range -128 to 127 */ + + a = lfo_noise_waveform[i]; + p = a-128; + break; + } + PSG->lfa = a * PSG->amd / 128; + PSG->lfp = p * PSG->pmd / 128; + + + /* The Noise Generator of the YM2151 is 17-bit shift register. + * Input to the bit16 is negated (bit0 XOR bit3) (EXNOR). + * Output of the register is negated (bit0 XOR bit3). + * Simply use bit16 as the noise output. + */ + PSG->noise_p += PSG->noise_f; + i = (PSG->noise_p>>16); /* number of events (shifts of the shift register) */ + PSG->noise_p &= 0xffff; + while (i) + { + UINT32 j; + j = ( (PSG->noise_rng ^ (PSG->noise_rng>>3) ) & 1) ^ 1; + PSG->noise_rng = (j<<16) | (PSG->noise_rng>>1); + i--; + } + + + /* phase generator */ + op = &PSG->oper[0]; /* CH 0 M1 */ + i = 8; + do + { + if (op->pms) /* only when phase modulation from LFO is enabled for this channel */ + { + INT32 mod_ind = PSG->lfp; /* -128..+127 (8bits signed) */ + if (op->pms < 6) + mod_ind >>= (6 - op->pms); + else + mod_ind <<= (op->pms - 5); + + if (mod_ind) + { + UINT32 kc_channel = op->kc_i + mod_ind; + (op+0)->phase += ( (PSG->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1; + (op+1)->phase += ( (PSG->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1; + (op+2)->phase += ( (PSG->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1; + (op+3)->phase += ( (PSG->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1; + } + else /* phase modulation from LFO is equal to zero */ + { + (op+0)->phase += (op+0)->freq; + (op+1)->phase += (op+1)->freq; + (op+2)->phase += (op+2)->freq; + (op+3)->phase += (op+3)->freq; + } + } + else /* phase modulation from LFO is disabled */ + { + (op+0)->phase += (op+0)->freq; + (op+1)->phase += (op+1)->freq; + (op+2)->phase += (op+2)->freq; + (op+3)->phase += (op+3)->freq; + } + + op+=4; + i--; + }while (i); + + + /* CSM is calculated *after* the phase generator calculations (verified on real chip) + * CSM keyon line seems to be ORed with the KO line inside of the chip. + * The result is that it only works when KO (register 0x08) is off, ie. 0 + * + * Interesting effect is that when timer A is set to 1023, the KEY ON happens + * on every sample, so there is no KEY OFF at all - the result is that + * the sound played is the same as after normal KEY ON. + */ + + if (PSG->csm_req) /* CSM KEYON/KEYOFF seqeunce request */ + { + if (PSG->csm_req==2) /* KEY ON */ + { + op = &PSG->oper[0]; /* CH 0 M1 */ + i = 32; + do + { + KEY_ON(op, 2); + op++; + i--; + }while (i); + PSG->csm_req = 1; + } + else /* KEY OFF */ + { + op = &PSG->oper[0]; /* CH 0 M1 */ + i = 32; + do + { + KEY_OFF(op,~2); + op++; + i--; + }while (i); + PSG->csm_req = 0; + } + } +} + +#if 0 +INLINE signed int acc_calc(signed int value) +{ + if (value>=0) + { + if (value < 0x0200) + return (value & ~0); + if (value < 0x0400) + return (value & ~1); + if (value < 0x0800) + return (value & ~3); + if (value < 0x1000) + return (value & ~7); + if (value < 0x2000) + return (value & ~15); + if (value < 0x4000) + return (value & ~31); + return (value & ~63); + } + /*else value < 0*/ + if (value > -0x0200) + return (~abs(value) & ~0); + if (value > -0x0400) + return (~abs(value) & ~1); + if (value > -0x0800) + return (~abs(value) & ~3); + if (value > -0x1000) + return (~abs(value) & ~7); + if (value > -0x2000) + return (~abs(value) & ~15); + if (value > -0x4000) + return (~abs(value) & ~31); + return (~abs(value) & ~63); +} +#endif + +/* first macro saves left and right channels to mono file */ +/* second macro saves left and right channels to stereo file */ +#if 0 /*MONO*/ + #ifdef SAVE_SEPARATE_CHANNELS + #define SAVE_SINGLE_CHANNEL(j) \ + { signed int pom= -(chanout[j] & PSG->pan[j*2]); \ + if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ + fputc((unsigned short)pom&0xff,sample[j]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ + } + #else + #define SAVE_SINGLE_CHANNEL(j) + #endif +#else /*STEREO*/ + #ifdef SAVE_SEPARATE_CHANNELS + #define SAVE_SINGLE_CHANNEL(j) \ + { signed int pom = -(chanout[j] & PSG->pan[j*2]); \ + if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ + fputc((unsigned short)pom&0xff,sample[j]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ + pom = -(chanout[j] & PSG->pan[j*2+1]); \ + if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \ + fputc((unsigned short)pom&0xff,sample[j]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[j]); \ + } + #else + #define SAVE_SINGLE_CHANNEL(j) + #endif +#endif + +/* first macro saves left and right channels to mono file */ +/* second macro saves left and right channels to stereo file */ +#if 1 /*MONO*/ + #ifdef SAVE_SAMPLE + #define SAVE_ALL_CHANNELS \ + { signed int pom = outl; \ + /*pom = acc_calc(pom);*/ \ + /*fprintf(sample[8]," %i\n",pom);*/ \ + fputc((unsigned short)pom&0xff,sample[8]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ + } + #else + #define SAVE_ALL_CHANNELS + #endif +#else /*STEREO*/ + #ifdef SAVE_SAMPLE + #define SAVE_ALL_CHANNELS \ + { signed int pom = outl; \ + fputc((unsigned short)pom&0xff,sample[8]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ + pom = outr; \ + fputc((unsigned short)pom&0xff,sample[8]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[8]); \ + } + #else + #define SAVE_ALL_CHANNELS + #endif +#endif + + +/* Generate samples for one of the YM2151's +* +* 'num' is the number of virtual YM2151 +* '**buffers' is table of pointers to the buffers: left and right +* 'length' is the number of samples that should be generated +*/ +void ym2151_update_one(void *chip, SAMP **buffers, int length) +{ + int i, chn; + signed int outl,outr; + SAMP *bufL, *bufR; + + bufL = buffers[0]; + bufR = buffers[1]; + + PSG = (YM2151 *)chip; + +#ifdef USE_MAME_TIMERS + /* ASG 980324 - handled by real timers now */ +#else + if (PSG->tim_B) + { + PSG->tim_B_val -= ( length << TIMER_SH ); + if (PSG->tim_B_val<=0) + { + PSG->tim_B_val += PSG->tim_B_tab[ PSG->timer_B_index ]; + if ( PSG->irq_enable & 0x08 ) + { + int oldstate = PSG->status & 3; + PSG->status |= 2; + //if ((!oldstate) && (PSG->irqhandler)) (*PSG->irqhandler)(chip->device, 1); + } + } + } +#endif + + for (i=0; ipan[0]; + outr = chanout[0] & PSG->pan[1]; + outl += (chanout[1] & PSG->pan[2]); + outr += (chanout[1] & PSG->pan[3]); + outl += (chanout[2] & PSG->pan[4]); + outr += (chanout[2] & PSG->pan[5]); + outl += (chanout[3] & PSG->pan[6]); + outr += (chanout[3] & PSG->pan[7]); + outl += (chanout[4] & PSG->pan[8]); + outr += (chanout[4] & PSG->pan[9]); + outl += (chanout[5] & PSG->pan[10]); + outr += (chanout[5] & PSG->pan[11]); + outl += (chanout[6] & PSG->pan[12]); + outr += (chanout[6] & PSG->pan[13]); + outl += (chanout[7] & PSG->pan[14]); + outr += (chanout[7] & PSG->pan[15]); + + outl >>= FINAL_SH; + outr >>= FINAL_SH; + //if (outl > MAXOUT) outl = MAXOUT; + // else if (outl < MINOUT) outl = MINOUT; + //if (outr > MAXOUT) outr = MAXOUT; + // else if (outr < MINOUT) outr = MINOUT; + ((SAMP*)bufL)[i] = (SAMP)outl; + ((SAMP*)bufR)[i] = (SAMP)outr; + + SAVE_ALL_CHANNELS + +#ifdef USE_MAME_TIMERS + /* ASG 980324 - handled by real timers now */ +#else + /* calculate timer A */ + if (PSG->tim_A) + { + PSG->tim_A_val -= ( 1 << TIMER_SH ); + if (PSG->tim_A_val <= 0) + { + PSG->tim_A_val += PSG->tim_A_tab[ PSG->timer_A_index ]; + if (PSG->irq_enable & 0x04) + { + int oldstate = PSG->status & 3; + PSG->status |= 1; + //if ((!oldstate) && (PSG->irqhandler)) (*PSG->irqhandler)(chip->device, 1); + } + if (PSG->irq_enable & 0x80) + PSG->csm_req = 2; /* request KEY ON / KEY OFF sequence */ + } + } +#endif + advance(); + } +} + +/*void ym2151_set_irq_handler(void *chip, void(*handler)(int irq)) +{ + YM2151 *PSG = (YM2151 *)chip; + PSG->irqhandler = handler; +} + +void ym2151_set_port_write_handler(void *chip, write8_device_func handler) +{ + YM2151 *PSG = (YM2151 *)chip; + PSG->porthandler = handler; +}*/ + +void ym2151_set_mutemask(void *chip, UINT32 MuteMask) +{ + YM2151 *PSG = (YM2151 *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 8; CurChn ++) + PSG->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; + + return; +} + diff --git a/Frameworks/GME/gme/ym2151.h b/Frameworks/GME/vgmplay/chips/ym2151.h similarity index 81% rename from Frameworks/GME/gme/ym2151.h rename to Frameworks/GME/vgmplay/chips/ym2151.h index 834dfe1bd..1c630cba7 100644 --- a/Frameworks/GME/gme/ym2151.h +++ b/Frameworks/GME/vgmplay/chips/ym2151.h @@ -1,89 +1,87 @@ -/* -** File: ym2151.h - header file for software implementation of YM2151 -** FM Operator Type-M(OPM) -** -** (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net) -** Some of the optimizing ideas by Tatsuyuki Satoh -** -** Version 2.150 final beta May, 11th 2002 -** -** -** I would like to thank following people for making this project possible: -** -** Beauty Planets - for making a lot of real YM2151 samples and providing -** additional informations about the chip. Also for the time spent making -** the samples and the speed of replying to my endless requests. -** -** Shigeharu Isoda - for general help, for taking time to scan his YM2151 -** Japanese Manual first of all, and answering MANY of my questions. -** -** Nao - for giving me some info about YM2151 and pointing me to Shigeharu. -** Also for creating fmemu (which I still use to test the emulator). -** -** Aaron Giles and Chris Hardy - they made some samples of one of my favourite -** arcade games so I could compare it to my emulator. -** -** Bryan McPhail and Tim (powerjaw) - for making some samples. -** -** Ishmair - for the datasheet and motivation. -*/ - -#pragma once - -#ifndef __YM2151_H__ -#define __YM2151_H__ - - -/* 16- and 8-bit samples (signed) are supported*/ -#define SAMPLE_BITS 16 - -#include "mamedef.h" - -typedef stream_sample_t SAMP; -/* -#if (SAMPLE_BITS==16) - typedef INT16 SAMP; -#endif -#if (SAMPLE_BITS==8) - typedef signed char SAMP; -#endif -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/* -** Initialize YM2151 emulator(s). -** -** 'num' is the number of virtual YM2151's to allocate -** 'clock' is the chip clock in Hz -** 'rate' is sampling rate -*/ -void *ym2151_init(int clock, int rate); - -/* shutdown the YM2151 emulators*/ -void ym2151_shutdown(void *chip); - -/* reset all chip registers for YM2151 number 'num'*/ -void ym2151_reset_chip(void *chip); - -/* -** Generate samples for one of the YM2151's -** -** 'num' is the number of virtual YM2151 -** '**buffers' is table of pointers to the buffers: left and right -** 'length' is the number of samples that should be generated -*/ -void ym2151_update_one(void *chip, SAMP **buffers, int length); - -/* write 'v' to register 'r' on YM2151 chip number 'n'*/ -void ym2151_write_reg(void *chip, int r, int v); - -void ym2151_set_mask(void *chip, UINT32 mask); - -#ifdef __cplusplus -} -#endif - -#endif /*__YM2151_H__*/ +/* +** File: ym2151.h - header file for software implementation of YM2151 +** FM Operator Type-M(OPM) +** +** (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net) +** Some of the optimizing ideas by Tatsuyuki Satoh +** +** Version 2.150 final beta May, 11th 2002 +** +** +** I would like to thank following people for making this project possible: +** +** Beauty Planets - for making a lot of real YM2151 samples and providing +** additional informations about the chip. Also for the time spent making +** the samples and the speed of replying to my endless requests. +** +** Shigeharu Isoda - for general help, for taking time to scan his YM2151 +** Japanese Manual first of all, and answering MANY of my questions. +** +** Nao - for giving me some info about YM2151 and pointing me to Shigeharu. +** Also for creating fmemu (which I still use to test the emulator). +** +** Aaron Giles and Chris Hardy - they made some samples of one of my favourite +** arcade games so I could compare it to my emulator. +** +** Bryan McPhail and Tim (powerjaw) - for making some samples. +** +** Ishmair - for the datasheet and motivation. +*/ + +#pragma once + + +/* 16- and 8-bit samples (signed) are supported*/ +#define SAMPLE_BITS 16 + +typedef stream_sample_t SAMP; +/* +#if (SAMPLE_BITS==16) + typedef INT16 SAMP; +#endif +#if (SAMPLE_BITS==8) + typedef signed char SAMP; +#endif +*/ + +/* +** Initialize YM2151 emulator(s). +** +** 'num' is the number of virtual YM2151's to allocate +** 'clock' is the chip clock in Hz +** 'rate' is sampling rate +*/ +void *ym2151_init(int clock, int rate); + +/* shutdown the YM2151 emulators*/ +void ym2151_shutdown(void *chip); + +/* reset all chip registers for YM2151 number 'num'*/ +void ym2151_reset_chip(void *chip); + +/* +** Generate samples for one of the YM2151's +** +** 'num' is the number of virtual YM2151 +** '**buffers' is table of pointers to the buffers: left and right +** 'length' is the number of samples that should be generated +*/ +void ym2151_update_one(void *chip, SAMP **buffers, int length); + +/* write 'v' to register 'r' on YM2151 chip number 'n'*/ +void ym2151_write_reg(void *chip, int r, int v); + +/* read status register on YM2151 chip number 'n'*/ +int ym2151_read_status(void *chip); + +/* set interrupt handler on YM2151 chip number 'n'*/ +//void ym2151_set_irq_handler(void *chip, void (*handler)(int irq)); + +/* set port write handler on YM2151 chip number 'n'*/ +//void ym2151_set_port_write_handler(void *chip, write8_device_func handler); + +/* refresh chip when load state */ +//STATE_POSTLOAD( ym2151_postload ); +void ym2151_postload(void *param); + +void ym2151_set_mutemask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/gme/ym2413.c b/Frameworks/GME/vgmplay/chips/ym2413.c similarity index 73% rename from Frameworks/GME/gme/ym2413.c rename to Frameworks/GME/vgmplay/chips/ym2413.c index 726e880cc..04994ac73 100644 --- a/Frameworks/GME/gme/ym2413.c +++ b/Frameworks/GME/vgmplay/chips/ym2413.c @@ -1,2108 +1,2255 @@ -/* -** -** File: ym2413.c - software implementation of YM2413 -** FM sound generator type OPLL -** -** Copyright Jarek Burczynski -** -** Version 1.0 -** - - Features as listed in LSI-212413A2 data sheet: - 1. FM Sound Generator for real sound creation. - 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds - (different tones can be used together in either case). - 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones). - 4. Built-in DA Converter. - 5. Built-in Quartz Oscillator. - 6. Built-in Vibrato Oscillator/AM Oscillator - 7. TTL Compatible Input. - 8. Si-Gate NMOS LSI - 9. A single 5V power source. - -to do: - -- make sure of the sinus amplitude bits - -- make sure of the EG resolution bits (looks like the biggest - modulation index generated by the modulator is 123, 124 = no modulation) -- find proper algorithm for attack phase of EG - -- tune up instruments ROM - -- support sample replay in test mode (it is NOT as simple as setting bit 0 - in register 0x0f and using register 0x10 for sample data). - Which games use this feature ? - - -*/ - -#define _USE_MATH_DEFINES -#include -#include -#include -#include "mamedef.h" -#include "ym2413.h" - -#ifndef INLINE -#define INLINE static __inline -#endif -#ifndef logerror -#define logerror (void) -#endif - -#ifndef M_PI - #define M_PI 3.14159265358979323846 -#endif - -/* output final shift */ -#if (SAMPLE_BITS==16) - #define FINAL_SH (0) - #define MAXOUT (+32767) - #define MINOUT (-32768) -#else - #define FINAL_SH (8) - #define MAXOUT (+127) - #define MINOUT (-128) -#endif - - -#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define EG_SH 16 /* 16.16 fixed point (EG timing) */ -#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ - -#define FREQ_MASK ((1<>KSR */ - UINT8 mul; /* multiple: mul_tab[ML] */ - - /* Phase Generator */ - UINT32 phase; /* frequency counter */ - UINT32 freq; /* frequency counter step */ - UINT8 fb_shift; /* feedback shift value */ - INT32 op1_out[2]; /* slot1 output for feedback */ - - /* Envelope Generator */ - UINT8 eg_type; /* percussive/nonpercussive mode*/ - UINT8 state; /* phase type */ - UINT32 TL; /* total level: TL << 2 */ - INT32 TLL; /* adjusted now TL */ - INT32 volume; /* envelope counter */ - UINT32 sl; /* sustain level: sl_tab[SL] */ - - UINT8 eg_sh_dp; /* (dump state) */ - UINT8 eg_sel_dp; /* (dump state) */ - UINT8 eg_sh_ar; /* (attack state) */ - UINT8 eg_sel_ar; /* (attack state) */ - UINT8 eg_sh_dr; /* (decay state) */ - UINT8 eg_sel_dr; /* (decay state) */ - UINT8 eg_sh_rr; /* (release state for non-perc.)*/ - UINT8 eg_sel_rr; /* (release state for non-perc.)*/ - UINT8 eg_sh_rs; /* (release state for perc.mode)*/ - UINT8 eg_sel_rs; /* (release state for perc.mode)*/ - - UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ - - /* LFO */ - UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ - UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ - - /* waveform select */ - unsigned int wavetable; -} OPLL_SLOT; - -#define OPLL_MASK_CH(x) (1<<(x)) -#define OPLL_MASK_HH (1<<(9)) -#define OPLL_MASK_CYM (1<<(10)) -#define OPLL_MASK_TOM (1<<(11)) -#define OPLL_MASK_SD (1<<(12)) -#define OPLL_MASK_BD (1<<(13)) -#define OPLL_MASK_RHYTHM ( OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD ) - -typedef struct{ - OPLL_SLOT SLOT[2]; - /* phase generator state */ - UINT32 block_fnum; /* block+fnum */ - UINT32 fc; /* Freq. freqement base */ - UINT32 ksl_base; /* KeyScaleLevel Base step */ - UINT8 kcode; /* key code (for key scaling) */ - UINT8 sus; /* sus on/off (release speed in percussive mode)*/ -} OPLL_CH; - -/* chip state */ -typedef struct { - OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/ - UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/ - - UINT32 eg_cnt; /* global envelope generator counter */ - UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ - UINT32 eg_timer_add; /* step of eg_timer */ - UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ - - UINT8 rhythm; /* Rhythm mode */ - - /* LFO */ - UINT32 lfo_am_cnt; - UINT32 lfo_am_inc; - UINT32 lfo_pm_cnt; - UINT32 lfo_pm_inc; - - UINT32 noise_rng; /* 23 bit noise shift register */ - UINT32 noise_p; /* current noise 'phase' */ - UINT32 noise_f; /* current noise period */ - - -/* instrument settings */ -/* - 0-user instrument - 1-15 - fixed instruments - 16 -bass drum settings - 17,18 - other percussion instruments -*/ - UINT8 inst_tab[19][8]; - - /* external event callback handlers */ - OPLL_UPDATEHANDLER UpdateHandler; /* stream update handler */ - void * UpdateParam; /* stream update parameter */ - - UINT32 fn_tab[1024]; /* fnumber->increment counter */ - - UINT8 address; /* address register */ - UINT8 status; /* status flag */ - - int clock; /* master clock (Hz) */ - int rate; /* sampling rate (Hz) */ - double freqbase; /* frequency base */ - - /* work table */ - OPLL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2; - - signed int output[2]; - - UINT32 LFO_AM; - INT32 LFO_PM; - - int chip_type; - UINT32 mask; -} YM2413; - -/* key scale level */ -/* table is 3dB/octave, DV converts this into 6dB/octave */ -/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ -#define DV (0.1875/1.0) -static const UINT32 ksl_tab[8*16]= -{ - /* OCT 0 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - /* OCT 1 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, - 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, - /* OCT 2 */ - 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, - 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, - 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, - 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, - /* OCT 3 */ - 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, - 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, - 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, - 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, - /* OCT 4 */ - 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, - 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, - 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, - 10.875/DV,11.250/DV,11.625/DV,12.000/DV, - /* OCT 5 */ - 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, - 9.000/DV,10.125/DV,10.875/DV,11.625/DV, - 12.000/DV,12.750/DV,13.125/DV,13.500/DV, - 13.875/DV,14.250/DV,14.625/DV,15.000/DV, - /* OCT 6 */ - 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, - 12.000/DV,13.125/DV,13.875/DV,14.625/DV, - 15.000/DV,15.750/DV,16.125/DV,16.500/DV, - 16.875/DV,17.250/DV,17.625/DV,18.000/DV, - /* OCT 7 */ - 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, - 15.000/DV,16.125/DV,16.875/DV,17.625/DV, - 18.000/DV,18.750/DV,19.125/DV,19.500/DV, - 19.875/DV,20.250/DV,20.625/DV,21.000/DV -}; -#undef DV - -/* sustain level table (3dB per step) */ -/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB)*/ -#define SC(db) (UINT32) ( db * (1.0/ENV_STEP) ) -static const UINT32 sl_tab[16]={ - SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), - SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15) -}; -#undef SC - - -#define RATE_STEPS (8) -static const unsigned char eg_inc[15*RATE_STEPS]={ - -/*cycle:0 1 2 3 4 5 6 7*/ - -/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ -/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ -/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ -/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ - -/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ -/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ -/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ -/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ - -/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ -/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ -/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ -/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ - -/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ -/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ -/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ -}; - - -#define O(a) (a*RATE_STEPS) - -/*note that there is no O(13) in this table - it's directly in the code */ -static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), -O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), - -/* rates 00-12 */ -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), -O( 0),O( 1),O( 2),O( 3), - -/* rate 13 */ -O( 4),O( 5),O( 6),O( 7), - -/* rate 14 */ -O( 8),O( 9),O(10),O(11), - -/* rate 15 */ -O(12),O(12),O(12),O(12), - -/* 16 dummy rates (same as 15 3) */ -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), -O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), - -}; -#undef O - -/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ -/*shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 */ -/*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */ - -#define O(a) (a*1) -static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ -/* 16 infinite time rates */ -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), -O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), - -/* rates 00-12 */ -O(13),O(13),O(13),O(13), -O(12),O(12),O(12),O(12), -O(11),O(11),O(11),O(11), -O(10),O(10),O(10),O(10), -O( 9),O( 9),O( 9),O( 9), -O( 8),O( 8),O( 8),O( 8), -O( 7),O( 7),O( 7),O( 7), -O( 6),O( 6),O( 6),O( 6), -O( 5),O( 5),O( 5),O( 5), -O( 4),O( 4),O( 4),O( 4), -O( 3),O( 3),O( 3),O( 3), -O( 2),O( 2),O( 2),O( 2), -O( 1),O( 1),O( 1),O( 1), - -/* rate 13 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 14 */ -O( 0),O( 0),O( 0),O( 0), - -/* rate 15 */ -O( 0),O( 0),O( 0),O( 0), - -/* 16 dummy rates (same as 15 3) */ -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), -O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), - -}; -#undef O - - -/* multiple table */ -#define ML 2 -static const UINT8 mul_tab[16]= { -/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ - 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, - 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML -}; -#undef ML - -/* TL_TAB_LEN is calculated as: -* 11 - sinus amplitude bits (Y axis) -* 2 - sinus sign bit (Y axis) -* TL_RES_LEN - sinus resolution (X axis) -*/ -#define TL_TAB_LEN (11*2*TL_RES_LEN) -static signed int tl_tab[TL_TAB_LEN]; - -#define ENV_QUIET (TL_TAB_LEN>>5) - -/* sin waveform table in 'decibel' scale */ -/* two waveforms on OPLL type chips */ -static unsigned int sin_tab[SIN_LEN * 2]; - - -/* LFO Amplitude Modulation table (verified on real YM3812) - 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples - - Length: 210 elements. - - Each of the elements has to be repeated - exactly 64 times (on 64 consecutive samples). - The whole table takes: 64 * 210 = 13440 samples. - -We use data>>1, until we find what it really is on real chip... - -*/ - -#define LFO_AM_TAB_ELEMENTS 210 - -static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { -0,0,0,0,0,0,0, -1,1,1,1, -2,2,2,2, -3,3,3,3, -4,4,4,4, -5,5,5,5, -6,6,6,6, -7,7,7,7, -8,8,8,8, -9,9,9,9, -10,10,10,10, -11,11,11,11, -12,12,12,12, -13,13,13,13, -14,14,14,14, -15,15,15,15, -16,16,16,16, -17,17,17,17, -18,18,18,18, -19,19,19,19, -20,20,20,20, -21,21,21,21, -22,22,22,22, -23,23,23,23, -24,24,24,24, -25,25,25,25, -26,26,26, -25,25,25,25, -24,24,24,24, -23,23,23,23, -22,22,22,22, -21,21,21,21, -20,20,20,20, -19,19,19,19, -18,18,18,18, -17,17,17,17, -16,16,16,16, -15,15,15,15, -14,14,14,14, -13,13,13,13, -12,12,12,12, -11,11,11,11, -10,10,10,10, -9,9,9,9, -8,8,8,8, -7,7,7,7, -6,6,6,6, -5,5,5,5, -4,4,4,4, -3,3,3,3, -2,2,2,2, -1,1,1,1 -}; - -/* LFO Phase Modulation table (verified on real YM2413) */ -static const INT8 lfo_pm_table[8*8] = { - -/* FNUM2/FNUM = 0 00xxxxxx (0x0000) */ -0, 0, 0, 0, 0, 0, 0, 0, - -/* FNUM2/FNUM = 0 01xxxxxx (0x0040) */ -1, 0, 0, 0,-1, 0, 0, 0, - -/* FNUM2/FNUM = 0 10xxxxxx (0x0080) */ -2, 1, 0,-1,-2,-1, 0, 1, - -/* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */ -3, 1, 0,-1,-3,-1, 0, 1, - -/* FNUM2/FNUM = 1 00xxxxxx (0x0100) */ -4, 2, 0,-2,-4,-2, 0, 2, - -/* FNUM2/FNUM = 1 01xxxxxx (0x0140) */ -5, 2, 0,-2,-5,-2, 0, 2, - -/* FNUM2/FNUM = 1 10xxxxxx (0x0180) */ -6, 3, 0,-3,-6,-3, 0, 3, - -/* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */ -7, 3, 0,-3,-7,-3, 0, 3, -}; - - - - - - -/* This is not 100% perfect yet but very close */ -/* - - multi parameters are 100% correct (instruments and drums) - - LFO PM and AM enable are 100% correct - - waveform DC and DM select are 100% correct -*/ - -static const unsigned char table[19][8] = { -/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ -/* 0 1 2 3 4 5 6 7 */ - {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, /* 0 */ - - {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, /* 1 */ - {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, /* 2 */ - {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, /* 3 */ - {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, /* 4 */ - -/* {0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 */ - {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, /* 5 */ - -/* {0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 */ - {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, /* 6 */ - - {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, /* 7 */ - {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, /* 8 */ - {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, /* 9 */ - -/* {0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A */ - {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, /* A */ - - {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, /* B */ - {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, /* C */ - -/* {0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D */ - {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, /* D */ - - {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, /* E */ - {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, /* F */ - -/* drum instruments definitions */ -/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ -/* 0 1 2 3 4 5 6 7 */ - {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ - {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ - {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ -}; - -static const unsigned char table_vrc7[15][8] = -{ -/* VRC7 instruments, January 17, 2004 update -Xodnizel */ - {0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17}, - {0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12}, - {0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02}, - {0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27}, - {0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28}, - {0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95}, - {0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07}, - {0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15}, - {0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11}, - {0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12}, - {0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4}, - {0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16}, - {0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51}, - {0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05}, - {0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17}, - -}; - -INLINE int limit( int val, int max, int min ) { - if ( val > max ) - val = max; - else if ( val < min ) - val = min; - - return val; -} - - -/* advance LFO to next sample */ -INLINE void advance_lfo(YM2413 *chip) -{ - /* LFO */ - chip->lfo_am_cnt += chip->lfo_am_inc; - if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<LFO_AM = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ] >> 1; - - chip->lfo_pm_cnt += chip->lfo_pm_inc; - chip->LFO_PM = (chip->lfo_pm_cnt>>LFO_SH) & 7; -} - -/* advance to next sample */ -INLINE void advance(YM2413 *chip) -{ - OPLL_CH *CH; - OPLL_SLOT *op; - unsigned int i; - - /* Envelope Generator */ - chip->eg_timer += chip->eg_timer_add; - - while (chip->eg_timer >= chip->eg_timer_overflow) - { - chip->eg_timer -= chip->eg_timer_overflow; - - chip->eg_cnt++; - - for (i=0; i<9*2; i++) - { - CH = &chip->P_CH[i/2]; - - op = &CH->SLOT[i&1]; - - switch(op->state) - { - - case EG_DMP: /* dump phase */ - /*dump phase is performed by both operators in each channel*/ - /*when CARRIER envelope gets down to zero level, - ** phases in BOTH opearators are reset (at the same time ?) - */ - if ( !(chip->eg_cnt & ((1<eg_sh_dp)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_dp + ((chip->eg_cnt>>op->eg_sh_dp)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_ATT; - /* restart Phase Generator */ - op->phase = 0; - } - } - break; - - case EG_ATT: /* attack phase */ - if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) - { - op->volume += (~op->volume * - (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) - ) >>2; - - if (op->volume <= MIN_ATT_INDEX) - { - op->volume = MIN_ATT_INDEX; - op->state = EG_DEC; - } - } - break; - - case EG_DEC: /* decay phase */ - if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; - - if ( op->volume >= (INT32)(op->sl) ) - op->state = EG_SUS; - } - break; - - case EG_SUS: /* sustain phase */ - /* this is important behaviour: - one can change percusive/non-percussive modes on the fly and - the chip will remain in sustain phase - verified on real YM3812 */ - - if(op->eg_type) /* non-percussive mode (sustained tone) */ - { - /* do nothing */ - } - else /* percussive mode */ - { - /* during sustain phase chip adds Release Rate (in percussive mode) */ - if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; - - if ( op->volume >= MAX_ATT_INDEX ) - op->volume = MAX_ATT_INDEX; - } - /* else do nothing in sustain phase */ - } - break; - - case EG_REL: /* release phase */ - /* exclude modulators in melody channels from performing anything in this mode*/ - /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */ - - /*This table shows which operators and on what conditions are allowed to perform EG_REL: - (a) - always perform EG_REL - (n) - never perform EG_REL - (r) - perform EG_REL in Rhythm mode ONLY - 0: 0 (n), 1 (a) - 1: 2 (n), 3 (a) - 2: 4 (n), 5 (a) - 3: 6 (n), 7 (a) - 4: 8 (n), 9 (a) - 5: 10(n), 11(a) - 6: 12(r), 13(a) - 7: 14(r), 15(a) - 8: 16(r), 17(a) - */ - if ( (i&1) || ((chip->rhythm&0x20) && (i>=12)) )/* exclude modulators */ - { - if(op->eg_type) /* non-percussive mode (sustained tone) */ - /*this is correct: use RR when SUS = OFF*/ - /*and use RS when SUS = ON*/ - { - if (CH->sus) - { - if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)]; - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - } - } - else - { - if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - } - } - } - else /* percussive mode */ - { - if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) ) - { - op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)]; - if ( op->volume >= MAX_ATT_INDEX ) - { - op->volume = MAX_ATT_INDEX; - op->state = EG_OFF; - } - } - } - } - break; - - default: - break; - } - } - } - - for (i=0; i<9*2; i++) - { - CH = &chip->P_CH[i/2]; - op = &CH->SLOT[i&1]; - - /* Phase Generator */ - if(op->vib) - { - UINT8 block; - - unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6); - unsigned int block_fnum = CH->block_fnum * 2; - signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + fnum_lfo ]; - - if (lfo_fn_table_index_offset) /* LFO phase modulation active */ - { - block_fnum += lfo_fn_table_index_offset; - block = (block_fnum&0x1c00) >> 10; - op->phase += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; - } - else /* LFO phase modulation = zero */ - { - op->phase += op->freq; - } - } - else /* LFO phase modulation disabled for this operator */ - { - op->phase += op->freq; - } - } - - /* The Noise Generator of the YM3812 is 23-bit shift register. - * Period is equal to 2^23-2 samples. - * Register works at sampling frequency of the chip, so output - * can change on every sample. - * - * Output of the register and input to the bit 22 is: - * bit0 XOR bit14 XOR bit15 XOR bit22 - * - * Simply use bit 22 as the noise output. - */ - - chip->noise_p += chip->noise_f; - i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ - chip->noise_p &= FREQ_MASK; - while (i) - { - /* - UINT32 j; - j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; - chip->noise_rng = (j<<22) | (chip->noise_rng>>1); - */ - - /* - Instead of doing all the logic operations above, we - use a trick here (and use bit 0 as the noise output). - The difference is only that the noise bit changes one - step ahead. This doesn't matter since we don't know - what is real state of the noise_rng after the reset. - */ - - if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; - chip->noise_rng >>= 1; - - i--; - } -} - - -INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) -{ - UINT32 p; - - p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ]; - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; -} - -INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) -{ - UINT32 p; - INT32 i; - - i = (phase & ~FREQ_MASK) + pm; - -/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/ - - p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)]; - -/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ - - if (p >= TL_TAB_LEN) - return 0; - return tl_tab[p]; -} - - -#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) - -/* calculate output */ -INLINE void chan_calc( YM2413 *chip, OPLL_CH *CH ) -{ - OPLL_SLOT *SLOT; - unsigned int env; - signed int out; - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - - - /* SLOT 1 */ - SLOT = &CH->SLOT[SLOT1]; - env = volume_calc(SLOT); - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - - SLOT->op1_out[0] = SLOT->op1_out[1]; - phase_modulation = SLOT->op1_out[0]; - - SLOT->op1_out[1] = 0; - - if( env < ENV_QUIET ) - { - if (!SLOT->fb_shift) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); - } - - /* SLOT 2 */ - - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - { - signed int outp = op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); - chip->output[0] += outp; - /* output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); */ - } -} - -/* - operators used in the rhythm sounds generation process: - - Envelope Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal - 6 / 0 12 50 70 90 f0 + - 6 / 1 15 53 73 93 f3 + - 7 / 0 13 51 71 91 f1 + - 7 / 1 16 54 74 94 f4 + - 8 / 0 14 52 72 92 f2 + - 8 / 1 17 55 75 95 f5 + - - Phase Generator: - -channel operator register number Bass High Snare Tom Top -/ slot number MULTIPLE Drum Hat Drum Tom Cymbal - 6 / 0 12 30 + - 6 / 1 15 33 + - 7 / 0 13 31 + + + - 7 / 1 16 34 ----- n o t u s e d ----- - 8 / 0 14 32 + - 8 / 1 17 35 + + - -channel operator register number Bass High Snare Tom Top -number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal - 6 12,15 B6 A6 + - - 7 13,16 B7 A7 + + + - - 8 14,17 B8 A8 + + + - -*/ - -/* calculate rhythm */ - -INLINE void rhythm_calc( YM2413 *chip, OPLL_CH *CH, unsigned int noise ) -{ - OPLL_SLOT *SLOT; - signed int out; - unsigned int env; - signed int phase_modulation; /* phase modulation input (SLOT 2) */ - - - /* Bass Drum (verified on real YM3812): - - depends on the channel 6 'connect' register: - when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) - when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored - - output sample always is multiplied by 2 - */ - - - /* SLOT 1 */ - if ( !(chip->mask & OPLL_MASK_BD) ) - { - SLOT = &CH[6].SLOT[SLOT1]; - env = volume_calc(SLOT); - - out = SLOT->op1_out[0] + SLOT->op1_out[1]; - SLOT->op1_out[0] = SLOT->op1_out[1]; - - phase_modulation = SLOT->op1_out[0]; - - SLOT->op1_out[1] = 0; - if( env < ENV_QUIET ) - { - if (!SLOT->fb_shift) - out = 0; - SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); - } - - /* SLOT 2 */ - SLOT++; - env = volume_calc(SLOT); - if( env < ENV_QUIET ) - chip->output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2; - } - - - /* Phase generation is based on: - HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) - SD (16) channel 7->slot 1 - TOM (14) channel 8->slot 1 - TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */ - - /* Envelope generation based on: - HH channel 7->slot1 - SD channel 7->slot2 - TOM channel 8->slot1 - TOP channel 8->slot2 */ - - - /* The following formulas can be well optimized. - I leave them in direct form for now (in case I've missed something). - */ - - /* High Hat (verified on real YM3812) */ - if ( !(chip->mask & OPLL_MASK_HH) ) - { - env = volume_calc(chip->SLOT7_1); - if( env < ENV_QUIET ) - { - - /* high hat phase generation: - phase = d0 or 234 (based on frequency only) - phase = 34 or 2d0 (based on noise) - */ - - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((chip->SLOT7_1->phase>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((chip->SLOT7_1->phase>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((chip->SLOT7_1->phase>>FREQ_SH)>>2)&1; - - unsigned char res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0xd0; */ - /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ - UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((chip->SLOT8_2->phase>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((chip->SLOT8_2->phase>>FREQ_SH)>>3)&1; - - unsigned char res2 = (bit3e | bit5e); - - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ - if (res2) - phase = (0x200|(0xd0>>2)); - - - /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ - /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ - if (phase&0x200) - { - if (noise) - phase = 0x200|0xd0; - } - else - /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ - /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ - { - if (noise) - phase = 0xd0>>2; - } - - chip->output[1] += op_calc(phase<SLOT7_1->wavetable) * 2; - } - } - - /* Snare Drum (verified on real YM3812) */ - if ( !(chip->mask & OPLL_MASK_SD) ) - { - env = volume_calc(chip->SLOT7_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit8 = ((chip->SLOT7_1->phase>>FREQ_SH)>>8)&1; - - /* when bit8 = 0 phase = 0x100; */ - /* when bit8 = 1 phase = 0x200; */ - UINT32 phase = bit8 ? 0x200 : 0x100; - - /* Noise bit XOR'es phase by 0x100 */ - /* when noisebit = 0 pass the phase from calculation above */ - /* when noisebit = 1 phase ^= 0x100; */ - /* in other words: phase ^= (noisebit<<8); */ - if (noise) - phase ^= 0x100; - - chip->output[1] += op_calc(phase<SLOT7_2->wavetable) * 2; - } - } - - /* Tom Tom (verified on real YM3812) */ - if ( !(chip->mask & OPLL_MASK_TOM) ) - { - env = volume_calc(chip->SLOT8_1); - if( env < ENV_QUIET ) - chip->output[1] += op_calc(chip->SLOT8_1->phase, env, 0, chip->SLOT8_1->wavetable) * 2; - } - - /* Top Cymbal (verified on real YM2413) */ - if ( !(chip->mask & OPLL_MASK_CYM) ) - { - env = volume_calc(chip->SLOT8_2); - if( env < ENV_QUIET ) - { - /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((chip->SLOT7_1->phase>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((chip->SLOT7_1->phase>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((chip->SLOT7_1->phase>>FREQ_SH)>>2)&1; - - unsigned char res1 = (bit2 ^ bit7) | bit3; - - /* when res1 = 0 phase = 0x000 | 0x100; */ - /* when res1 = 1 phase = 0x200 | 0x100; */ - UINT32 phase = res1 ? 0x300 : 0x100; - - /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((chip->SLOT8_2->phase>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((chip->SLOT8_2->phase>>FREQ_SH)>>3)&1; - - unsigned char res2 = (bit3e | bit5e); - /* when res2 = 0 pass the phase from calculation above (res1); */ - /* when res2 = 1 phase = 0x200 | 0x100; */ - if (res2) - phase = 0x300; - - chip->output[1] += op_calc(phase<SLOT8_2->wavetable) * 2; - } - } -} - - -/* generic table initialize */ -static int init_tables(void) -{ - signed int i,x; - signed int n; - double o,m; - - - for (x=0; x>= 4; /* 12 bits here */ - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - /* 11 bits here (rounded) */ - tl_tab[ x*2 + 0 ] = n; - tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; - - for (i=1; i<11; i++) - { - tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; - tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; - } - #if 0 - logerror("tl %04i", x*2); - for (i=0; i<11; i++) - logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); - logerror("\n"); - #endif - } - /*logerror("ym2413.c: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ - - - for (i=0; i0.0) - o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ - else - o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ - - o = o / (ENV_STEP/4); - - n = (int)(2.0*o); - if (n&1) /* round to nearest */ - n = (n>>1)+1; - else - n = n>>1; - - /* waveform 0: standard sinus */ - sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); - - /*logerror("ym2413.c: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ - - - /* waveform 1: __ __ */ - /* / \____/ \____*/ - /* output only first half of the sinus waveform (positive one) */ - if (i & (1<<(SIN_BITS-1)) ) - sin_tab[1*SIN_LEN+i] = TL_TAB_LEN; - else - sin_tab[1*SIN_LEN+i] = sin_tab[i]; - - /*logerror("ym2413.c: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );*/ - } -#if 0 - logerror("YM2413.C: ENV_QUIET= %08x (*32=%08x)\n", ENV_QUIET, ENV_QUIET*32 ); - for (i=0; ifreqbase = (chip->rate) ? ((double)chip->clock / 72.0) / chip->rate : 0; - if ( fabs( chip->freqbase - 1.0 ) < 0.0000001 ) - chip->freqbase = 1.0; -#if 0 - chip->rate = (double)chip->clock / 72.0; - chip->freqbase = 1.0; - logerror("freqbase=%f\n", chip->freqbase); -#endif - - - - /* make fnumber -> increment counter table */ - for( i = 0 ; i < 1024; i++ ) - { - /* OPLL (YM2413) phase increment counter = 18bit */ - - chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ -#if 0 - logerror("ym2413.c: fn_tab[%4i] = %08x (dec=%8i)\n", - i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); -#endif - } - -#if 0 - for( i=0 ; i < 16 ; i++ ) - { - logerror("ym2413.c: sl_tab[%i] = %08x\n", i, sl_tab[i] ); - } - for( i=0 ; i < 8 ; i++ ) - { - int j; - logerror("ym2413.c: ksl_tab[oct=%2i] =",i); - for (j=0; j<16; j++) - { - logerror("%08x ", ksl_tab[i*16+j] ); - } - logerror("\n"); - } -#endif - - - /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ - /* One entry from LFO_AM_TABLE lasts for 64 samples */ - chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; - - /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ - chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; - - /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ - - /* Noise generator: a step takes 1 sample */ - chip->noise_f = (1.0 / 1.0) * (1<freqbase; - /*logerror("YM2413init noise_f=%8x\n", chip->noise_f);*/ - - chip->eg_timer_add = (1<freqbase; - chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ -} - -INLINE void KEY_ON(OPLL_SLOT *SLOT, UINT32 key_set) -{ - if( !SLOT->key ) - { - /* do NOT restart Phase Generator (verified on real YM2413)*/ - /* phase -> Dump */ - SLOT->state = EG_DMP; - } - SLOT->key |= key_set; -} - -INLINE void KEY_OFF(OPLL_SLOT *SLOT, UINT32 key_clr) -{ - if( SLOT->key ) - { - SLOT->key &= key_clr; - - if( !SLOT->key ) - { - /* phase -> Release */ - if (SLOT->state>EG_REL) - SLOT->state = EG_REL; - } - } -} - -/* update phase increment counter of operator (also update the EG rates if necessary) */ -INLINE void CALC_FCSLOT(OPLL_CH *CH,OPLL_SLOT *SLOT) -{ - int ksr; - UINT32 SLOT_rs; - UINT32 SLOT_dp; - - /* (frequency) phase increment counter */ - SLOT->freq = CH->fc * SLOT->mul; - ksr = CH->kcode >> SLOT->KSR; - - if( SLOT->ksr != ksr ) - { - SLOT->ksr = ksr; - - /* calculate envelope generator rates */ - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; - - } - - if (CH->sus) - SLOT_rs = 16 + (5<<2); - else - SLOT_rs = 16 + (7<<2); - - SLOT->eg_sh_rs = eg_rate_shift [SLOT_rs + SLOT->ksr ]; - SLOT->eg_sel_rs = eg_rate_select[SLOT_rs + SLOT->ksr ]; - - SLOT_dp = 16 + (13<<2); - SLOT->eg_sh_dp = eg_rate_shift [SLOT_dp + SLOT->ksr ]; - SLOT->eg_sel_dp = eg_rate_select[SLOT_dp + SLOT->ksr ]; -} - -/* set multi,am,vib,EG-TYP,KSR,mul */ -INLINE void set_mul(YM2413 *chip,int slot,int v) -{ - OPLL_CH *CH = &chip->P_CH[slot/2]; - OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->mul = mul_tab[v&0x0f]; - SLOT->KSR = (v&0x10) ? 0 : 2; - SLOT->eg_type = (v&0x20); - SLOT->vib = (v&0x40); - SLOT->AMmask = (v&0x80) ? ~0 : 0; - CALC_FCSLOT(CH,SLOT); -} - -/* set ksl, tl */ -INLINE void set_ksl_tl(YM2413 *chip,int chan,int v) -{ - int ksl; - OPLL_CH *CH = &chip->P_CH[chan]; -/* modulator */ - OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; - - ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */ - - SLOT->ksl = ksl ? 3-ksl : 31; - SLOT->TL = (v&0x3f)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); -} - -/* set ksl , waveforms, feedback */ -INLINE void set_ksl_wave_fb(YM2413 *chip,int chan,int v) -{ - int ksl; - OPLL_CH *CH = &chip->P_CH[chan]; -/* modulator */ - OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; - SLOT->wavetable = ((v&0x08)>>3)*SIN_LEN; - SLOT->fb_shift = (v&7) ? (v&7) + 8 : 0; - -/*carrier*/ - SLOT = &CH->SLOT[SLOT2]; - ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */ - - SLOT->ksl = ksl ? 3-ksl : 31; - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - - SLOT->wavetable = ((v&0x10)>>4)*SIN_LEN; -} - -/* set attack rate & decay rate */ -INLINE void set_ar_dr(YM2413 *chip,int slot,int v) -{ - OPLL_CH *CH = &chip->P_CH[slot/2]; - OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; - - if ((SLOT->ar + SLOT->ksr) < 16+62) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 13*RATE_STEPS; - } - - SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; - SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; -} - -/* set sustain level & release rate */ -INLINE void set_sl_rr(YM2413 *chip,int slot,int v) -{ - OPLL_CH *CH = &chip->P_CH[slot/2]; - OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->sl = sl_tab[ v>>4 ]; - - SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; - SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; -} - -static void load_instrument(YM2413 *chip, UINT32 chan, UINT32 slot, UINT8* inst ) -{ - set_mul (chip, slot, inst[0]); - set_mul (chip, slot+1, inst[1]); - set_ksl_tl (chip, chan, inst[2]); - set_ksl_wave_fb (chip, chan, inst[3]); - set_ar_dr (chip, slot, inst[4]); - set_ar_dr (chip, slot+1, inst[5]); - set_sl_rr (chip, slot, inst[6]); - set_sl_rr (chip, slot+1, inst[7]); -} -static void update_instrument_zero(YM2413 *chip, UINT8 r ) -{ - UINT8* inst = &chip->inst_tab[0][0]; /* point to user instrument */ - UINT32 chan; - UINT32 chan_max; - - chan_max = 9; - if (chip->rhythm & 0x20) - chan_max=6; - - switch(r) - { - case 0: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_mul (chip, chan*2, inst[0]); - } - } - break; - case 1: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_mul (chip, chan*2+1,inst[1]); - } - } - break; - case 2: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_ksl_tl (chip, chan, inst[2]); - } - } - break; - case 3: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_ksl_wave_fb (chip, chan, inst[3]); - } - } - break; - case 4: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_ar_dr (chip, chan*2, inst[4]); - } - } - break; - case 5: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_ar_dr (chip, chan*2+1,inst[5]); - } - } - break; - case 6: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_sl_rr (chip, chan*2, inst[6]); - } - } - break; - case 7: - for (chan=0; chaninstvol_r[chan]&0xf0)==0) - { - set_sl_rr (chip, chan*2+1,inst[7]); - } - } - break; - } -} - -/* write a value v to register r on chip chip */ -static void OPLLWriteReg(YM2413 *chip, int r, int v) -{ - OPLL_CH *CH; - OPLL_SLOT *SLOT; - UINT8 *inst; - int chan; - int slot; - - /* adjust bus to 8 bits */ - r &= 0xff; - v &= 0xff; - - - switch(r&0xf0) - { - case 0x00: /* 00-0f:control */ - { - switch(r&0x0f) - { - case 0x00: /* AM/VIB/EGTYP/KSR/MULTI (modulator) */ - case 0x01: /* AM/VIB/EGTYP/KSR/MULTI (carrier) */ - case 0x02: /* Key Scale Level, Total Level (modulator) */ - case 0x03: /* Key Scale Level, carrier waveform, modulator waveform, Feedback */ - case 0x04: /* Attack, Decay (modulator) */ - case 0x05: /* Attack, Decay (carrier) */ - case 0x06: /* Sustain, Release (modulator) */ - case 0x07: /* Sustain, Release (carrier) */ - chip->inst_tab[0][r & 0x07] = v; - update_instrument_zero(chip,r&7); - break; - - case 0x0e: /* x, x, r,bd,sd,tom,tc,hh */ - { - if(v&0x20) - { - if ((chip->rhythm&0x20)==0) - /*rhythm off to on*/ - { - /*logerror("YM2413: Rhythm mode enable\n");*/ - - /* Load instrument settings for channel seven(chan=6 since we're zero based). (Bass drum) */ - chan = 6; - inst = &chip->inst_tab[16][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - - /* Load instrument settings for channel eight. (High hat and snare drum) */ - chan = 7; - inst = &chip->inst_tab[17][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - - CH = &chip->P_CH[chan]; - SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH */ - SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - - /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */ - chan = 8; - inst = &chip->inst_tab[18][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - - CH = &chip->P_CH[chan]; - SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */ - SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - /* BD key on/off */ - if(v&0x10) - { - KEY_ON (&chip->P_CH[6].SLOT[SLOT1], 2); - KEY_ON (&chip->P_CH[6].SLOT[SLOT2], 2); - } - else - { - KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2); - KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2); - } - /* HH key on/off */ - if(v&0x01) KEY_ON (&chip->P_CH[7].SLOT[SLOT1], 2); - else KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2); - /* SD key on/off */ - if(v&0x08) KEY_ON (&chip->P_CH[7].SLOT[SLOT2], 2); - else KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2); - /* TOM key on/off */ - if(v&0x04) KEY_ON (&chip->P_CH[8].SLOT[SLOT1], 2); - else KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY key on/off */ - if(v&0x02) KEY_ON (&chip->P_CH[8].SLOT[SLOT2], 2); - else KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2); - } - else - { - if ((chip->rhythm&0x20)==1) - /*rhythm on to off*/ - { - /*logerror("YM2413: Rhythm mode disable\n");*/ - /* Load instrument settings for channel seven(chan=6 since we're zero based).*/ - chan = 6; - inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - - /* Load instrument settings for channel eight.*/ - chan = 7; - inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - - /* Load instrument settings for channel nine.*/ - chan = 8; - inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - } - /* BD key off */ - KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2); - KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2); - /* HH key off */ - KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2); - /* SD key off */ - KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2); - /* TOM key off */ - KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2); - /* TOP-CY off */ - KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2); - } - chip->rhythm = v&0x3f; - } - break; - } - } - break; - - case 0x10: - case 0x20: - { - UINT32 block_fnum; - - chan = r&0x0f; - - if (chan >= 9) - chan -= 9; /* verified on real YM2413 */ - - CH = &chip->P_CH[chan]; - - if(r&0x10) - { /* 10-18: FNUM 0-7 */ - block_fnum = (CH->block_fnum&0x0f00) | v; - } - else - { /* 20-28: suson, keyon, block, FNUM 8 */ - block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff); - - if(v&0x10) - { - KEY_ON (&CH->SLOT[SLOT1], 1); - KEY_ON (&CH->SLOT[SLOT2], 1); - } - else - { - KEY_OFF(&CH->SLOT[SLOT1],~1); - KEY_OFF(&CH->SLOT[SLOT2],~1); - } - - - /*if (CH->sus!=(v&0x20)) - logerror("chan=%i sus=%2x\n",chan,v&0x20);*/ - - CH->sus = v & 0x20; - } - /* update */ - if(CH->block_fnum != block_fnum) - { - UINT8 block; - - CH->block_fnum = block_fnum; - - /* BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB */ - CH->kcode = (block_fnum&0x0f00)>>8; - - CH->ksl_base = ksl_tab[block_fnum>>5]; - - block_fnum = block_fnum * 2; - block = (block_fnum&0x1c00) >> 10; - CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); - - /* refresh Total Level in both SLOTs of this channel */ - CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); - CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); - - /* refresh frequency counter in both SLOTs of this channel */ - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - } - } - break; - - case 0x30: /* inst 4 MSBs, VOL 4 LSBs */ - { - UINT8 old_instvol; - - chan = r&0x0f; - - if (chan >= 9) - chan -= 9; /* verified on real YM2413 */ - - old_instvol = chip->instvol_r[chan]; - chip->instvol_r[chan] = v; /* store for later use */ - - CH = &chip->P_CH[chan]; - SLOT = &CH->SLOT[SLOT2]; /* carrier */ - SLOT->TL = ((v&0x0f)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - - - /*check wether we are in rhythm mode and handle instrument/volume register accordingly*/ - if ((chan>=6) && (chip->rhythm&0x20)) - { - /* we're in rhythm mode*/ - - if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/ - { - SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */ - SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } - } - else - { - if ( (old_instvol&0xf0) == (v&0xf0) ) - return; - - inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; - slot = chan*2; - - load_instrument(chip, chan, slot, inst); - - #if 0 - logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v); - logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]); - logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]); - #endif - } - } - break; - - default: - break; - } -} - -/* lock/unlock for common table */ -static void OPLLResetChip(YM2413 *chip) -{ - int c,s; - int i; - - chip->eg_timer = 0; - chip->eg_cnt = 0; - - chip->noise_rng = 1; /* noise shift register */ - - chip->mask = 0; - - /* setup instruments table */ - if (!chip->chip_type) - { - for (i=0; i<19; i++) - { - for (c=0; c<8; c++) - { - chip->inst_tab[i][c] = table[i][c]; - } - } - } - else - { - memset( &chip->inst_tab, 0, sizeof(chip->inst_tab) ); - - for (i=0; i<15; i++) - { - for (c=0; c<8; c++) - { - chip->inst_tab[i+1][c] = table_vrc7[i][c]; - } - } - } - - - /* reset with register write */ - OPLLWriteReg(chip,0x0f,0); /*test reg*/ - for(i = 0x3f ; i >= 0x10 ; i-- ) OPLLWriteReg(chip,i,0x00); - - /* reset operator parameters */ - for( c = 0 ; c < 9 ; c++ ) - { - OPLL_CH *CH = &chip->P_CH[c]; - for(s = 0 ; s < 2 ; s++ ) - { - /* wave table */ - CH->SLOT[s].wavetable = 0; - CH->SLOT[s].state = EG_OFF; - CH->SLOT[s].volume = MAX_ATT_INDEX; - } - } -} - -/* Create one of virtual YM2413 */ -/* 'clock' is chip clock in Hz */ -/* 'rate' is sampling rate */ -static YM2413 *OPLLCreate(int clock, int rate, int type) -{ - char *ptr; - YM2413 *chip; - int state_size; - - init_tables(); - - /* calculate chip state size */ - state_size = sizeof(YM2413); - - /* allocate memory block */ - ptr = (char *)malloc(state_size); - - if (ptr==NULL) - return NULL; - - /* clear */ - memset(ptr,0,state_size); - - chip = (YM2413 *)ptr; - - chip->clock = clock; - chip->rate = rate; - - chip->chip_type = type; - - chip->mask = 0; - - /* init global tables */ - OPLL_initalize(chip); - - /* reset chip */ - OPLLResetChip(chip); - return chip; -} - -/* Destroy one of virtual YM3812 */ -static void OPLLDestroy(YM2413 *chip) -{ - free(chip); -} - -/* Option handlers */ - -static void OPLLSetUpdateHandler(YM2413 *chip,OPLL_UPDATEHANDLER UpdateHandler,void * param) -{ - chip->UpdateHandler = UpdateHandler; - chip->UpdateParam = param; -} - -/* YM3812 I/O interface */ -static void OPLLWrite(YM2413 *chip,int a,int v) -{ - if( !(a&1) ) - { /* address port */ - chip->address = v & 0xff; - } - else - { /* data port */ - if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0); - OPLLWriteReg(chip,chip->address,v); - } -} - -static unsigned char OPLLRead(YM2413 *chip,int a) -{ - if( !(a&1) ) - { - /* status port */ - return chip->status; - } - return 0xff; -} - - - - - -void * ym2413_init(int clock, int rate, int type) -{ - /* emulator create */ - return OPLLCreate(clock, rate, type); -} - -void ym2413_shutdown(void *chip) -{ - YM2413 *OPLL = (YM2413 *)chip; - - /* emulator shutdown */ - OPLLDestroy(OPLL); -} - -void ym2413_reset_chip(void *chip) -{ - YM2413 *OPLL = (YM2413 *)chip; - OPLLResetChip(OPLL); -} - -void ym2413_write(void *chip, int a, int v) -{ - YM2413 *OPLL = (YM2413 *)chip; - OPLLWrite(OPLL, a, v); -} - -unsigned char ym2413_read(void *chip, int a) -{ - YM2413 *OPLL = (YM2413 *)chip; - return OPLLRead(OPLL, a) & 0x03 ; -} - -void ym2413_set_update_handler(void *chip,OPLL_UPDATEHANDLER UpdateHandler,void *param) -{ - YM2413 *OPLL = (YM2413 *)chip; - OPLLSetUpdateHandler(OPLL, UpdateHandler, param); -} - - -/* -** Generate samples for one of the YM2413's -** -** 'which' is the virtual YM2413 number -** '*buffer' is the output buffer pointer -** 'length' is the number of samples that should be generated -*/ -void ym2413_update_one(void *_chip, SAMP **buffers, int length) -{ - YM2413 *chip = (YM2413 *)_chip; - UINT8 rhythm = chip->rhythm&0x20; - SAMP *bufMO = buffers[0]; - SAMP *bufRO = buffers[1]; - - int i,j; - - chip->SLOT7_1 = &chip->P_CH[7].SLOT[SLOT1]; - chip->SLOT7_2 = &chip->P_CH[7].SLOT[SLOT2]; - chip->SLOT8_1 = &chip->P_CH[8].SLOT[SLOT1]; - chip->SLOT8_2 = &chip->P_CH[8].SLOT[SLOT2]; - - - for( i=0; i < length ; i++ ) - { - int mo,ro; - - chip->output[0] = 0; - chip->output[1] = 0; - - advance_lfo(chip); - -#if 0 - /* FM part */ - chan_calc(chip,&chip->P_CH[0]); -/* SAVE_SEPARATE_CHANNEL(0); */ - chan_calc(chip,&chip->P_CH[1]); - chan_calc(chip,&chip->P_CH[2]); - chan_calc(chip,&chip->P_CH[3]); - chan_calc(chip,&chip->P_CH[4]); - chan_calc(chip,&chip->P_CH[5]); -#else - for ( j=0; j < 6; j++ ) - { - if (!(chip->mask & OPLL_MASK_CH(j))) chan_calc(chip, &chip->P_CH[j]); - } -#endif - - if(!rhythm) - { -#if 0 - chan_calc(chip,&chip->P_CH[6]); - chan_calc(chip,&chip->P_CH[7]); - chan_calc(chip,&chip->P_CH[8]); -#else - for ( j=6; j < 9; j++ ) - { - if (!(chip->mask & OPLL_MASK_CH(j))) chan_calc(chip, &chip->P_CH[j]); - } -#endif - } - else /* Rhythm part */ - { - if ( ( chip->mask & OPLL_MASK_RHYTHM ) != OPLL_MASK_RHYTHM ) - rhythm_calc(chip,&chip->P_CH[0], (chip->noise_rng>>0)&1 ); - } - - mo = chip->output[0]; - ro = chip->output[1]; - - mo >>= FINAL_SH; - ro >>= FINAL_SH; - - /* limit check */ - mo = limit( mo , MAXOUT, MINOUT ); - ro = limit( ro , MAXOUT, MINOUT ); - - /* store to sound buffer */ - bufMO[i] = mo; - bufRO[i] = ro; - - advance(chip); - } - -} - -void ym2413_advance_lfo(void *_chip) -{ - YM2413 *chip = (YM2413 *)_chip; - advance_lfo(chip); -} - -void ym2413_advance(void *_chip) -{ - YM2413 *chip = (YM2413 *)_chip; - advance(chip); -} - -SAMP ym2413_calcch(void *_chip, int ch) -{ - YM2413 *chip = (YM2413 *)_chip; - - int output; - - chip->output[0] = 0; - chip->output[1] = 0; - - if (ch >= 0 && ch < 6) chan_calc( chip, &chip->P_CH[ch] ); - else if (ch >= 6 && ch < 9) - { - UINT8 rhythm = chip->rhythm&0x20; - if (!rhythm) chan_calc( chip, &chip->P_CH[ch] ); - else if (ch == 6) rhythm_calc(chip,&chip->P_CH[0], (chip->noise_rng>>0)&1 ); - } - - output = chip->output[0]; - output += chip->output[1]; - - return output; -} - -void * ym2413_get_inst0(void *_chip) -{ - YM2413 *chip = (YM2413 *)_chip; - - return &chip->inst_tab; -} - -void ym2413_set_mask(void *_chip, UINT32 mask) -{ - YM2413 *chip = (YM2413 *)_chip; - - chip->mask = mask; -} +/* +** +** File: ym2413.c - software implementation of YM2413 +** FM sound generator type OPLL +** +** Copyright Jarek Burczynski +** +** Version 1.0 +** + + Features as listed in LSI-212413A2 data sheet: + 1. FM Sound Generator for real sound creation. + 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds + (different tones can be used together in either case). + 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones). + 4. Built-in DA Converter. + 5. Built-in Quartz Oscillator. + 6. Built-in Vibrato Oscillator/AM Oscillator + 7. TTL Compatible Input. + 8. Si-Gate NMOS LSI + 9. A single 5V power source. + +to do: + +- make sure of the sinus amplitude bits + +- make sure of the EG resolution bits (looks like the biggest + modulation index generated by the modulator is 123, 124 = no modulation) +- find proper algorithm for attack phase of EG + +- tune up instruments ROM + +- support sample replay in test mode (it is NOT as simple as setting bit 0 + in register 0x0f and using register 0x10 for sample data). + Which games use this feature ? + + +*/ + +#include +#include +#include "mamedef.h" +#include +#include +//#include "sndintrf.h" +#include "ym2413.h" + +#define NULL ((void *)0) + +/* output final shift */ +#if (SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ + +#define FREQ_MASK ((1< + +INLINE signed int acc_calc(signed int value) +{ + if (value>=0) + { + if (value < 0x0200) + return (value & ~0); + if (value < 0x0400) + return (value & ~1); + if (value < 0x0800) + return (value & ~3); + if (value < 0x1000) + return (value & ~7); + if (value < 0x2000) + return (value & ~15); + if (value < 0x4000) + return (value & ~31); + return (value & ~63); + } + /*else value < 0*/ + if (value > -0x0200) + return (~abs(value) & ~0); + if (value > -0x0400) + return (~abs(value) & ~1); + if (value > -0x0800) + return (~abs(value) & ~3); + if (value > -0x1000) + return (~abs(value) & ~7); + if (value > -0x2000) + return (~abs(value) & ~15); + if (value > -0x4000) + return (~abs(value) & ~31); + return (~abs(value) & ~63); +} + + +static FILE *sample[1]; + #if 0 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = acc_calc(mo); \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = mo; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = ro; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #define SAVE_SEPARATE_CHANNEL(j) \ + { signed int pom = outchan; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = chip->instvol_r[j]>>4; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + +//#define LOG_CYM_FILE 0 +//static FILE * cymfile = NULL; + + + + +typedef struct{ + UINT32 ar; /* attack rate: AR<<2 */ + UINT32 dr; /* decay rate: DR<<2 */ + UINT32 rr; /* release rate:RR<<2 */ + UINT8 KSR; /* key scale rate */ + UINT8 ksl; /* keyscale level */ + UINT8 ksr; /* key scale rate: kcode>>KSR */ + UINT8 mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + UINT32 phase; /* frequency counter */ + UINT32 freq; /* frequency counter step */ + UINT8 fb_shift; /* feedback shift value */ + INT32 op1_out[2]; /* slot1 output for feedback */ + + /* Envelope Generator */ + UINT8 eg_type; /* percussive/nonpercussive mode*/ + UINT8 state; /* phase type */ + UINT32 TL; /* total level: TL << 2 */ + INT32 TLL; /* adjusted now TL */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level: sl_tab[SL] */ + + UINT8 eg_sh_dp; /* (dump state) */ + UINT8 eg_sel_dp; /* (dump state) */ + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_dr; /* (decay state) */ + UINT8 eg_sel_dr; /* (decay state) */ + UINT8 eg_sh_rr; /* (release state for non-perc.)*/ + UINT8 eg_sel_rr; /* (release state for non-perc.)*/ + UINT8 eg_sh_rs; /* (release state for perc.mode)*/ + UINT8 eg_sel_rs; /* (release state for perc.mode)*/ + + UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ + UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + unsigned int wavetable; +} OPLL_SLOT; + +typedef struct{ + OPLL_SLOT SLOT[2]; + /* phase generator state */ + UINT32 block_fnum; /* block+fnum */ + UINT32 fc; /* Freq. freqement base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 kcode; /* key code (for key scaling) */ + UINT8 sus; /* sus on/off (release speed in percussive mode)*/ + UINT8 Muted; +} OPLL_CH; + +/* chip state */ +typedef struct { + OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/ + UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/ + UINT8 MuteSpc[5]; /* Mute Special: 5 Rhythm */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ + + UINT8 rhythm; /* Rhythm mode */ + + /* LFO */ + UINT32 LFO_AM; + INT32 LFO_PM; + UINT32 lfo_am_cnt; + UINT32 lfo_am_inc; + UINT32 lfo_pm_cnt; + UINT32 lfo_pm_inc; + + UINT32 noise_rng; /* 23 bit noise shift register */ + UINT32 noise_p; /* current noise 'phase' */ + UINT32 noise_f; /* current noise period */ + + +/* instrument settings */ +/* + 0-user instrument + 1-15 - fixed instruments + 16 -bass drum settings + 17,18 - other percussion instruments +*/ + UINT8 inst_tab[19][8]; + + /* external event callback handlers */ + OPLL_UPDATEHANDLER UpdateHandler; /* stream update handler */ + void * UpdateParam; /* stream update parameter */ + + UINT32 fn_tab[1024]; /* fnumber->increment counter */ + + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + + UINT8 VRC7_Mode; + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + + signed int output[2]; + signed int outchan; +} YM2413; + +/* key scale level */ +/* table is 3dB/octave, DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/1.0) +static const UINT32 ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; +#undef DV + +/* 0 / 1.5 / 3.0 / 6.0 dB/OCT, confirmed on a real YM2413 (the application manual is incorrect) */ +static const UINT32 ksl_shift[4] = { 31, 2, 1, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB)*/ +#define SC(db) (UINT32) ( db * (1.0/ENV_STEP) ) +static const UINT32 sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 */ +/*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(13),O(13),O(13),O(13), +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const UINT8 mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, + 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML +}; +#undef ML + +/* TL_TAB_LEN is calculated as: +* 11 - sinus amplitude bits (Y axis) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (11*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>5) + +/* sin waveform table in 'decibel' scale */ +/* two waveforms on OPLL type chips */ +static unsigned int sin_tab[SIN_LEN * 2]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + +We use data>>1, until we find what it really is on real chip... + +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM2413) */ +static const INT8 lfo_pm_table[8*8] = { + +/* FNUM2/FNUM = 0 00xxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, + +/* FNUM2/FNUM = 0 01xxxxxx (0x0040) */ +1, 0, 0, 0,-1, 0, 0, 0, + +/* FNUM2/FNUM = 0 10xxxxxx (0x0080) */ +2, 1, 0,-1,-2,-1, 0, 1, + +/* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */ +3, 1, 0,-1,-3,-1, 0, 1, + +/* FNUM2/FNUM = 1 00xxxxxx (0x0100) */ +4, 2, 0,-2,-4,-2, 0, 2, + +/* FNUM2/FNUM = 1 01xxxxxx (0x0140) */ +5, 2, 0,-2,-5,-2, 0, 2, + +/* FNUM2/FNUM = 1 10xxxxxx (0x0180) */ +6, 3, 0,-3,-6,-3, 0, 3, + +/* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */ +7, 3, 0,-3,-7,-3, 0, 3, +}; + + + + + + +/* This is not 100% perfect yet but very close */ +/* + - multi parameters are 100% correct (instruments and drums) + - LFO PM and AM enable are 100% correct + - waveform DC and DM select are 100% correct +*/ + +static const unsigned char table[19][8] = { +/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ +/* 0 1 2 3 4 5 6 7 */ + {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, //0 + + {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, //1 + {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, //2 + {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, //3 + {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, //4 + +//{0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 + {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, //5 + +//{0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 + {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, //6 + + {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, //7 + {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, //8 + {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, //9 + +//{0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A + {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, //A + + {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, //B + {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, //C + +//{0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D + {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, //D + + {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, //E + {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, //F + +/* drum instruments definitions */ +/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ +/* 0 1 2 3 4 5 6 7 */ + {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ + {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ + {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ +}; + +/* lock level of common table */ +static int num_lock = 0; + +/* work table */ +#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) +#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) +#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) +#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) + + +/*INLINE int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +}*/ + + +/* advance LFO to next sample */ +INLINE void advance_lfo(YM2413 *chip) +{ + /* LFO */ + chip->lfo_am_cnt += chip->lfo_am_inc; + if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<LFO_AM = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ] >> 1; + + chip->lfo_pm_cnt += chip->lfo_pm_inc; + chip->LFO_PM = (chip->lfo_pm_cnt>>LFO_SH) & 7; +} + +/* advance to next sample */ +INLINE void advance(YM2413 *chip) +{ + OPLL_CH *CH; + OPLL_SLOT *op; + unsigned int i; + + /* Envelope Generator */ + chip->eg_timer += chip->eg_timer_add; + + while (chip->eg_timer >= chip->eg_timer_overflow) + { + chip->eg_timer -= chip->eg_timer_overflow; + + chip->eg_cnt++; + + for (i=0; i<9*2; i++) + { + CH = &chip->P_CH[i/2]; + + op = &CH->SLOT[i&1]; + + switch(op->state) + { + + case EG_DMP: /* dump phase */ + /*dump phase is performed by both operators in each channel*/ + /*when CARRIER envelope gets down to zero level, + ** phases in BOTH opearators are reset (at the same time ?) + */ + if ( !(chip->eg_cnt & ((1<eg_sh_dp)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dp + ((chip->eg_cnt>>op->eg_sh_dp)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_ATT; + /* restart Phase Generator */ + op->phase = 0; + } + } + break; + + case EG_ATT: /* attack phase */ + if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>2; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + } + break; + + case EG_DEC: /* decay phase */ + if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + } + break; + + case EG_SUS: /* sustain phase */ + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode (sustained tone) */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + /* exclude modulators in melody channels from performing anything in this mode*/ + /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */ + + /*This table shows which operators and on what conditions are allowed to perform EG_REL: + (a) - always perform EG_REL + (n) - never perform EG_REL + (r) - perform EG_REL in Rhythm mode ONLY + 0: 0 (n), 1 (a) + 1: 2 (n), 3 (a) + 2: 4 (n), 5 (a) + 3: 6 (n), 7 (a) + 4: 8 (n), 9 (a) + 5: 10(n), 11(a) + 6: 12(r), 13(a) + 7: 14(r), 15(a) + 8: 16(r), 17(a) + */ + if ( (i&1) || ((chip->rhythm&0x20) && (i>=12)) )/* exclude modulators */ + { + if(op->eg_type) /* non-percussive mode (sustained tone) */ + /*this is correct: use RR when SUS = OFF*/ + /*and use RS when SUS = ON*/ + { + if (CH->sus) + { + if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)]; + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + } + } + else + { + if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + } + } + } + else /* percussive mode */ + { + if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)]; + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + } + } + } + break; + + default: + break; + } + } + } + + for (i=0; i<9*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Phase Generator */ + if(op->vib) + { + UINT8 block; + + unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6); + unsigned int block_fnum = CH->block_fnum * 2; + signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->phase += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->phase += op->freq; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->phase += op->freq; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + chip->noise_p += chip->noise_f; + i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + chip->noise_p &= FREQ_MASK; + while (i) + { + /* + UINT32 j; + j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; + chip->noise_rng = (j<<22) | (chip->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; + chip->noise_rng >>= 1; + + i--; + } +} + + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + INT32 i; + + i = (phase & ~FREQ_MASK) + pm; + +/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/ + + p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)]; + +/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) + +/* calculate output */ +INLINE void chan_calc( YM2413*chip, OPLL_CH *CH ) +{ + OPLL_SLOT *SLOT; + unsigned int env; + signed int out; + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + + + if (CH->Muted) + return; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + + SLOT->op1_out[0] = SLOT->op1_out[1]; + phase_modulation = SLOT->op1_out[0]; + + SLOT->op1_out[1] = 0; + + if( env < ENV_QUIET ) + { + if (!SLOT->fb_shift) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); + } + + /* SLOT 2 */ + + chip->outchan=0; + + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + { + signed int outp = op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); + chip->output[0] += outp; + chip->outchan = outp; + //chip->output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); + } +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +INLINE void rhythm_calc( YM2413 *chip, OPLL_CH *CH, unsigned int noise ) +{ + OPLL_SLOT *SLOT; + signed int out; + unsigned int env; + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + phase_modulation = SLOT->op1_out[0]; + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->fb_shift) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET && ! chip->MuteSpc[0] ) + chip->output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) + // SD (16) channel 7->slot 1 + // TOM (14) channel 8->slot 1 + // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) + + /* Envelope generation based on: */ + // HH channel 7->slot1 + // SD channel 7->slot2 + // TOM channel 8->slot1 + // TOP channel 8->slot2 + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET && ! chip->MuteSpc[4] ) + { + + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e | bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + chip->output[1] += op_calc(phase<wavetable) * 2; + } + + /* Snare Drum (verified on real YM3812) */ + env = volume_calc(SLOT7_2); + if( env < ENV_QUIET && ! chip->MuteSpc[1] ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit8 = ((SLOT7_1->phase>>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + UINT32 phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + chip->output[1] += op_calc(phase<wavetable) * 2; + } + + /* Tom Tom (verified on real YM3812) */ + env = volume_calc(SLOT8_1); + if( env < ENV_QUIET && ! chip->MuteSpc[2] ) + chip->output[1] += op_calc(SLOT8_1->phase, env, 0, SLOT8_1->wavetable) * 2; + + /* Top Cymbal (verified on real YM2413) */ + env = volume_calc(SLOT8_2); + if( env < ENV_QUIET && ! chip->MuteSpc[3] ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + UINT32 phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e | bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + chip->output[1] += op_calc(phase<wavetable) * 2; + } + +} + + +/* generic table initialize */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + for (i=1; i<11; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + //#if 0 + // logerror("tl %04i", x*2); + // for (i=0; i<11; i++) + // logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); + // logerror("\n"); + //#endif*/ + } + /*logerror("ym2413.c: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ + + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* waveform 0: standard sinus */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("ym2413.c: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + + + /* waveform 1: __ __ */ + /* / \____/ \____*/ + /* output only first half of the sinus waveform (positive one) */ + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[1*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[1*SIN_LEN+i] = sin_tab[i]; + + /*logerror("ym2413.c: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );*/ + } +/*#if 0 + logerror("YM2413.C: ENV_QUIET= %08x (*32=%08x)\n", ENV_QUIET, ENV_QUIET*32 ); + for (i=0; iinstvol_r); + state_save_register_device_item(device, 0, chip->eg_cnt); + state_save_register_device_item(device, 0, chip->eg_timer); + state_save_register_device_item(device, 0, chip->eg_timer_add); + state_save_register_device_item(device, 0, chip->eg_timer_overflow); + state_save_register_device_item(device, 0, chip->rhythm); + state_save_register_device_item(device, 0, chip->lfo_am_cnt); + state_save_register_device_item(device, 0, chip->lfo_am_inc); + state_save_register_device_item(device, 0, chip->lfo_pm_cnt); + state_save_register_device_item(device, 0, chip->lfo_pm_inc); + state_save_register_device_item(device, 0, chip->noise_rng); + state_save_register_device_item(device, 0, chip->noise_p); + state_save_register_device_item(device, 0, chip->noise_f); + state_save_register_device_item_2d_array(device, 0, chip->inst_tab); + state_save_register_device_item(device, 0, chip->address); + state_save_register_device_item(device, 0, chip->status); + + for (chnum = 0; chnum < ARRAY_LENGTH(chip->P_CH); chnum++) + { + OPLL_CH *ch = &chip->P_CH[chnum]; + int slotnum; + + state_save_register_device_item(device, chnum, ch->block_fnum); + state_save_register_device_item(device, chnum, ch->fc); + state_save_register_device_item(device, chnum, ch->ksl_base); + state_save_register_device_item(device, chnum, ch->kcode); + state_save_register_device_item(device, chnum, ch->sus); + + for (slotnum = 0; slotnum < ARRAY_LENGTH(ch->SLOT); slotnum++) + { + OPLL_SLOT *sl = &ch->SLOT[slotnum]; + + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->ar); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->dr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->rr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->KSR); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->ksl); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->ksr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->mul); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->phase); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->freq); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->fb_shift); + state_save_register_device_item_array(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->op1_out); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_type); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->state); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->TL); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->TLL); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->volume); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->sl); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_dp); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_dp); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_ar); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_ar); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_dr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_dr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_rr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_rr); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sh_rs); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->eg_sel_rs); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->key); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->AMmask); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->vib); + state_save_register_device_item(device, chnum * ARRAY_LENGTH(ch->SLOT) + slotnum, sl->wavetable); + } + } +}*/ + + +//static void OPLL_initalize(YM2413 *chip, const device_config *device) +static void OPLL_initalize(YM2413 *chip) +{ + int i; + + //OPLL_init_save(chip, device); + + /* frequency base */ + chip->freqbase = (chip->rate) ? ((double)chip->clock / 72.0) / chip->rate : 0; +/*#if 0 + chip->rate = (double)chip->clock / 72.0; + chip->freqbase = 1.0; + logerror("freqbase=%f\n", chip->freqbase); +#endif*/ + + + + /* make fnumber -> increment counter table */ + for( i = 0 ; i < 1024; i++ ) + { + /* OPLL (YM2413) phase increment counter = 18bit */ + + chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +/*#if 0 + logerror("ym2413.c: fn_tab[%4i] = %08x (dec=%8i)\n", + i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); +#endif*/ + } + +/*#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("ym2413.c: sl_tab[%i] = %08x\n", i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("ym2413.c: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", ksl_tab[i*16+j] ); + } + logerror("\n"); + } +#endif*/ + + for(i = 0; i < 9; i ++) + chip->P_CH[i].Muted = 0x00; + for(i = 0; i < 5; i ++) + chip->MuteSpc[i] = 0x00; + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; + + /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ + chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; + + /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ + + /* Noise generator: a step takes 1 sample */ + chip->noise_f = (1.0 / 1.0) * (1<freqbase; + /*logerror("YM2413init noise_f=%8x\n", chip->noise_f);*/ + + chip->eg_timer_add = (1<freqbase; + chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ +} + +INLINE void KEY_ON(OPLL_SLOT *SLOT, UINT32 key_set) +{ + if( !SLOT->key ) + { + /* do NOT restart Phase Generator (verified on real YM2413)*/ + /* phase -> Dump */ + SLOT->state = EG_DMP; + } + SLOT->key |= key_set; +} + +INLINE void KEY_OFF(OPLL_SLOT *SLOT, UINT32 key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +INLINE void CALC_FCSLOT(OPLL_CH *CH,OPLL_SLOT *SLOT) +{ + int ksr; + UINT32 SLOT_rs; + UINT32 SLOT_dp; + + /* (frequency) phase increment counter */ + SLOT->freq = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + + } + + if (CH->sus) + SLOT_rs = 16 + (5<<2); + else + SLOT_rs = 16 + (7<<2); + + SLOT->eg_sh_rs = eg_rate_shift [SLOT_rs + SLOT->ksr ]; + SLOT->eg_sel_rs = eg_rate_select[SLOT_rs + SLOT->ksr ]; + + SLOT_dp = 16 + (13<<2); + SLOT->eg_sh_dp = eg_rate_shift [SLOT_dp + SLOT->ksr ]; + SLOT->eg_sel_dp = eg_rate_select[SLOT_dp + SLOT->ksr ]; +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(YM2413 *chip,int slot,int v) +{ + OPLL_CH *CH = &chip->P_CH[slot/2]; + OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl, tl */ +INLINE void set_ksl_tl(YM2413 *chip,int chan,int v) +{ + OPLL_CH *CH = &chip->P_CH[chan]; +/* modulator */ + OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set ksl , waveforms, feedback */ +INLINE void set_ksl_wave_fb(YM2413 *chip,int chan,int v) +{ + OPLL_CH *CH = &chip->P_CH[chan]; +/* modulator */ + OPLL_SLOT *SLOT = &CH->SLOT[SLOT1]; + SLOT->wavetable = ((v&0x08)>>3)*SIN_LEN; + SLOT->fb_shift = (v&7) ? (v&7) + 8 : 0; + +/*carrier*/ + SLOT = &CH->SLOT[SLOT2]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + + SLOT->wavetable = ((v&0x10)>>4)*SIN_LEN; +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(YM2413 *chip,int slot,int v) +{ + OPLL_CH *CH = &chip->P_CH[slot/2]; + OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(YM2413 *chip,int slot,int v) +{ + OPLL_CH *CH = &chip->P_CH[slot/2]; + OPLL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + +static void load_instrument(YM2413 *chip, UINT32 chan, UINT32 slot, UINT8* inst ) +{ + set_mul (chip, slot, inst[0]); + set_mul (chip, slot+1, inst[1]); + set_ksl_tl (chip, chan, inst[2]); + set_ksl_wave_fb (chip, chan, inst[3]); + set_ar_dr (chip, slot, inst[4]); + set_ar_dr (chip, slot+1, inst[5]); + set_sl_rr (chip, slot, inst[6]); + set_sl_rr (chip, slot+1, inst[7]); +} +static void update_instrument_zero(YM2413 *chip, UINT8 r ) +{ + UINT8* inst = &chip->inst_tab[0][0]; /* point to user instrument */ + UINT32 chan; + UINT32 chan_max; + + chan_max = 9; + if (chip->rhythm & 0x20) + chan_max=6; + + switch(r) + { + case 0: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_mul (chip, chan*2, inst[0]); + } + } + break; + case 1: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_mul (chip, chan*2+1,inst[1]); + } + } + break; + case 2: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_ksl_tl (chip, chan, inst[2]); + } + } + break; + case 3: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_ksl_wave_fb (chip, chan, inst[3]); + } + } + break; + case 4: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_ar_dr (chip, chan*2, inst[4]); + } + } + break; + case 5: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_ar_dr (chip, chan*2+1,inst[5]); + } + } + break; + case 6: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_sl_rr (chip, chan*2, inst[6]); + } + } + break; + case 7: + for (chan=0; chaninstvol_r[chan]&0xf0)==0) + { + set_sl_rr (chip, chan*2+1,inst[7]); + } + } + break; + } +} + +/* write a value v to register r on chip chip */ +static void OPLLWriteReg(YM2413 *chip, int r, int v) +{ + OPLL_CH *CH; + OPLL_SLOT *SLOT; + UINT8 *inst; + int chan; + int slot; + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + + /*if (LOG_CYM_FILE && (cymfile) && (r!=8) ) + { + fputc( (unsigned char)r, cymfile ); + fputc( (unsigned char)v, cymfile ); + }*/ + + + switch(r&0xf0) + { + case 0x00: /* 00-0f:control */ + { + switch(r&0x0f) + { + case 0x00: /* AM/VIB/EGTYP/KSR/MULTI (modulator) */ + case 0x01: /* AM/VIB/EGTYP/KSR/MULTI (carrier) */ + case 0x02: /* Key Scale Level, Total Level (modulator) */ + case 0x03: /* Key Scale Level, carrier waveform, modulator waveform, Feedback */ + case 0x04: /* Attack, Decay (modulator) */ + case 0x05: /* Attack, Decay (carrier) */ + case 0x06: /* Sustain, Release (modulator) */ + case 0x07: /* Sustain, Release (carrier) */ + chip->inst_tab[0][r & 0x07] = v; + update_instrument_zero(chip,r&7); + break; + + case 0x0e: /* x, x, r,bd,sd,tom,tc,hh */ + { + if (chip->VRC7_Mode) + break; + if(v&0x20) + { + if ((chip->rhythm&0x20)==0) + /*rhythm off to on*/ + { + //logerror("YM2413: Rhythm mode enable\n"); + + /* Load instrument settings for channel seven(chan=6 since we're zero based). (Bass drum) */ + chan = 6; + inst = &chip->inst_tab[16][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + + /* Load instrument settings for channel eight. (High hat and snare drum) */ + chan = 7; + inst = &chip->inst_tab[17][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + + CH = &chip->P_CH[chan]; + SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH */ + SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + + /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */ + chan = 8; + inst = &chip->inst_tab[18][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + + CH = &chip->P_CH[chan]; + SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */ + SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + /* BD key on/off */ + if(v&0x10) + { + KEY_ON (&chip->P_CH[6].SLOT[SLOT1], 2); + KEY_ON (&chip->P_CH[6].SLOT[SLOT2], 2); + } + else + { + KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2); + KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) KEY_ON (&chip->P_CH[7].SLOT[SLOT1], 2); + else KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) KEY_ON (&chip->P_CH[7].SLOT[SLOT2], 2); + else KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) KEY_ON (&chip->P_CH[8].SLOT[SLOT1], 2); + else KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) KEY_ON (&chip->P_CH[8].SLOT[SLOT2], 2); + else KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + else + { + if ((chip->rhythm&0x20)!=0) + /*rhythm on to off*/ + { + //logerror("YM2413: Rhythm mode disable\n"); + /* Load instrument settings for channel seven(chan=6 since we're zero based).*/ + chan = 6; + inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + + /* Load instrument settings for channel eight.*/ + chan = 7; + inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + + /* Load instrument settings for channel nine.*/ + chan = 8; + inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + } + /* BD key off */ + KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2); + KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + chip->rhythm = v&0x3f; + } + break; + } + } + break; + + case 0x10: + case 0x20: + { + int block_fnum; + + chan = r&0x0f; + + if (chan >= 9) + chan -= 9; /* verified on real YM2413 */ + if (chip->VRC7_Mode && chan >= 6) + break; + + CH = &chip->P_CH[chan]; + + if(r&0x10) + { /* 10-18: FNUM 0-7 */ + block_fnum = (CH->block_fnum&0x0f00) | v; + } + else + { /* 20-28: suson, keyon, block, FNUM 8 */ + block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff); + + if(v&0x10) + { + KEY_ON (&CH->SLOT[SLOT1], 1); + KEY_ON (&CH->SLOT[SLOT2], 1); + } + else + { + KEY_OFF(&CH->SLOT[SLOT1],~1); + KEY_OFF(&CH->SLOT[SLOT2],~1); + } + + + //if (CH->sus!=(v&0x20)) + // logerror("chan=%i sus=%2x\n",chan,v&0x20); + + CH->sus = v & 0x20; + } + /* update */ + if(CH->block_fnum != block_fnum) + { + UINT8 block; + + CH->block_fnum = block_fnum; + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB */ + CH->kcode = (block_fnum&0x0f00)>>8; + + CH->ksl_base = ksl_tab[block_fnum>>5]; + + block_fnum = block_fnum * 2; + block = (block_fnum&0x1c00) >> 10; + CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + } + break; + + case 0x30: /* inst 4 MSBs, VOL 4 LSBs */ + { + UINT8 old_instvol; + + chan = r&0x0f; + + if (chan >= 9) + chan -= 9; /* verified on real YM2413 */ + if (chip->VRC7_Mode && chan >= 6) + break; + + old_instvol = chip->instvol_r[chan]; + chip->instvol_r[chan] = v; /* store for later use */ + + CH = &chip->P_CH[chan]; + SLOT = &CH->SLOT[SLOT2]; /* carrier */ + SLOT->TL = ((v&0x0f)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + + + /*check whether we are in rhythm mode and handle instrument/volume register accordingly*/ + if ((chan>=6) && (chip->rhythm&0x20)) + { + /* we're in rhythm mode*/ + + if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/ + { + SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */ + SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + } + else + { + if ( (old_instvol&0xf0) == (v&0xf0) ) + return; + + inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0]; + slot = chan*2; + + load_instrument(chip, chan, slot, inst); + + /*#if 0 + logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v); + logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]); + logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]); + #endif*/ + } + } + break; + + default: + break; + } +} + +/*static TIMER_CALLBACK( cymfile_callback ) +{ + if (cymfile) + { + fputc( (unsigned char)8, cymfile ); + } +}*/ + +/* lock/unlock for common table */ +//static int OPLL_LockTable(const device_config *device) +static int OPLL_LockTable(void) +{ + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + /* allocate total level table (128kb space) */ + if( !init_tables() ) + { + num_lock--; + return -1; + } + + /*if (LOG_CYM_FILE) + { + cymfile = fopen("2413_.cym","wb"); + if (cymfile) + timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer + else + logerror("Could not create file 2413_.cym\n"); + }*/ + + return 0; +} + +static void OPLL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + + OPLCloseTable(); + + /*if (cymfile) + fclose (cymfile); + cymfile = NULL;*/ +} + +static void OPLLResetChip(YM2413 *chip) +{ + int c,s; + int i; + + chip->eg_timer = 0; + chip->eg_cnt = 0; + + chip->noise_rng = 1; /* noise shift register */ + + + /* setup instruments table */ + for (i=0; i<19; i++) + { + for (c=0; c<8; c++) + { + chip->inst_tab[i][c] = table[i][c]; + } + } + + + /* reset with register write */ + OPLLWriteReg(chip,0x0f,0); /*test reg*/ + for(i = 0x3f ; i >= 0x10 ; i-- ) OPLLWriteReg(chip,i,0x00); + + /* reset operator parameters */ + for( c = 0 ; c < 9 ; c++ ) + { + OPLL_CH *CH = &chip->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = 0; + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +} + +/* Create one of virtual YM2413 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +//static YM2413 *OPLLCreate(const device_config *device, int clock, int rate) +static YM2413 *OPLLCreate(int clock, int rate) +{ + char *ptr; + YM2413 *chip; + int state_size; + + if (OPLL_LockTable() == -1) return NULL; + + /* calculate chip state size */ + state_size = sizeof(YM2413); + + /* allocate memory block */ + ptr = (char *)malloc(state_size); + + if (ptr==NULL) + return NULL; + + /* clear */ + memset(ptr,0,state_size); + + chip = (YM2413 *)ptr; + + chip->clock = clock; + chip->rate = rate; + + /* init global tables */ + OPLL_initalize(chip); + + /* reset chip */ + OPLLResetChip(chip); + return chip; +} + +/* Destroy one of virtual YM3812 */ +static void OPLLDestroy(YM2413 *chip) +{ + OPLL_UnLockTable(); + free(chip); +} + +/* Option handlers */ + +static void OPLLSetUpdateHandler(YM2413 *chip,OPLL_UPDATEHANDLER UpdateHandler,void * param) +{ + chip->UpdateHandler = UpdateHandler; + chip->UpdateParam = param; +} + +/* YM3812 I/O interface */ +static void OPLLWrite(YM2413 *chip,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + chip->address = v & 0xff; + } + else + { /* data port */ + if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0); + OPLLWriteReg(chip,chip->address,v); + } +} + +static unsigned char OPLLRead(YM2413 *chip,int a) +{ + if( !(a&1) ) + { + /* status port */ + return chip->status; + } + return 0xff; +} + + + + + +//void * ym2413_init(const device_config *device, int clock, int rate) +void * ym2413_init(int clock, int rate) +{ + /* emulator create */ + return OPLLCreate(clock, rate); +} + +void ym2413_shutdown(void *chip) +{ + YM2413 *OPLL = (YM2413 *)chip; + + /* emulator shutdown */ + OPLLDestroy(OPLL); +} + +void ym2413_reset_chip(void *chip) +{ + YM2413 *OPLL = (YM2413 *)chip; + OPLLResetChip(OPLL); +} + +void ym2413_write(void *chip, int a, int v) +{ + YM2413 *OPLL = (YM2413 *)chip; + OPLLWrite(OPLL, a, v); +} + +unsigned char ym2413_read(void *chip, int a) +{ + YM2413 *OPLL = (YM2413 *)chip; + return OPLLRead(OPLL, a) & 0x03 ; +} + +void ym2413_set_update_handler(void *chip,OPLL_UPDATEHANDLER UpdateHandler,void *param) +{ + YM2413 *OPLL = (YM2413 *)chip; + OPLLSetUpdateHandler(OPLL, UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM2413's +** +** 'which' is the virtual YM2413 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void ym2413_update_one(void *_chip, SAMP **buffers, int length) +{ + YM2413 *chip = (YM2413 *)_chip; + UINT8 rhythm = chip->rhythm&0x20; + SAMP *bufMO = buffers[0]; + SAMP *bufRO = buffers[1]; + + int i; + + for( i=0; i < length ; i++ ) + { + int mo,ro; + + chip->output[0] = 0; + chip->output[1] = 0; + + advance_lfo(chip); + + /* FM part */ + chan_calc(chip, &chip->P_CH[0]); +//SAVE_SEPARATE_CHANNEL(0); + chan_calc(chip, &chip->P_CH[1]); + chan_calc(chip, &chip->P_CH[2]); + chan_calc(chip, &chip->P_CH[3]); + chan_calc(chip, &chip->P_CH[4]); + chan_calc(chip, &chip->P_CH[5]); + + if (! chip->VRC7_Mode) + { + if(!rhythm) + { + chan_calc(chip, &chip->P_CH[6]); + chan_calc(chip, &chip->P_CH[7]); + chan_calc(chip, &chip->P_CH[8]); + } + else /* Rhythm part */ + { + rhythm_calc(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); + } + } + + mo = chip->output[0]; + ro = chip->output[1]; + + mo >>= FINAL_SH; + ro >>= FINAL_SH; + + /* limit check */ + //mo = limit( mo , MAXOUT, MINOUT ); + //ro = limit( ro , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + //if (which==0) + //{ + SAVE_ALL_CHANNELS + //} + #endif + + /* store to sound buffer */ + bufMO[i] = mo + ro; + bufRO[i] = mo + ro; + + advance(chip); + } + +} + +void ym2413_set_mutemask(void* chip, UINT32 MuteMask) +{ + YM2413* OPLL = (YM2413*)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 9; CurChn ++) + OPLL->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + for (CurChn = 0; CurChn < 5; CurChn ++) + OPLL->MuteSpc[CurChn] = (MuteMask >> (9 + CurChn)) & 0x01; + + return; +} + +void ym2413_set_chip_mode(void* chip, UINT8 Mode) +{ + // Enable/Disable VRC7 Mode (with only 6 instead of 9 channels and no rhythm part) + YM2413* OPLL = (YM2413*)chip; + + OPLL->VRC7_Mode = Mode; + + return; +} + +void ym2413_override_patches(void* chip, const UINT8* PatchDump) +{ + YM2413* OPLL = (YM2413*)chip; + UINT8 CurIns; + UINT8 CurReg; + + for (CurIns = 0; CurIns < 19; CurIns ++) + { + for (CurReg = 0; CurReg < 8; CurReg ++) + { + OPLL->inst_tab[CurIns][CurReg] = PatchDump[CurIns * 8 + CurReg]; + } + } + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/ym2413.h b/Frameworks/GME/vgmplay/chips/ym2413.h new file mode 100644 index 000000000..0778dc222 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ym2413.h @@ -0,0 +1,43 @@ +#pragma once + +/* select output bits size of output : 8 or 16 */ +#define SAMPLE_BITS 16 + +/* compiler dependence */ +//#ifndef __OSDCOMM_H__ +//#define __OSDCOMM_H__ +/*typedef unsigned char UINT8; // unsigned 8bit +typedef unsigned short UINT16; // unsigned 16bit +typedef unsigned int UINT32; // unsigned 32bit +typedef signed char INT8; // signed 8bit +typedef signed short INT16; // signed 16bit +typedef signed int INT32; // signed 32bit */ +//#endif + +//typedef INT32 stream_sample_t; +typedef stream_sample_t SAMP; +/* +#if (SAMPLE_BITS==16) +typedef INT16 SAMP; +#endif +#if (SAMPLE_BITS==8) +typedef INT8 SAMP; +#endif +*/ + + + +//void *ym2413_init(const device_config *device, int clock, int rate); +void *ym2413_init(int clock, int rate); +void ym2413_shutdown(void *chip); +void ym2413_reset_chip(void *chip); +void ym2413_write(void *chip, int a, int v); +unsigned char ym2413_read(void *chip, int a); +void ym2413_update_one(void *chip, SAMP **buffers, int length); + +typedef void (*OPLL_UPDATEHANDLER)(void *param,int min_interval_us); + +void ym2413_set_update_handler(void *chip, OPLL_UPDATEHANDLER UpdateHandler, void *param); +void ym2413_set_mutemask(void* chip, UINT32 MuteMask); +void ym2413_set_chip_mode(void* chip, UINT8 Mode); +void ym2413_override_patches(void* chip, const UINT8* PatchDump); diff --git a/Frameworks/GME/vgmplay/chips/ym2612.c b/Frameworks/GME/vgmplay/chips/ym2612.c new file mode 100644 index 000000000..b13defa7c --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ym2612.c @@ -0,0 +1,2551 @@ +/***********************************************************/ +/* */ +/* YM2612.C : YM2612 emulator */ +/* */ +/* Almost constantes are taken from the MAME core */ +/* */ +/* This source is a part of Gens project */ +/* Written by Stéphane Dallongeville (gens@consolemul.com) */ +/* Copyright (c) 2002 by Stéphane Dallongeville */ +/* */ +/***********************************************************/ + +/***********************************************************/ +/* */ +/* Modified by Maxim, Blargg */ +/* - removed non-sound-related functionality */ +/* - added high-pass PCM filter */ +/* - added per-channel muting control */ +/* - made it use a context struct to allow multiple */ +/* instances */ +/* */ +/***********************************************************/ + +#include // for malloc +#include +#include +#include // for memset() +#include "mamedef.h" // for correct INLINE macro +#include "ym2612.h" + + +/******************************************** + * Partie définition * + ********************************************/ + +#define YM_DEBUG_LEVEL 0 + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#define ATTACK 0 +#define DECAY 1 +#define SUBSTAIN 2 +#define RELEASE 3 + +// SIN_LBITS <= 16 +// LFO_HBITS <= 16 +// (SIN_LBITS + SIN_HBITS) <= 26 +// (ENV_LBITS + ENV_HBITS) <= 28 +// (LFO_LBITS + LFO_HBITS) <= 28 + +#define SIN_HBITS 12 // Sinus phase counter int part +#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) + +#if(SIN_LBITS > 16) +#define SIN_LBITS 16 // Can't be greater than 16 bits +#endif + +#define ENV_HBITS 12 // Env phase counter int part +#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) + +#define LFO_HBITS 10 // LFO phase counter int part +#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) + +#define SIN_LENGHT (1 << SIN_HBITS) +#define ENV_LENGHT (1 << ENV_HBITS) +#define LFO_LENGHT (1 << LFO_HBITS) + +#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO + +#define SIN_MASK (SIN_LENGHT - 1) +#define ENV_MASK (ENV_LENGHT - 1) +#define LFO_MASK (LFO_LENGHT - 1) + +#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB + +#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) +#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) +#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) + +#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 +#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) + +//Just for tests stuff... +// +//#define COEF_MOD 0.5 +//#define MAX_OUT ((int) (((1 << MAX_OUT_BITS) - 1) * COEF_MOD)) + +#define OUT_BITS (OUTPUT_BITS - 2) +#define OUT_SHIFT (MAX_OUT_BITS - OUT_BITS) +#define LIMIT_CH_OUT ((int) (((1 << OUT_BITS) * 1.5) - 1)) + +#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) +#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) + +#define AR_RATE 399128 +#define DR_RATE 5514396 + +//#define AR_RATE 426136 // good rate ? +//#define DR_RATE (AR_RATE * 12) + +#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) +#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) + +#define S0 0 // Stupid typo of the YM2612 +#define S1 2 +#define S2 1 +#define S3 3 + + +/******************************************** + * Partie variables * + ********************************************/ + + +//struct ym2612__ YM2612; + +int *SIN_TAB[SIN_LENGHT]; // SINUS TABLE (pointer on TL TABLE) +int TL_TAB[TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) +unsigned int ENV_TAB[2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) + +//unsigned int ATTACK_TO_DECAY[ENV_LENGHT]; // Conversion from attack to decay phase +unsigned int DECAY_TO_ATTACK[ENV_LENGHT]; // Conversion from decay to attack phase + +unsigned int FINC_TAB[2048]; // Frequency step table + +unsigned int AR_TAB[128]; // Attack rate table +unsigned int DR_TAB[96]; // Decay rate table +unsigned int DT_TAB[8][32]; // Detune table +unsigned int SL_TAB[16]; // Substain level table +unsigned int NULL_RATE[32]; // Table for NULL rate + +int LFO_ENV_TAB[LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) +int LFO_FREQ_TAB[LFO_LENGHT]; // LFO FMS TABLE + +// int INTER_TAB[MAX_UPDATE_LENGHT]; // Interpolation table + +int LFO_INC_TAB[8]; // LFO step table + +void (* const UPDATE_CHAN[8 * 8])(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) = // Update Channel functions pointer table +{ + Update_Chan_Algo0, + Update_Chan_Algo1, + Update_Chan_Algo2, + Update_Chan_Algo3, + Update_Chan_Algo4, + Update_Chan_Algo5, + Update_Chan_Algo6, + Update_Chan_Algo7, + + Update_Chan_Algo0_LFO, + Update_Chan_Algo1_LFO, + Update_Chan_Algo2_LFO, + Update_Chan_Algo3_LFO, + Update_Chan_Algo4_LFO, + Update_Chan_Algo5_LFO, + Update_Chan_Algo6_LFO, + Update_Chan_Algo7_LFO, + + Update_Chan_Algo0_Int, + Update_Chan_Algo1_Int, + Update_Chan_Algo2_Int, + Update_Chan_Algo3_Int, + Update_Chan_Algo4_Int, + Update_Chan_Algo5_Int, + Update_Chan_Algo6_Int, + Update_Chan_Algo7_Int, + + Update_Chan_Algo0_LFO_Int, + Update_Chan_Algo1_LFO_Int, + Update_Chan_Algo2_LFO_Int, + Update_Chan_Algo3_LFO_Int, + Update_Chan_Algo4_LFO_Int, + Update_Chan_Algo5_LFO_Int, + Update_Chan_Algo6_LFO_Int, + Update_Chan_Algo7_LFO_Int +}; + +void (* const ENV_NEXT_EVENT[8])(slot_ *SL) = // Next Enveloppe phase functions pointer table +{ + Env_Attack_Next, + Env_Decay_Next, + Env_Substain_Next, + Env_Release_Next, + Env_NULL_Next, + Env_NULL_Next, + Env_NULL_Next, + Env_NULL_Next +}; + +const unsigned int DT_DEF_TAB[4 * 32] = +{ +// FD = 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + +// FD = 1 + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + +// FD = 2 + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, + +// FD = 3 + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 +}; + +const unsigned int FKEY_TAB[16] = +{ + 0, 0, 0, 0, + 0, 0, 0, 1, + 2, 3, 3, 3, + 3, 3, 3, 3 +}; + +const unsigned int LFO_AMS_TAB[4] = +{ + 31, 4, 1, 0 +}; + +const unsigned int LFO_FMS_TAB[8] = +{ + LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, + LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, + LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, + LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 +}; + +int int_cnt; // Interpolation calculation + + +#if YM_DEBUG_LEVEL > 0 // Debug +FILE *debug_file = NULL; +#endif + + +/* Gens */ + +//extern unsigned int Sound_Extrapol[312][2]; +//extern int Seg_L[882], Seg_R[882]; +//extern int VDP_Current_Line; +//extern int GYM_Dumping; +//extern int YM2612_Enable; +//extern int DAC_Enable=1; + +//int Update_GYM_Dump(char v0, char v1, char v2); + +int YM2612_Enable; +int YM2612_Improv; +//int DAC_Enable = 1; +int *YM_Buf[2]; +int YM_Len = 0; +int YM2612_Enable_SSGEG = 1; // enable SSG-EG envelope (causes inacurate sound sometimes - rodrigo) +int DAC_Highpass_Enable = 1; // sometimes it creates a terrible noise + +/* end */ + + +/*********************************************** + * fonctions calcul param * + ***********************************************/ + + +INLINE void CALC_FINC_SL(slot_ *SL, int finc, int kc) +{ + int ksr; + + SL->Finc = (finc + SL->DT[kc]) * SL->MUL; + + ksr = kc >> SL->KSR_S; // keycode atténuation + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "FINC = %d SL->Finc = %d\n", finc, SL->Finc); +#endif + + if (SL->KSR != ksr) // si le KSR a changé alors + { // les différents taux pour l'enveloppe sont mis à jour + SL->KSR = ksr; + + SL->EincA = SL->AR[ksr]; + SL->EincD = SL->DR[ksr]; + SL->EincS = SL->SR[ksr]; + SL->EincR = SL->RR[ksr]; + + if(SL->Ecurp == ATTACK) SL->Einc = SL->EincA; + else if(SL->Ecurp == DECAY) SL->Einc = SL->EincD; + else if(SL->Ecnt < ENV_END) + { + if(SL->Ecurp == SUBSTAIN) SL->Einc = SL->EincS; + else if(SL->Ecurp == RELEASE) SL->Einc = SL->EincR; + } + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "KSR = %.4X EincA = %.8X EincD = %.8X EincS = %.8X EincR = %.8X\n", ksr, SL->EincA, SL->EincD, SL->EincS, SL->EincR); +#endif + } +} + + +INLINE void CALC_FINC_CH(channel_ *CH) +{ + int finc, kc; + + finc = FINC_TAB[CH->FNUM[0]] >> (7 - CH->FOCT[0]); + kc = CH->KC[0]; + + CALC_FINC_SL(&CH->SLOT[0], finc, kc); + CALC_FINC_SL(&CH->SLOT[1], finc, kc); + CALC_FINC_SL(&CH->SLOT[2], finc, kc); + CALC_FINC_SL(&CH->SLOT[3], finc, kc); +} + + + +/*********************************************** + * fonctions setting * + ***********************************************/ + + +INLINE void KEY_ON(channel_ *CH, int nsl) +{ + slot_ *SL = &(CH->SLOT[nsl]); // on recupère le bon pointeur de slot + + if(SL->Ecurp == RELEASE) // la touche est-elle relâchée ? + { + SL->Fcnt = 0; + + // Fix Ecco 2 splash sound + + SL->Ecnt = (DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; + SL->ChgEnM = 0xFFFFFFFF; + +// SL->Ecnt = DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; +// SL->Ecnt = 0; + + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } +} + + +INLINE void KEY_OFF(channel_ *CH, int nsl) +{ + slot_ *SL = &(CH->SLOT[nsl]); // on recupère le bon pointeur de slot + + if(SL->Ecurp != RELEASE) // la touche est-elle appuyée ? + { + if(SL->Ecnt < ENV_DECAY) // attack phase ? + { + SL->Ecnt = (ENV_TAB[SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; + } + + SL->Einc = SL->EincR; + SL->Ecmp = ENV_END; + SL->Ecurp = RELEASE; + } +} + + +INLINE void CSM_Key_Control(ym2612_ *YM2612) +{ + KEY_ON(&YM2612->CHANNEL[2], 0); + KEY_ON(&YM2612->CHANNEL[2], 1); + KEY_ON(&YM2612->CHANNEL[2], 2); + KEY_ON(&YM2612->CHANNEL[2], 3); +} + + +int SLOT_SET(ym2612_ *YM2612, int Adr, unsigned char data) +{ + channel_ *CH; + slot_ *SL; + int nch, nsl; + + if((nch = Adr & 3) == 3) return 1; + nsl = (Adr >> 2) & 3; + + if(Adr & 0x100) nch += 3; + + CH = &(YM2612->CHANNEL[nch]); + SL = &(CH->SLOT[nsl]); + + switch(Adr & 0xF0) + { + case 0x30: + if(SL->MUL = (data & 0x0F)) SL->MUL <<= 1; + else SL->MUL = 1; + + SL->DT = DT_TAB[(data >> 4) & 7]; + + CH->SLOT[0].Finc = -1; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] DTMUL = %.2X\n", nch, nsl, data & 0x7F); +#endif + break; + + case 0x40: + SL->TL = data & 0x7F; + + // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... + YM2612_Special_Update(YM2612); + +#if((ENV_HBITS - 7) < 0) + SL->TLL = SL->TL >> (7 - ENV_HBITS); +#else + SL->TLL = SL->TL << (ENV_HBITS - 7); +#endif + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] TL = %.2X\n", nch, nsl, SL->TL); +#endif + break; + + case 0x50: + SL->KSR_S = 3 - (data >> 6); + + CH->SLOT[0].Finc = -1; + + if(data &= 0x1F) SL->AR = &AR_TAB[data << 1]; + else SL->AR = &NULL_RATE[0]; + + SL->EincA = SL->AR[SL->KSR]; + if(SL->Ecurp == ATTACK) SL->Einc = SL->EincA; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] AR = %.2X EincA = %.6X\n", nch, nsl, data, SL->EincA); +#endif + break; + + case 0x60: + if(SL->AMSon = (data & 0x80)) SL->AMS = CH->AMS; + else SL->AMS = 31; + + if(data &= 0x1F) SL->DR = &DR_TAB[data << 1]; + else SL->DR = &NULL_RATE[0]; + + SL->EincD = SL->DR[SL->KSR]; + if(SL->Ecurp == DECAY) SL->Einc = SL->EincD; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] AMS = %d DR = %.2X EincD = %.6X\n", nch, nsl, SL->AMSon, data, SL->EincD); +#endif + break; + + case 0x70: + if(data &= 0x1F) SL->SR = &DR_TAB[data << 1]; + else SL->SR = &NULL_RATE[0]; + + SL->EincS = SL->SR[SL->KSR]; + if((SL->Ecurp == SUBSTAIN) && (SL->Ecnt < ENV_END)) SL->Einc = SL->EincS; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SR = %.2X EincS = %.6X\n", nch, nsl, data, SL->EincS); +#endif + break; + + case 0x80: + SL->SLL = SL_TAB[data >> 4]; + + SL->RR = &DR_TAB[((data & 0xF) << 2) + 2]; + + SL->EincR = SL->RR[SL->KSR]; + if((SL->Ecurp == RELEASE) && (SL->Ecnt < ENV_END)) SL->Einc = SL->EincR; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SL = %.8X\n", nch, nsl, SL->SLL); + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] RR = %.2X EincR = %.2X\n", nch, nsl, ((data & 0xF) << 1) | 2, SL->EincR); +#endif + break; + + case 0x90: + // SSG-EG envelope shapes : + // + // E At Al H + // + // 1 0 0 0 \\\\ + // + // 1 0 0 1 \___ + // + // 1 0 1 0 \/\/ + // ___ + // 1 0 1 1 \ + // + // 1 1 0 0 //// + // ___ + // 1 1 0 1 / + // + // 1 1 1 0 /\/\ + // + // 1 1 1 1 /___ + // + // E = SSG-EG enable + // At = Start negate + // Al = Altern + // H = Hold + if(YM2612_Enable_SSGEG) + { + if(data & 0x08) SL->SEG = data & 0x0F; + else SL->SEG = 0; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SSG-EG = %.2X\n", nch, nsl, data); +#endif + } + break; + } + + return 0; +} + + +int CHANNEL_SET(ym2612_ *YM2612, int Adr, unsigned char data) +{ + channel_ *CH; + int num; + + if((num = Adr & 3) == 3) return 1; + + switch(Adr & 0xFC) + { + case 0xA0: + if(Adr & 0x100) num += 3; + CH = &(YM2612->CHANNEL[num]); + + YM2612_Special_Update(YM2612); + + CH->FNUM[0] = (CH->FNUM[0] & 0x700) + data; + CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; + + CH->SLOT[0].Finc = -1; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d] part1 FNUM = %d KC = %d\n", num, CH->FNUM[0], CH->KC[0]); +#endif + break; + + case 0xA4: + if(Adr & 0x100) num += 3; + CH = &(YM2612->CHANNEL[num]); + + YM2612_Special_Update(YM2612); + + CH->FNUM[0] = (CH->FNUM[0] & 0x0FF) + ((int) (data & 0x07) << 8); + CH->FOCT[0] = (data & 0x38) >> 3; + CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; + + CH->SLOT[0].Finc = -1; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d] part2 FNUM = %d FOCT = %d KC = %d\n", num, CH->FNUM[0], CH->FOCT[0], CH->KC[0]); +#endif + break; + + case 0xA8: + if(Adr < 0x100) + { + num++; + + YM2612_Special_Update(YM2612); + + YM2612->CHANNEL[2].FNUM[num] = (YM2612->CHANNEL[2].FNUM[num] & 0x700) + data; + YM2612->CHANNEL[2].KC[num] = (YM2612->CHANNEL[2].FOCT[num] << 2) | FKEY_TAB[YM2612->CHANNEL[2].FNUM[num] >> 7]; + + YM2612->CHANNEL[2].SLOT[0].Finc = -1; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[2] part1 FNUM[%d] = %d KC[%d] = %d\n", num, YM2612->CHANNEL[2].FNUM[num], num, YM2612->CHANNEL[2].KC[num]); +#endif + } + break; + + case 0xAC: + if(Adr < 0x100) + { + num++; + + YM2612_Special_Update(YM2612); + + YM2612->CHANNEL[2].FNUM[num] = (YM2612->CHANNEL[2].FNUM[num] & 0x0FF) + ((int) (data & 0x07) << 8); + YM2612->CHANNEL[2].FOCT[num] = (data & 0x38) >> 3; + YM2612->CHANNEL[2].KC[num] = (YM2612->CHANNEL[2].FOCT[num] << 2) | FKEY_TAB[YM2612->CHANNEL[2].FNUM[num] >> 7]; + + YM2612->CHANNEL[2].SLOT[0].Finc = -1; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[2] part2 FNUM[%d] = %d FOCT[%d] = %d KC[%d] = %d\n", num, YM2612->CHANNEL[2].FNUM[num], num, YM2612->CHANNEL[2].FOCT[num], num, YM2612->CHANNEL[2].KC[num]); +#endif + } + break; + + case 0xB0: + if(Adr & 0x100) num += 3; + CH = &(YM2612->CHANNEL[num]); + + if(CH->ALGO != (data & 7)) + { + // Fix VectorMan 2 heli sound (level 1) + YM2612_Special_Update(YM2612); + + CH->ALGO = data & 7; + + CH->SLOT[0].ChgEnM = 0; + CH->SLOT[1].ChgEnM = 0; + CH->SLOT[2].ChgEnM = 0; + CH->SLOT[3].ChgEnM = 0; + } + + CH->FB = 9 - ((data >> 3) & 7); // Real thing ? + +// if(CH->FB = ((data >> 3) & 7)) CH->FB = 9 - CH->FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... +// else CH->FB = 31; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "CHANNEL[%d] ALGO = %d FB = %d\n", num, CH->ALGO, CH->FB); +#endif + break; + + case 0xB4: + if(Adr & 0x100) num += 3; + CH = &(YM2612->CHANNEL[num]); + + YM2612_Special_Update(YM2612); + + if(data & 0x80) CH->LEFT = 0xFFFFFFFF; + else CH->LEFT = 0; + + if(data & 0x40) CH->RIGHT = 0xFFFFFFFF; + else CH->RIGHT = 0; + + CH->AMS = LFO_AMS_TAB[(data >> 4) & 3]; + CH->FMS = LFO_FMS_TAB[data & 7]; + + if(CH->SLOT[0].AMSon) CH->SLOT[0].AMS = CH->AMS; + else CH->SLOT[0].AMS = 31; + if(CH->SLOT[1].AMSon) CH->SLOT[1].AMS = CH->AMS; + else CH->SLOT[1].AMS = 31; + if(CH->SLOT[2].AMSon) CH->SLOT[2].AMS = CH->AMS; + else CH->SLOT[2].AMS = 31; + if(CH->SLOT[3].AMSon) CH->SLOT[3].AMS = CH->AMS; + else CH->SLOT[3].AMS = 31; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "CHANNEL[%d] AMS = %d FMS = %d\n", num, CH->AMS, CH->FMS); +#endif + break; + } + + return 0; +} + + +int YM_SET(ym2612_ *YM2612, int Adr, unsigned char data) +{ + channel_ *CH; + int nch; + + switch(Adr) + { + case 0x22: + if(data & 8) + { + // Cool Spot music 1, LFO modified severals time which + // distord the sound, have to check that on a real genesis... + + YM2612->LFOinc = LFO_INC_TAB[data & 7]; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "\nLFO Enable, LFOinc = %.8X %d\n", YM2612->LFOinc, data & 7); +#endif + } + else + { + YM2612->LFOinc = YM2612->LFOcnt = 0; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "\nLFO Disable\n"); +#endif + } + break; + + case 0x24: + YM2612->TimerA = (YM2612->TimerA & 0x003) | (((int) data) << 2); + + if(YM2612->TimerAL != (1024 - YM2612->TimerA) << 12) + { + YM2612->TimerAcnt = YM2612->TimerAL = (1024 - YM2612->TimerA) << 12; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "Timer A Set = %.8X\n", YM2612->TimerAcnt); +#endif + } + break; + + case 0x25: + YM2612->TimerA = (YM2612->TimerA & 0x3fc) | (data & 3); + + if(YM2612->TimerAL != (1024 - YM2612->TimerA) << 12) + { + YM2612->TimerAcnt = YM2612->TimerAL = (1024 - YM2612->TimerA) << 12; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "Timer A Set = %.8X\n", YM2612->TimerAcnt); +#endif + } + break; + + case 0x26: + YM2612->TimerB = data; + + if(YM2612->TimerBL != (256 - YM2612->TimerB) << (4 + 12)) + { + YM2612->TimerBcnt = YM2612->TimerBL = (256 - YM2612->TimerB) << (4 + 12); + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "Timer B Set = %.8X\n", YM2612->TimerBcnt); +#endif + } + break; + + case 0x27: + // Paramètre divers + // b7 = CSM MODE + // b6 = 3 slot mode + // b5 = reset b + // b4 = reset a + // b3 = timer enable b + // b2 = timer enable a + // b1 = load b + // b0 = load a + + if((data ^ YM2612->Mode) & 0x40) + { + // We changed the channel 2 mode, so recalculate phase step + // This fix the punch sound in Street of Rage 2 + + YM2612_Special_Update(YM2612); + + YM2612->CHANNEL[2].SLOT[0].Finc = -1; // recalculate phase step + } + +// if((data & 2) && (YM2612->Status & 2)) YM2612->TimerBcnt = YM2612->TimerBL; +// if((data & 1) && (YM2612->Status & 1)) YM2612->TimerAcnt = YM2612->TimerAL; + +// YM2612->Status &= (~data >> 4); // Reset du Status au cas ou c'est demand? + YM2612->Status &= (~data >> 4) & (data >> 2); // Reset Status + + YM2612->Mode = data; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "Mode reg = %.2X\n", data); +#endif + break; + + case 0x28: + if((nch = data & 3) == 3) return 1; + + if(data & 4) nch += 3; + CH = &(YM2612->CHANNEL[nch]); + + YM2612_Special_Update(YM2612); + + if(data & 0x10) KEY_ON(CH, S0); // On appuie sur la touche pour le slot 1 + else KEY_OFF(CH, S0); // On relâche la touche pour le slot 1 + if(data & 0x20) KEY_ON(CH, S1); // On appuie sur la touche pour le slot 3 + else KEY_OFF(CH, S1); // On relâche la touche pour le slot 3 + if(data & 0x40) KEY_ON(CH, S2); // On appuie sur la touche pour le slot 2 + else KEY_OFF(CH, S2); // On relâche la touche pour le slot 2 + if(data & 0x80) KEY_ON(CH, S3); // On appuie sur la touche pour le slot 4 + else KEY_OFF(CH, S3); // On relâche la touche pour le slot 4 + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "CHANNEL[%d] KEY %.1X\n", nch, ((data & 0xf0) >> 4)); +#endif + break; + + case 0x2A: + YM2612->DACdata = ((int) data - 0x80) << DAC_SHIFT; // donnée du DAC + break; + + case 0x2B: + if(YM2612->DAC ^ (data & 0x80)) YM2612_Special_Update(YM2612); + + YM2612->DAC = data & 0x80; // activation/désactivation du DAC + break; + } + + return 0; +} + + + +/*********************************************** + * fonctions de génération * + ***********************************************/ + + +void Env_NULL_Next(slot_ *SL) +{ +} + + +void Env_Attack_Next(slot_ *SL) +{ + // Verified with Gynoug even in HQ (explode SFX) + SL->Ecnt = ENV_DECAY; + + SL->Einc = SL->EincD; + SL->Ecmp = SL->SLL; + SL->Ecurp = DECAY; +} + + +void Env_Decay_Next(slot_ *SL) +{ + // Verified with Gynoug even in HQ (explode SFX) + SL->Ecnt = SL->SLL; + + SL->Einc = SL->EincS; + SL->Ecmp = ENV_END; + SL->Ecurp = SUBSTAIN; +} + + +void Env_Substain_Next(slot_ *SL) +{ + if(YM2612_Enable_SSGEG) + { + if(SL->SEG & 8) // SSG envelope type + { + if(SL->SEG & 1) + { + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; + } + else + { + // re KEY ON + + // SL->Fcnt = 0; + // SL->ChgEnM = 0xFFFFFFFF; + + SL->Ecnt = 0; + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } + + SL->SEG ^= (SL->SEG & 2) << 1; + } + else + { + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; + } + } + else + { + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; + } +} + + +void Env_Release_Next(slot_ *SL) +{ + SL->Ecnt = ENV_END; + SL->Einc = 0; + SL->Ecmp = ENV_END + 1; +} + + +#define GET_CURRENT_PHASE \ +YM2612->in0 = CH->SLOT[S0].Fcnt; \ +YM2612->in1 = CH->SLOT[S1].Fcnt; \ +YM2612->in2 = CH->SLOT[S2].Fcnt; \ +YM2612->in3 = CH->SLOT[S3].Fcnt; + + +#define UPDATE_PHASE \ +CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc; \ +CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc; \ +CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc; \ +CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc; + + +#define UPDATE_PHASE_LFO \ +if(freq_LFO = (CH->FMS * YM2612->LFO_FREQ_UP[i]) >> (LFO_HBITS - 1)) \ +{ \ + CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc + ((CH->SLOT[S0].Finc * freq_LFO) >> LFO_FMS_LBITS); \ + CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc + ((CH->SLOT[S1].Finc * freq_LFO) >> LFO_FMS_LBITS); \ + CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc + ((CH->SLOT[S2].Finc * freq_LFO) >> LFO_FMS_LBITS); \ + CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc + ((CH->SLOT[S3].Finc * freq_LFO) >> LFO_FMS_LBITS); \ +} \ +else \ +{ \ + CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc; \ + CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc; \ + CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc; \ + CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc; \ +} + + +#define GET_CURRENT_ENV \ +if(CH->SLOT[S0].SEG & 4) \ +{ \ + if((YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL) > ENV_MASK) YM2612->en0 = 0; \ + else YM2612->en0 ^= ENV_MASK; \ +} \ +else YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL; \ +if(CH->SLOT[S1].SEG & 4) \ +{ \ + if((YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL) > ENV_MASK) YM2612->en1 = 0; \ + else YM2612->en1 ^= ENV_MASK; \ +} \ +else YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL; \ +if(CH->SLOT[S2].SEG & 4) \ +{ \ + if((YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL) > ENV_MASK) YM2612->en2 = 0; \ + else YM2612->en2 ^= ENV_MASK; \ +} \ +else YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL; \ +if(CH->SLOT[S3].SEG & 4) \ +{ \ + if((YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL) > ENV_MASK) YM2612->en3 = 0; \ + else YM2612->en3 ^= ENV_MASK; \ +} \ +else YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL; + + +#define GET_CURRENT_ENV_LFO \ +env_LFO = YM2612->LFO_ENV_UP[i]; \ + \ +if(CH->SLOT[S0].SEG & 4) \ +{ \ + if((YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL) > ENV_MASK) YM2612->en0 = 0; \ + else YM2612->en0 = (YM2612->en0 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S0].AMS); \ +} \ +else YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL + (env_LFO >> CH->SLOT[S0].AMS); \ +if(CH->SLOT[S1].SEG & 4) \ +{ \ + if((YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL) > ENV_MASK) YM2612->en1 = 0; \ + else YM2612->en1 = (YM2612->en1 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S1].AMS); \ +} \ +else YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL + (env_LFO >> CH->SLOT[S1].AMS); \ +if(CH->SLOT[S2].SEG & 4) \ +{ \ + if((YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL) > ENV_MASK) YM2612->en2 = 0; \ + else YM2612->en2 = (YM2612->en2 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S2].AMS); \ +} \ +else YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL + (env_LFO >> CH->SLOT[S2].AMS); \ +if(CH->SLOT[S3].SEG & 4) \ +{ \ + if((YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL) > ENV_MASK) YM2612->en3 = 0; \ + else YM2612->en3 = (YM2612->en3 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S3].AMS); \ +} \ +else YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL + (env_LFO >> CH->SLOT[S3].AMS); + + +#define UPDATE_ENV \ +if((CH->SLOT[S0].Ecnt += CH->SLOT[S0].Einc) >= CH->SLOT[S0].Ecmp) \ + ENV_NEXT_EVENT[CH->SLOT[S0].Ecurp](&(CH->SLOT[S0])); \ +if((CH->SLOT[S1].Ecnt += CH->SLOT[S1].Einc) >= CH->SLOT[S1].Ecmp) \ + ENV_NEXT_EVENT[CH->SLOT[S1].Ecurp](&(CH->SLOT[S1])); \ +if((CH->SLOT[S2].Ecnt += CH->SLOT[S2].Einc) >= CH->SLOT[S2].Ecmp) \ + ENV_NEXT_EVENT[CH->SLOT[S2].Ecurp](&(CH->SLOT[S2])); \ +if((CH->SLOT[S3].Ecnt += CH->SLOT[S3].Einc) >= CH->SLOT[S3].Ecmp) \ + ENV_NEXT_EVENT[CH->SLOT[S3].Ecurp](&(CH->SLOT[S3])); + + +#define DO_LIMIT \ +if(CH->OUTd > LIMIT_CH_OUT) CH->OUTd = LIMIT_CH_OUT; \ +else if(CH->OUTd < -LIMIT_CH_OUT) CH->OUTd = -LIMIT_CH_OUT; + + +#define DO_FEEDBACK0 \ +YM2612->in0 += CH->S0_OUT[0] >> CH->FB; \ +CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; + + +#define DO_FEEDBACK \ +YM2612->in0 += (CH->S0_OUT[0] + CH->S0_OUT[1]) >> CH->FB; \ +CH->S0_OUT[1] = CH->S0_OUT[0]; \ +CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; + + +#define DO_FEEDBACK2 \ +YM2612->in0 += (CH->S0_OUT[0] + (CH->S0_OUT[0] >> 2) + CH->S0_OUT[1]) >> CH->FB; \ +CH->S0_OUT[1] = CH->S0_OUT[0] >> 2; \ +CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; + + +#define DO_FEEDBACK3 \ +YM2612->in0 += (CH->S0_OUT[0] + CH->S0_OUT[1] + CH->S0_OUT[2] + CH->S0_OUT[3]) >> CH->FB; \ +CH->S0_OUT[3] = CH->S0_OUT[2] >> 1; \ +CH->S0_OUT[2] = CH->S0_OUT[1] >> 1; \ +CH->S0_OUT[1] = CH->S0_OUT[0] >> 1; \ +CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; + + +#define DO_ALGO_0 \ +DO_FEEDBACK \ +YM2612->in1 += CH->S0_OUT[1]; \ +YM2612->in2 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ +YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ +CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; + +#define DO_ALGO_1 \ +DO_FEEDBACK \ +YM2612->in2 += CH->S0_OUT[1] + SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ +YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ +CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; + +#define DO_ALGO_2 \ +DO_FEEDBACK \ +YM2612->in2 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ +YM2612->in3 += CH->S0_OUT[1] + SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ +CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; + +#define DO_ALGO_3 \ +DO_FEEDBACK \ +YM2612->in1 += CH->S0_OUT[1]; \ +YM2612->in3 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ +CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; + +#define DO_ALGO_4 \ +DO_FEEDBACK \ +YM2612->in1 += CH->S0_OUT[1]; \ +YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ +CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]) >> OUT_SHIFT; \ +DO_LIMIT + +#define DO_ALGO_5 \ +DO_FEEDBACK \ +YM2612->in1 += CH->S0_OUT[1]; \ +YM2612->in2 += CH->S0_OUT[1]; \ +YM2612->in3 += CH->S0_OUT[1]; \ +CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]) >> OUT_SHIFT; \ +DO_LIMIT + +#define DO_ALGO_6 \ +DO_FEEDBACK \ +YM2612->in1 += CH->S0_OUT[1]; \ +CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]) >> OUT_SHIFT; \ +DO_LIMIT + +#define DO_ALGO_7 \ +DO_FEEDBACK \ +CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2] + CH->S0_OUT[1]) >> OUT_SHIFT; \ +DO_LIMIT + + +#define DO_OUTPUT \ +buf[0][i] += CH->OUTd & CH->LEFT; \ +buf[1][i] += CH->OUTd & CH->RIGHT; + + +#define DO_OUTPUT_INT0 \ +if((int_cnt += YM2612->Inter_Step) & 0x04000) \ +{ \ + int_cnt &= 0x3FFF; \ + buf[0][i] += CH->OUTd & CH->LEFT; \ + buf[1][i] += CH->OUTd & CH->RIGHT; \ +} \ +else i--; + + +#define DO_OUTPUT_INT1 \ +CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ +if((int_cnt += YM2612->Inter_Step) & 0x04000) \ +{ \ + int_cnt &= 0x3FFF; \ + buf[0][i] += CH->Old_OUTd & CH->LEFT; \ + buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ +} \ +else i--; + + +#define DO_OUTPUT_INT2 \ +if((int_cnt += YM2612->Inter_Step) & 0x04000) \ +{ \ + int_cnt &= 0x3FFF; \ + CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ + buf[0][i] += CH->Old_OUTd & CH->LEFT; \ + buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ +} \ +else i--; \ +CH->Old_OUTd = CH->OUTd; + + +#define DO_OUTPUT_INT \ +if((int_cnt += YM2612->Inter_Step) & 0x04000) \ +{ \ + int_cnt &= 0x3FFF; \ + CH->Old_OUTd = (((int_cnt ^ 0x3FFF) * CH->OUTd) + (int_cnt * CH->Old_OUTd)) >> 14; \ + buf[0][i] += CH->Old_OUTd & CH->LEFT; \ + buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ +} \ +else i--; \ +CH->Old_OUTd = CH->OUTd; + + +void Update_Chan_Algo0(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 0 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_0 + DO_OUTPUT + } +} + + +void Update_Chan_Algo1(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 1 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_1 + DO_OUTPUT + } +} + + +void Update_Chan_Algo2(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 2 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_2 + DO_OUTPUT + } +} + + +void Update_Chan_Algo3(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 3 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_3 + DO_OUTPUT + } +} + + +void Update_Chan_Algo4(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 4 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_4 + DO_OUTPUT + } +} + + +void Update_Chan_Algo5(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 5 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_5 + DO_OUTPUT + } +} + + +void Update_Chan_Algo6(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 6 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_6 + DO_OUTPUT + } +} + + +void Update_Chan_Algo7(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 7 len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_7 + DO_OUTPUT + } +} + + +void Update_Chan_Algo0_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 0 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_0 + DO_OUTPUT + } +} + + +void Update_Chan_Algo1_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 1 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_1 + DO_OUTPUT + } +} + + +void Update_Chan_Algo2_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 2 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_2 + DO_OUTPUT + } +} + + +void Update_Chan_Algo3_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 3 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_3 + DO_OUTPUT + } +} + + +void Update_Chan_Algo4_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 4 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_4 + DO_OUTPUT + } +} + + +void Update_Chan_Algo5_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 5 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_5 + DO_OUTPUT + } +} + + +void Update_Chan_Algo6_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 6 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_6 + DO_OUTPUT + } +} + + +void Update_Chan_Algo7_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 7 LFO len = %d\n\n", lenght); +#endif + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_7 + DO_OUTPUT + } +} + + +/****************************************************** + * Interpolated output * + *****************************************************/ + + +void Update_Chan_Algo0_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 0 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_0 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo1_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 1 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_1 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo2_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 2 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_2 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo3_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 3 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_3 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo4_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 4 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_4 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo5_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 5 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_5 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo6_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 6 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_6 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo7_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i; + + if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 7 len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE + GET_CURRENT_ENV + UPDATE_ENV + DO_ALGO_7 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo0_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 0 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_0 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo1_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 1 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_1 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo2_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 2 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_2 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo3_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if(CH->SLOT[S3].Ecnt == ENV_END) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 3 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_3 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo4_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 4 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_4 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo5_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 5 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_5 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo6_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 6 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_6 + DO_OUTPUT_INT + } +} + + +void Update_Chan_Algo7_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) +{ + int i, env_LFO, freq_LFO; + + if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nAlgo 7 LFO len = %d\n\n", lenght); +#endif + + int_cnt = YM2612->Inter_Cnt; + + for(i = 0; i < lenght; i++) + { + GET_CURRENT_PHASE + UPDATE_PHASE_LFO + GET_CURRENT_ENV_LFO + UPDATE_ENV + DO_ALGO_7 + DO_OUTPUT_INT + } +} + + + +/*********************************************** + * fonctions publiques * + ***********************************************/ + + +// Initialisation de l'émulateur YM2612 +ym2612_ *YM2612_Init(int Clock, int Rate, int Interpolation) +{ + ym2612_ *YM2612; + int i, j; + double x; + + if((Rate == 0) || (Clock == 0)) return NULL; + + YM2612 = (ym2612_ *)malloc(sizeof(ym2612_)); + memset(YM2612, 0, sizeof(ym2612_)); + +#if YM_DEBUG_LEVEL > 0 + if(debug_file == NULL) + { + debug_file = fopen("ym2612.log", "w"); + fprintf(debug_file, "YM2612 logging :\n\n"); + } +#endif + + YM2612->Clock = Clock; + YM2612->Rate = Rate; + + // 144 = 12 * (prescale * 2) = 12 * 6 * 2 + // prescale set to 6 by default + + YM2612->Frequence = ((double) YM2612->Clock / (double) YM2612->Rate) / 144.0; + YM2612->TimerBase = (int) (YM2612->Frequence * 4096.0); + + if((Interpolation) && (YM2612->Frequence > 1.0)) + { + YM2612->Inter_Step = (unsigned int) ((1.0 / YM2612->Frequence) * (double) (0x4000)); + YM2612->Inter_Cnt = 0; + + // We recalculate rate and frequence after interpolation + + YM2612->Rate = YM2612->Clock / 144; + YM2612->Frequence = 1.0; + } + else + { + YM2612->Inter_Step = 0x4000; + YM2612->Inter_Cnt = 0; + } + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "YM2612 frequence = %g rate = %d interp step = %.8X\n\n", YM2612->Frequence, YM2612->Rate, YM2612->Inter_Step); +#endif + + // Tableau TL : + // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) + // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) + + for(i = 0; i < TL_LENGHT; i++) + { + if(i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) + { + TL_TAB[TL_LENGHT + i] = TL_TAB[i] = 0; + } + else + { + x = MAX_OUT; // Max output + x /= pow(10, (ENV_STEP * i) / 20); // Decibel -> Voltage + + TL_TAB[i] = (int) x; + TL_TAB[TL_LENGHT + i] = -TL_TAB[i]; + } + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "TL_TAB[%d] = %.8X TL_TAB[%d] = %.8X\n", i, TL_TAB[i], TL_LENGHT + i, TL_TAB[TL_LENGHT + i]); +#endif + } + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "\n\n\n\n"); +#endif + + // Tableau SIN : + // SIN_TAB[x][y] = sin(x) * y; + // x = phase and y = volume + + SIN_TAB[0] = SIN_TAB[SIN_LENGHT / 2] = &TL_TAB[(int)PG_CUT_OFF]; + + for(i = 1; i <= SIN_LENGHT / 4; i++) + { + x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus + x = 20 * log10(1 / x); // convert to dB + + j = (int) (x / ENV_STEP); // Get TL range + + if(j > PG_CUT_OFF) j = (int) PG_CUT_OFF; + + SIN_TAB[i] = SIN_TAB[(SIN_LENGHT / 2) - i] = &TL_TAB[j]; + SIN_TAB[(SIN_LENGHT / 2) + i] = SIN_TAB[SIN_LENGHT - i] = &TL_TAB[TL_LENGHT + j]; + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X\n", i, SIN_TAB[i][0], (SIN_LENGHT / 2) - i, SIN_TAB[(SIN_LENGHT / 2) - i][0], (SIN_LENGHT / 2) + i, SIN_TAB[(SIN_LENGHT / 2) + i][0], SIN_LENGHT - i, SIN_TAB[SIN_LENGHT - i][0]); +#endif + } + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "\n\n\n\n"); +#endif + + // Tableau LFO (LFO wav) : + + for(i = 0; i < LFO_LENGHT; i++) + { + x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x += 1.0; + x /= 2.0; // positive only + x *= 11.8 / ENV_STEP; // ajusted to MAX enveloppe modulation + + LFO_ENV_TAB[i] = (int) x; + + x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x *= (double) ((1 << (LFO_HBITS - 1)) - 1); + + LFO_FREQ_TAB[i] = (int) x; + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "LFO[%d] = %.8X\n", i, LFO_ENV_TAB[i]); +#endif + } + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "\n\n\n\n"); +#endif + + // Tableau Enveloppe : + // ENV_TAB[0] -> ENV_TAB[ENV_LENGHT - 1] = attack curve + // ENV_TAB[ENV_LENGHT] -> ENV_TAB[2 * ENV_LENGHT - 1] = decay curve + + for(i = 0; i < ENV_LENGHT; i++) + { + // Attack curve (x^8 - music level 2 Vectorman 2) + x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); + x *= ENV_LENGHT; + + ENV_TAB[i] = (int) x; + + // Decay curve (just linear) + x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); + x *= ENV_LENGHT; + + ENV_TAB[ENV_LENGHT + i] = (int) x; + +#if YM_DEBUG_LEVEL > 2 + fprintf(debug_file, "ATTACK[%d] = %d DECAY[%d] = %d\n", i, ENV_TAB[i], i, ENV_TAB[ENV_LENGHT + i]); +#endif + } + + ENV_TAB[ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state + + // Tableau pour la conversion Attack -> Decay and Decay -> Attack + + for(i = 0, j = ENV_LENGHT - 1; i < ENV_LENGHT; i++) + { + while (j && (ENV_TAB[j] < (unsigned) i)) j--; + + DECAY_TO_ATTACK[i] = j << ENV_LBITS; + } + + // Tableau pour le Substain Level + + for(i = 0; i < 15; i++) + { + x = i * 3; // 3 and not 6 (Mickey Mania first music for test) + x /= ENV_STEP; + + j = (int) x; + j <<= ENV_LBITS; + + SL_TAB[i] = j + ENV_DECAY; + } + + j = ENV_LENGHT - 1; // special case : volume off + j <<= ENV_LBITS; + SL_TAB[15] = j + ENV_DECAY; + + // Tableau Frequency Step + + for(i = 0; i < 2048; i++) + { + x = (double) (i) * YM2612->Frequence; + +#if((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) + x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); +#else + x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); +#endif + + x /= 2.0; // because MUL = value * 2 + + FINC_TAB[i] = (unsigned int) x; + } + + // Tableaux Attack & Decay Rate + + for(i = 0; i < 4; i++) + { + AR_TAB[i] = 0; + DR_TAB[i] = 0; + } + + for(i = 0; i < 60; i++) + { + x = YM2612->Frequence; + + x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 + x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) + x *= (double) (ENV_LENGHT << ENV_LBITS); // on ajuste pour le tableau ENV_TAB + + AR_TAB[i + 4] = (unsigned int) (x / AR_RATE); + DR_TAB[i + 4] = (unsigned int) (x / DR_RATE); + } + + for(i = 64; i < 96; i++) + { + AR_TAB[i] = AR_TAB[63]; + DR_TAB[i] = DR_TAB[63]; + + NULL_RATE[i - 64] = 0; + } + + // Tableau Detune + + for(i = 0; i < 4; i++) + { + for (j = 0; j < 32; j++) + { +#if((SIN_LBITS + SIN_HBITS - 21) < 0) + x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612->Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); +#else + x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612->Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); +#endif + + DT_TAB[i + 0][j] = (int) x; + DT_TAB[i + 4][j] = (int) -x; + } + } + + // Tableau LFO + + j = (YM2612->Rate * YM2612->Inter_Step) / 0x4000; + + LFO_INC_TAB[0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + LFO_INC_TAB[7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); + + YM2612_Reset(YM2612); + + return YM2612; +} + + +int YM2612_End(ym2612_ *YM2612) +{ + free(YM2612); + +#if YM_DEBUG_LEVEL > 0 + if(debug_file) fclose(debug_file); + debug_file = NULL; +#endif + + return 0; +} + + +int YM2612_Reset(ym2612_ *YM2612) +{ + int i, j; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "\n\nStarting reseting YM2612 ...\n\n"); +#endif + + YM2612->LFOcnt = 0; + YM2612->TimerA = 0; + YM2612->TimerAL = 0; + YM2612->TimerAcnt = 0; + YM2612->TimerB = 0; + YM2612->TimerBL = 0; + YM2612->TimerBcnt = 0; + YM2612->DAC = 0; + YM2612->DACdata = 0; + YM2612->dac_highpass = 0; + + YM2612->Status = 0; + + YM2612->OPNAadr = 0; + YM2612->OPNBadr = 0; + YM2612->Inter_Cnt = 0; + + for(i = 0; i < 6; i++) + { + YM2612->CHANNEL[i].Old_OUTd = 0; + YM2612->CHANNEL[i].OUTd = 0; + YM2612->CHANNEL[i].LEFT = 0xFFFFFFFF; + YM2612->CHANNEL[i].RIGHT = 0xFFFFFFFF; + YM2612->CHANNEL[i].ALGO = 0;; + YM2612->CHANNEL[i].FB = 31; + YM2612->CHANNEL[i].FMS = 0; + YM2612->CHANNEL[i].AMS = 0; + + for(j = 0 ;j < 4 ; j++) + { + YM2612->CHANNEL[i].S0_OUT[j] = 0; + YM2612->CHANNEL[i].FNUM[j] = 0; + YM2612->CHANNEL[i].FOCT[j] = 0; + YM2612->CHANNEL[i].KC[j] = 0; + + YM2612->CHANNEL[i].SLOT[j].Fcnt = 0; + YM2612->CHANNEL[i].SLOT[j].Finc = 0; + YM2612->CHANNEL[i].SLOT[j].Ecnt = ENV_END; // Put it at the end of Decay phase... + YM2612->CHANNEL[i].SLOT[j].Einc = 0; + YM2612->CHANNEL[i].SLOT[j].Ecmp = 0; + YM2612->CHANNEL[i].SLOT[j].Ecurp = RELEASE; + + YM2612->CHANNEL[i].SLOT[j].ChgEnM = 0; + } + } + + for(i = 0; i < 0x100; i++) + { + YM2612->REG[0][i] = -1; + YM2612->REG[1][i] = -1; + } + + for(i = 0xB6; i >= 0xB4; i--) + { + YM2612_Write(YM2612, 0, (unsigned char) i); + YM2612_Write(YM2612, 2, (unsigned char) i); + YM2612_Write(YM2612, 1, 0xC0); + YM2612_Write(YM2612, 3, 0xC0); + } + + for(i = 0xB2; i >= 0x22; i--) + { + YM2612_Write(YM2612, 0, (unsigned char) i); + YM2612_Write(YM2612, 2, (unsigned char) i); + YM2612_Write(YM2612, 1, 0); + YM2612_Write(YM2612, 3, 0); + } + + YM2612_Write(YM2612, 0, 0x2A); + YM2612_Write(YM2612, 1, 0x80); + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "\n\nFinishing reseting YM2612 ...\n\n"); +#endif + + return 0; +} + + +int YM2612_Read(ym2612_ *YM2612) +{ +/* static int cnt = 0; + + if(cnt++ == 50) + { + cnt = 0; + return YM2612->Status; + } + else return YM2612->Status | 0x80; +*/ + return YM2612->Status; +} + + +int YM2612_Write(ym2612_ *YM2612, unsigned char adr, unsigned char data) +{ + int d; + + data &= 0xFF; + adr &= 0x03; + + switch(adr) + { + case 0: + YM2612->OPNAadr = data; + break; + + case 1: + // Trivial optimisation + if(YM2612->OPNAadr == 0x2A) + { + YM2612->DACdata = ((int)data - 0x80) << DAC_SHIFT; + return 0; + } + + d = YM2612->OPNAadr & 0xF0; + + if(d >= 0x30) + { + if(YM2612->REG[0][YM2612->OPNAadr] == data) return 2; + YM2612->REG[0][YM2612->OPNAadr] = data; + +// if (GYM_Dumping) Update_GYM_Dump(1, YM2612->OPNAadr, data); + + if(d < 0xA0) // SLOT + { + SLOT_SET(YM2612, YM2612->OPNAadr, data); + } + else // CHANNEL + { + CHANNEL_SET(YM2612, YM2612->OPNAadr, data); + } + } + else // YM2612 + { + YM2612->REG[0][YM2612->OPNAadr] = data; + +// if ((GYM_Dumping) && ((YM2612->OPNAadr == 0x22) || (YM2612->OPNAadr == 0x27) || (YM2612->OPNAadr == 0x28))) Update_GYM_Dump(1, YM2612->OPNAadr, data); + + YM_SET(YM2612, YM2612->OPNAadr, data); + } + break; + + case 2: + YM2612->OPNBadr = data; + break; + + case 3: + d = YM2612->OPNBadr & 0xF0; + + if(d >= 0x30) + { + if(YM2612->REG[1][YM2612->OPNBadr] == data) return 2; + YM2612->REG[1][YM2612->OPNBadr] = data; + +// if (GYM_Dumping) Update_GYM_Dump(2, YM2612->OPNBadr, data); + + if(d < 0xA0) // SLOT + { + SLOT_SET(YM2612, YM2612->OPNBadr + 0x100, data); + } + else // CHANNEL + { + CHANNEL_SET(YM2612, YM2612->OPNBadr + 0x100, data); + } + } + else return 1; + break; + } + + return 0; +} + +int YM2612_GetMute(ym2612_ *YM2612) +{ + int i, result = 0; + for (i = 0; i < 6; ++i) + { + result |= YM2612->CHANNEL[i].Mute << i; + } + result |= YM2612->DAC_Mute << 6; + //result &= !(YM2612_Enable_SSGEG); + return result; +} + +void YM2612_SetMute(ym2612_ *YM2612, int val) +{ + int i; + for (i = 0; i < 6; ++i) + { + YM2612->CHANNEL[i].Mute = (val >> i) & 1; + } + YM2612->DAC_Mute = (val >> 6) & 1; + //YM2612_Enable_SSGEG = !(val & 1); +} + +void YM2612_SetOptions(int Flags) +{ + DAC_Highpass_Enable = (Flags >> 0) & 0x01; + YM2612_Enable_SSGEG = (Flags >> 1) & 0x01; +} + +void YM2612_ClearBuffer(int **buffer, int length) +{ + // the MAME core does this before updating, + // but the Gens core does this before mixing + int *bufL, *bufR; + int i; + + bufL = buffer[0]; + bufR = buffer[1]; + + for(i = 0; i < length; i++) + { + bufL[i] = 0x0000; + bufR[i] = 0x0000; + } +} + +void YM2612_Update(ym2612_ *YM2612, int **buf, int length) +{ + int i, j, algo_type; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nStarting generating sound...\n\n"); +#endif + + // Mise ?jour des pas des compteurs-fréquences s'ils ont ét?modifiés + + if(YM2612->CHANNEL[0].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[0]); + if(YM2612->CHANNEL[1].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[1]); + if(YM2612->CHANNEL[2].SLOT[0].Finc == -1) + { + if(YM2612->Mode & 0x40) + { + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S0]), FINC_TAB[YM2612->CHANNEL[2].FNUM[2]] >> (7 - YM2612->CHANNEL[2].FOCT[2]), YM2612->CHANNEL[2].KC[2]); + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S1]), FINC_TAB[YM2612->CHANNEL[2].FNUM[3]] >> (7 - YM2612->CHANNEL[2].FOCT[3]), YM2612->CHANNEL[2].KC[3]); + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S2]), FINC_TAB[YM2612->CHANNEL[2].FNUM[1]] >> (7 - YM2612->CHANNEL[2].FOCT[1]), YM2612->CHANNEL[2].KC[1]); + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S3]), FINC_TAB[YM2612->CHANNEL[2].FNUM[0]] >> (7 - YM2612->CHANNEL[2].FOCT[0]), YM2612->CHANNEL[2].KC[0]); + } + else + { + CALC_FINC_CH(&YM2612->CHANNEL[2]); + } + } + if(YM2612->CHANNEL[3].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[3]); + if(YM2612->CHANNEL[4].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[4]); + if(YM2612->CHANNEL[5].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[5]); + +/* + CALC_FINC_CH(&YM2612->CHANNEL[0]); + CALC_FINC_CH(&YM2612->CHANNEL[1]); + if(YM2612->Mode & 0x40) + { + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[0]), FINC_TAB[YM2612->CHANNEL[2].FNUM[2]] >> (7 - YM2612->CHANNEL[2].FOCT[2]), YM2612->CHANNEL[2].KC[2]); + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[1]), FINC_TAB[YM2612->CHANNEL[2].FNUM[3]] >> (7 - YM2612->CHANNEL[2].FOCT[3]), YM2612->CHANNEL[2].KC[3]); + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[2]), FINC_TAB[YM2612->CHANNEL[2].FNUM[1]] >> (7 - YM2612->CHANNEL[2].FOCT[1]), YM2612->CHANNEL[2].KC[1]); + CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[3]), FINC_TAB[YM2612->CHANNEL[2].FNUM[0]] >> (7 - YM2612->CHANNEL[2].FOCT[0]), YM2612->CHANNEL[2].KC[0]); + } + else + { + CALC_FINC_CH(&YM2612->CHANNEL[2]); + } + CALC_FINC_CH(&YM2612->CHANNEL[3]); + CALC_FINC_CH(&YM2612->CHANNEL[4]); + CALC_FINC_CH(&YM2612->CHANNEL[5]); +*/ + + if(YM2612->Inter_Step & 0x04000) algo_type = 0; + else algo_type = 16; + + if(YM2612->LFOinc) + { + // Precalcul LFO wav + + for(i = 0; i < length; i++) + { + j = ((YM2612->LFOcnt += YM2612->LFOinc) >> LFO_LBITS) & LFO_MASK; + + YM2612->LFO_ENV_UP[i] = LFO_ENV_TAB[j]; + YM2612->LFO_FREQ_UP[i] = LFO_FREQ_TAB[j]; + +#if YM_DEBUG_LEVEL > 3 + fprintf(debug_file, "LFO_ENV_UP[%d] = %d LFO_FREQ_UP[%d] = %d\n", i, YM2612->LFO_ENV_UP[i], i, YM2612->LFO_FREQ_UP[i]); +#endif + } + + algo_type |= 8; + } + + + if (!YM2612->CHANNEL[0].Mute) UPDATE_CHAN[YM2612->CHANNEL[0].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[0]), buf, length); + if (!YM2612->CHANNEL[1].Mute) UPDATE_CHAN[YM2612->CHANNEL[1].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[1]), buf, length); + if (!YM2612->CHANNEL[2].Mute) UPDATE_CHAN[YM2612->CHANNEL[2].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[2]), buf, length); + if (!YM2612->CHANNEL[3].Mute) UPDATE_CHAN[YM2612->CHANNEL[3].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[3]), buf, length); + if (!YM2612->CHANNEL[4].Mute) UPDATE_CHAN[YM2612->CHANNEL[4].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[4]), buf, length); + if (!YM2612->CHANNEL[5].Mute + && !(YM2612->DAC)) UPDATE_CHAN[YM2612->CHANNEL[5].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[5]), buf, length); + + YM2612->Inter_Cnt = int_cnt; + +#if YM_DEBUG_LEVEL > 1 + fprintf(debug_file, "\n\nFinishing generating sound...\n\n"); +#endif + +} + + +/*int YM2612_Save(ym2612_ *YM2612, unsigned char SAVE[0x200]) +{ + int i; + + for(i = 0; i < 0x100; i++) + { + SAVE[0x000 + i] = YM2612->REG[0][i]; + SAVE[0x100 + i] = YM2612->REG[1][i]; + } + + return 0; +} + + +int YM2612_Restore(ym2612_ *YM2612, unsigned char SAVE[0x200]) +{ + int i; + + YM2612_Reset(YM2612); + + for(i = 0; i < 0x100; i++) + { + YM2612_Write(YM2612, 0, (unsigned char) i); + YM2612_Write(YM2612, 1, SAVE[0x000 + i]); + YM2612_Write(YM2612, 2, (unsigned char) i); + YM2612_Write(YM2612, 3, SAVE[0x100 + i]); + } + + return 0; +}*/ + +/* Gens */ + +enum { highpass_fract = 15 }; +enum { highpass_shift = 9 }; // higher values reduce highpass on DAC +void YM2612_DacAndTimers_Update(ym2612_ *YM2612, int **buffer, int length) +{ + int *bufL, *bufR; + int i; + + if(YM2612->DAC && YM2612->DACdata && ! YM2612->DAC_Mute) + { + + bufL = buffer[0]; + bufR = buffer[1]; + + for(i = 0; i < length; i++) + { + long dac = (YM2612->DACdata << highpass_fract) - YM2612->dac_highpass; + if (DAC_Highpass_Enable) // else it's left at 0 and doesn't affect the sound + YM2612->dac_highpass += dac >> highpass_shift; + dac >>= highpass_fract; + bufL[i] += dac & YM2612->CHANNEL[5].LEFT; + bufR[i] += dac & YM2612->CHANNEL[5].RIGHT; + } + } + + i = YM2612->TimerBase * length; + + if(YM2612->Mode & 1) // Timer A ON ? + { +// if((YM2612->TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if((YM2612->TimerAcnt -= i) <= 0) + { + YM2612->Status |= (YM2612->Mode & 0x04) >> 2; + YM2612->TimerAcnt += YM2612->TimerAL; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "Counter A overflow\n"); +#endif + + if(YM2612->Mode & 0x80) CSM_Key_Control(YM2612); + } + } + + if(YM2612->Mode & 2) // Timer B ON ? + { +// if((YM2612->TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if((YM2612->TimerBcnt -= i) <= 0) + { + YM2612->Status |= (YM2612->Mode & 0x08) >> 2; + YM2612->TimerBcnt += YM2612->TimerBL; + +#if YM_DEBUG_LEVEL > 0 + fprintf(debug_file, "Counter B overflow\n"); +#endif + } + } +} + +/* Gens */ + +void YM2612_Special_Update(ym2612_ *YM2612) +{ +/* if (YM_Len && YM2612_Enable) + { + YM2612_Update(YM_Buf, YM_Len); + + YM_Buf[0] = Seg_L + Sound_Extrapol[VDP_Current_Line + 1][0]; + YM_Buf[1] = Seg_R + Sound_Extrapol[VDP_Current_Line + 1][0]; + YM_Len = 0; + }*/ +} + +/* end */ diff --git a/Frameworks/GME/vgmplay/chips/ym2612.h b/Frameworks/GME/vgmplay/chips/ym2612.h new file mode 100644 index 000000000..19a5017f3 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ym2612.h @@ -0,0 +1,190 @@ +#ifndef _YM2612_H_ +#define _YM2612_H_ + +// Change it if you need to do long update +//#define MAX_UPDATE_LENGHT 4000 +#define MAX_UPDATE_LENGHT 0x100 +//#define MAX_UPDATE_LENGHT 1 // for in_vgm + +// Gens always uses 16 bits sound (in 32 bits buffer) and do the convertion later if needed. +#define OUTPUT_BITS 15 +// OUTPUT_BITS 15 is MAME's volume level +#define DAC_SHIFT (OUTPUT_BITS - 9) +// DAC_SHIFT makes sure that FM and DAC volume has the same volume + +// VC++ inline +#ifndef INLINE +#define INLINE __inline +#endif + +typedef struct slot__ { + int *DT; // paramètre detune + int MUL; // paramètre "multiple de fréquence" + int TL; // Total Level = volume lorsque l'enveloppe est au plus haut + int TLL; // Total Level ajusted + int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa première phase de régression + int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe + int KSR; // Key Scale Rate = cette valeur est calculée par rapport à la fréquence actuelle, elle va influer + // sur les différents paramètres de l'enveloppe comme l'attaque, le decay ... comme dans la réalité ! + int SEG; // Type enveloppe SSG + int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) + int *DR; // Decay Rate (table pointeur) = Taux pour la régression (DR[KSR]) + int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) + int *RR; // Release Rate (table pointeur) = Taux pour le relâchement (RR[KSR]) + int Fcnt; // Frequency Count = compteur-fréquence pour déterminer l'amplitude actuelle (SIN[Finc >> 16]) + int Finc; // frequency step = pas d'incrémentation du compteur-fréquence + // plus le pas est grand, plus la fréquence est aïgu (ou haute) + int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase + // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... + // en fonction de la valeur de cette variable, on va appeler une fonction permettant + // de mettre à jour l'enveloppe courante. + int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe + int Einc; // Envelope step courant + int Ecmp; // Envelope counter limite pour la prochaine phase + int EincA; // Envelope step for Attack = pas d'incrémentation du compteur durant la phase d'attaque + // cette valeur est égal à AR[KSR] + int EincD; // Envelope step for Decay = pas d'incrémentation du compteur durant la phase de regression + // cette valeur est égal à DR[KSR] + int EincS; // Envelope step for Sustain = pas d'incrémentation du compteur durant la phase de maintenue + // cette valeur est égal à SR[KSR] + int EincR; // Envelope step for Release = pas d'incrémentation du compteur durant la phase de relâchement + // cette valeur est égal à RR[KSR] + int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entrée + // d'un autre ou carrement à la sortie de la voie + int INd; // input data of the slot = données en entrée du slot + int ChgEnM; // Change envelop mask. + int AMS; // AMS depth level of this SLOT = degré de modulation de l'amplitude par le LFO + int AMSon; // AMS enable flag = drapeau d'activation de l'AMS +} slot_; + +typedef struct channel__ { + int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) + int Old_OUTd; // ancienne sortie de la voie (son brut) + int OUTd; // sortie de la voie (son brut) + int LEFT; // LEFT enable flag + int RIGHT; // RIGHT enable flag + int ALGO; // Algorythm = détermine les connections entre les opérateurs + int FB; // shift count of self feed back = degré de "Feed-Back" du SLOT 1 (il est son unique entrée) + int FMS; // Fréquency Modulation Sensitivity of channel = degré de modulation de la fréquence sur la voie par le LFO + int AMS; // Amplitude Modulation Sensitivity of channel = degré de modulation de l'amplitude sur la voie par le LFO + int FNUM[4]; // hauteur fréquence de la voie (+ 3 pour le mode spécial) + int FOCT[4]; // octave de la voie (+ 3 pour le mode spécial) + int KC[4]; // Key Code = valeur fonction de la fréquence (voir KSR pour les slots, KSR = KC >> KSR_S) + struct slot__ SLOT[4]; // four slot.operators = les 4 slots de la voie + int FFlag; // Frequency step recalculation flag + int Mute; // Maxim: channel mute flag +} channel_; + +typedef struct ym2612__ { + int Clock; // Horloge YM2612 + int Rate; // Sample Rate (11025/22050/44100) + int TimerBase; // TimerBase calculation + int Status; // YM2612 Status (timer overflow) + int OPNAadr; // addresse pour l'écriture dans l'OPN A (propre à l'émulateur) + int OPNBadr; // addresse pour l'écriture dans l'OPN B (propre à l'émulateur) + int LFOcnt; // LFO counter = compteur-fréquence pour le LFO + int LFOinc; // LFO step counter = pas d'incrémentation du compteur-fréquence du LFO + // plus le pas est grand, plus la fréquence est grande + int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter + int TimerAL; + int TimerAcnt; // timerA counter = valeur courante du Timer A + int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter + int TimerBL; + int TimerBcnt; // timerB counter = valeur courante du Timer B + int Mode; // Mode actuel des voie 3 et 6 (normal / spécial) + int DAC; // DAC enabled flag + int DACdata; // DAC data + long dac_highpass; + double Frequence; // Fréquence de base, se calcul par rapport à l'horlage et au sample rate + unsigned int Inter_Cnt; // Interpolation Counter + unsigned int Inter_Step; // Interpolation Step + struct channel__ CHANNEL[6]; // Les 6 voies du YM2612 + int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif + // cela nous rend le débuggage plus facile + + int LFO_ENV_UP[MAX_UPDATE_LENGHT]; // Temporary calculated LFO AMS (adjusted for 11.8 dB) * + int LFO_FREQ_UP[MAX_UPDATE_LENGHT]; // Temporary calculated LFO FMS * + + int in0, in1, in2, in3; // current phase calculation * + int en0, en1, en2, en3; // current enveloppe calculation * + + int DAC_Mute; +} ym2612_; + +/* Gens */ + +//extern int YM2612_Enable; +//extern int YM2612_Improv; +//extern int DAC_Enable; +//extern int *YM_Buf[2]; +//extern int YM_Len; + +/* end */ + +ym2612_ *YM2612_Init(int clock, int rate, int interpolation); +int YM2612_End(ym2612_ *YM2612); +int YM2612_Reset(ym2612_ *YM2612); +int YM2612_Read(ym2612_ *YM2612); +int YM2612_Write(ym2612_ *YM2612, unsigned char adr, unsigned char data); +void YM2612_ClearBuffer(int **buffer, int length); +void YM2612_Update(ym2612_ *YM2612, int **buf, int length); +/*int YM2612_Save(ym2612_ *YM2612, unsigned char SAVE[0x200]); +int YM2612_Restore(ym2612_ *YM2612, unsigned char SAVE[0x200]);*/ + +/* Maxim: muting (bits 0-5 for channels 0-5) */ +int YM2612_GetMute(ym2612_ *YM2612); +void YM2612_SetMute(ym2612_ *YM2612, int val); +void YM2612_SetOptions(int Flags); + +/* Gens */ + +void YM2612_DacAndTimers_Update(ym2612_ *YM2612, int **buffer, int length); +void YM2612_Special_Update(ym2612_ *YM2612); + +/* end */ + +// used for foward... +void Update_Chan_Algo0(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo1(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo2(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo3(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo4(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo5(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo6(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo7(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); + +void Update_Chan_Algo0_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo1_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo2_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo3_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo4_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo5_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo6_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo7_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); + +void Update_Chan_Algo0_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo1_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo2_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo3_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo4_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo5_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo6_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo7_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); + +void Update_Chan_Algo0_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo1_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo2_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo3_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo4_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo5_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo6_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); +void Update_Chan_Algo7_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght); + +// used for foward... +void Env_Attack_Next(slot_ *SL); +void Env_Decay_Next(slot_ *SL); +void Env_Substain_Next(slot_ *SL); +void Env_Release_Next(slot_ *SL); +void Env_NULL_Next(slot_ *SL); + +#endif diff --git a/Frameworks/GME/gme/ymdeltat.cpp b/Frameworks/GME/vgmplay/chips/ymdeltat.c similarity index 94% rename from Frameworks/GME/gme/ymdeltat.cpp rename to Frameworks/GME/vgmplay/chips/ymdeltat.c index 4e14f143c..6272f1711 100644 --- a/Frameworks/GME/gme/ymdeltat.cpp +++ b/Frameworks/GME/vgmplay/chips/ymdeltat.c @@ -1,659 +1,676 @@ -/* -** -** File: ymdeltat.c -** -** YAMAHA DELTA-T adpcm sound emulation subroutine -** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) -** -** Base program is YM2610 emulator by Hiromitsu Shioya. -** Written by Tatsuyuki Satoh -** Improvements by Jarek Burczynski (bujar at mame dot net) -** -** -** History: -** -** 03-08-2003 Jarek Burczynski: -** - fixed BRDY flag implementation. -** -** 24-07-2003 Jarek Burczynski, Frits Hilderink: -** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset -** -** 22-07-2003 Jarek Burczynski, Frits Hilderink: -** - fixed external memory support -** -** 15-06-2003 Jarek Burczynski: -** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) -** - implemented support for the Limit address register -** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM -** - implemented external memory access (read/write) via the ADPCM data reg reads/writes -** Thanks go to Frits Hilderink for the example code. -** -** 14-06-2003 Jarek Burczynski: -** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO -** - modified EOS handling -** -** 05-04-2003 Jarek Burczynski: -** - implemented partial support for external/processor memory on sample replay -** -** 01-12-2002 Jarek Burczynski: -** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi -** - renamed/removed some YM_DELTAT struct fields -** -** 28-12-2001 Acho A. Tang -** - added EOS status report on ADPCM playback. -** -** 05-08-2001 Jarek Burczynski: -** - now_step is initialized with 0 at the start of play. -** -** 12-06-2001 Jarek Burczynski: -** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. -** Checked on real YM2610 chip - address register is 24 bits wide. -** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. -** -** TO DO: -** Check size of the address register on the other chips.... -** -** Version 0.72 -** -** sound chips that have this unit: -** YM2608 OPNA -** YM2610/B OPNB -** Y8950 MSX AUDIO -** -*/ - -#include "ymdeltat.h" -#ifndef INLINE -#define INLINE __inline -#endif -#ifndef logerror -#define logerror (void) -#endif - -#define YM_DELTAT_DELTA_MAX (24576) -#define YM_DELTAT_DELTA_MIN (127) -#define YM_DELTAT_DELTA_DEF (127) - -#define YM_DELTAT_DECODE_RANGE 32768 -#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) -#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) - - -/* Forecast to next Forecast (rate = *8) */ -/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ -static const INT32 ym_deltat_decode_tableB1[16] = { - 1, 3, 5, 7, 9, 11, 13, 15, - -1, -3, -5, -7, -9, -11, -13, -15, -}; -/* delta to next delta (rate= *64) */ -/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ -static const INT32 ym_deltat_decode_tableB2[16] = { - 57, 57, 57, 57, 77, 102, 128, 153, - 57, 57, 57, 57, 77, 102, 128, 153 -}; - -#if 0 -void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT) -{ - logerror("BRDY_callback reached (flag set) !\n"); - - /* set BRDY bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); -} -#endif - -UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT) -{ - UINT8 v = 0; - - /* external memory read */ - if ( (DELTAT->portstate & 0xe0)==0x20 ) - { - /* two dummy reads */ - if (DELTAT->memread) - { - DELTAT->now_addr = DELTAT->start << 1; - DELTAT->memread--; - return 0; - } - - - if ( DELTAT->now_addr != (DELTAT->end<<1) ) - { - v = DELTAT->memory[DELTAT->now_addr>>1]; - - /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ - - DELTAT->now_addr+=2; /* two nibbles at a time */ - - /* reset BRDY bit in status register, which means we are reading the memory now */ - if(DELTAT->status_reset_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - - /* setup a timer that will callback us in 10 master clock cycles for Y8950 - * in the callback set the BRDY flag to 1 , which means we have another data ready. - * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. - */ - /* set BRDY bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - } - else - { - /* set EOS bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_EOS_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); - } - } - - return v; -} - - -/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ -static const UINT8 dram_rightshift[4]={3,0,0,0}; - -/* DELTA-T ADPCM write register */ -void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v) -{ - if(r>=0x10) return; - DELTAT->reg[r] = v; /* stock data */ - - switch( r ) - { - case 0x00: -/* -START: - Accessing *external* memory is started when START bit (D7) is set to "1", so - you must set all conditions needed for recording/playback before starting. - If you access *CPU-managed* memory, recording/playback starts after - read/write of ADPCM data register $08. - -REC: - 0 = ADPCM synthesis (playback) - 1 = ADPCM analysis (record) - -MEMDATA: - 0 = processor (*CPU-managed*) memory (means: using register $08) - 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) - - -SPOFF: - controls output pin that should disable the speaker while ADPCM analysis - -RESET and REPEAT only work with external memory. - - -some examples: -value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: - C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register - E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register - 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register - a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register - - 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 - 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 - -*/ - /* handle emulation mode */ - if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) - { - v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ - } - - DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ - - if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ - { - /* set PCM BUSY bit */ - DELTAT->PCM_BSY = 1; - - /* start ADPCM */ - DELTAT->now_step = 0; - DELTAT->acc = 0; - DELTAT->prev_acc = 0; - DELTAT->adpcml = 0; - DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; - DELTAT->now_data = 0; - - } - - if( DELTAT->portstate&0x20 ) /* do we access external memory? */ - { - DELTAT->now_addr = DELTAT->start << 1; - DELTAT->memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ - - /* if yes, then let's check if ADPCM memory is mapped and big enough */ - if(DELTAT->memory == 0) - { - /*logerror("YM Delta-T ADPCM rom not mapped\n");*/ - DELTAT->portstate = 0x00; - DELTAT->PCM_BSY = 0; - } - else - { - if( DELTAT->end >= DELTAT->memory_size ) /* Check End in Range */ - { - /*logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end);*/ - DELTAT->end = DELTAT->memory_size - 1; - } - if( DELTAT->start >= DELTAT->memory_size ) /* Check Start in Range */ - { - /*logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start);*/ - DELTAT->portstate = 0x00; - DELTAT->PCM_BSY = 0; - } - } - } - else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ - { - DELTAT->now_addr = 0; - } - - if( DELTAT->portstate&0x01 ) - { - DELTAT->portstate = 0x00; - - /* clear PCM BUSY bit (in status register) */ - DELTAT->PCM_BSY = 0; - - /* set BRDY flag */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - } - break; - case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ - /* handle emulation mode */ - if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) - { - v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */ - } - - DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03]; - if ((DELTAT->control2 & 3) != (v & 3)) - { - /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ - if (DELTAT->DRAMportshift != dram_rightshift[v&3]) - { - DELTAT->DRAMportshift = dram_rightshift[v&3]; - - /* final shift value depends on chip type and memory type selected: - 8 for YM2610 (ROM only), - 5 for ROM for Y8950 and YM2608, - 5 for x8bit DRAMs for Y8950 and YM2608, - 2 for x1bit DRAMs for Y8950 and YM2608. - */ - - /* refresh addresses */ - DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); - DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); - DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; - DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); - } - } - DELTAT->control2 = v; - break; - case 0x02: /* Start Address L */ - case 0x03: /* Start Address H */ - DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); - /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/ - break; - case 0x04: /* Stop Address L */ - case 0x05: /* Stop Address H */ - DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); - DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; - /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->end );*/ - break; - case 0x06: /* Prescale L (ADPCM and Record frq) */ - case 0x07: /* Prescale H */ - break; - case 0x08: /* ADPCM data */ - -/* -some examples: -value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: - C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register - E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register - 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register - a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register - - 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 - 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 - -*/ - - /* external memory write */ - if ( (DELTAT->portstate & 0xe0)==0x60 ) - { - if (DELTAT->memread) - { - DELTAT->now_addr = DELTAT->start << 1; - DELTAT->memread = 0; - } - - /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ - - if ( DELTAT->now_addr != (DELTAT->end<<1) ) - { - DELTAT->memory[DELTAT->now_addr>>1] = v; - DELTAT->now_addr+=2; /* two nibbles at a time */ - - /* reset BRDY bit in status register, which means we are processing the write */ - if(DELTAT->status_reset_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - - /* setup a timer that will callback us in 10 master clock cycles for Y8950 - * in the callback set the BRDY flag to 1 , which means we have written the data. - * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. - */ - /* set BRDY bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - - } - else - { - /* set EOS bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_EOS_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); - } - - return; - } - - /* ADPCM synthesis from CPU */ - if ( (DELTAT->portstate & 0xe0)==0x80 ) - { - DELTAT->CPU_data = v; - - /* Reset BRDY bit in status register, which means we are full of data */ - if(DELTAT->status_reset_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - return; - } - - break; - case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ - case 0x0a: /* DELTA-N H */ - DELTAT->delta = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]); - DELTAT->step = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) ); - /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/ - break; - case 0x0b: /* Output level control (volume, linear) */ - { - INT32 oldvol = DELTAT->volume; - DELTAT->volume = (v&0xff) * (DELTAT->output_range/256) / YM_DELTAT_DECODE_RANGE; -/* v * ((1<<16)>>8) >> 15; -* thus: v * (1<<8) >> 15; -* thus: output_range must be (1 << (15+8)) at least -* v * ((1<<23)>>8) >> 15; -* v * (1<<15) >> 15; -*/ - /*logerror("DELTAT vol = %2x\n",v&0xff);*/ - if( oldvol != 0 ) - { - DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume); - } - } - break; - case 0x0c: /* Limit Address L */ - case 0x0d: /* Limit Address H */ - DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); - /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/ - break; - } -} - -void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode) -{ - DELTAT->now_addr = 0; - DELTAT->now_step = 0; - DELTAT->step = 0; - DELTAT->start = 0; - DELTAT->end = 0; - DELTAT->limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ - DELTAT->volume = 0; - DELTAT->pan = &DELTAT->output_pointer[pan]; - DELTAT->acc = 0; - DELTAT->prev_acc = 0; - DELTAT->adpcmd = 127; - DELTAT->adpcml = 0; - DELTAT->emulation_mode = (UINT8)emulation_mode; - DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0; - DELTAT->control2 = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ - DELTAT->DRAMportshift = dram_rightshift[DELTAT->control2 & 3]; - - /* The flag mask register disables the BRDY after the reset, however - ** as soon as the mask is enabled the flag needs to be set. */ - - /* set BRDY bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); -} - -#if 0 -void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs) -{ - int r; - - /* to keep adpcml */ - DELTAT->volume = 0; - /* update */ - for(r=1;r<16;r++) - YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]); - DELTAT->reg[0] = regs[0]; - - /* current rom data */ - if (DELTAT->memory) - DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) ); - -} -void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT) -{ -#ifdef __STATE_H__ - state_save_register_device_item(device, 0, DELTAT->portstate); - state_save_register_device_item(device, 0, DELTAT->now_addr); - state_save_register_device_item(device, 0, DELTAT->now_step); - state_save_register_device_item(device, 0, DELTAT->acc); - state_save_register_device_item(device, 0, DELTAT->prev_acc); - state_save_register_device_item(device, 0, DELTAT->adpcmd); - state_save_register_device_item(device, 0, DELTAT->adpcml); -#endif -} -#endif - - -#define YM_DELTAT_Limit(val,max,min) \ -{ \ - if ( val > max ) val = max; \ - else if ( val < min ) val = min; \ -} - -INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) -{ - UINT32 step; - int data; - - DELTAT->now_step += DELTAT->step; - if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; - DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) - DELTAT->now_addr = 0; - - if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ - if( DELTAT->portstate&0x10 ){ - /* repeat start */ - DELTAT->now_addr = DELTAT->start<<1; - DELTAT->acc = 0; - DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; - DELTAT->prev_acc = 0; - }else{ - /* set EOS bit in status register */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_EOS_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); - - /* clear PCM BUSY bit (reflected in status register) */ - DELTAT->PCM_BSY = 0; - - DELTAT->portstate = 0; - DELTAT->adpcml = 0; - DELTAT->prev_acc = 0; - return; - } - } - - if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; - else - { - DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1)); - data = DELTAT->now_data >> 4; - } - - DELTAT->now_addr++; - /* 12-06-2001 JB: */ - /* YM2610 address register is 24 bits wide.*/ - /* The "+1" is there because we use 1 bit more for nibble calculations.*/ - /* WARNING: */ - /* Side effect: we should take the size of the mapped ROM into account */ - DELTAT->now_addr &= ( (1<<(24+1))-1); - - /* store accumulator value */ - DELTAT->prev_acc = DELTAT->acc; - - /* Forecast to next Forecast */ - DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); - YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); - - /* delta to next delta */ - DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; - YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); - - /* ElSemi: Fix interpolator. */ - /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ - - }while(--step); - - } - - /* ElSemi: Fix interpolator. */ - DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); - DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); - DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; - - /* output for work of output channels (outd[OPNxxxx])*/ - *(DELTAT->pan) += DELTAT->adpcml; -} - - - -INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) -{ - UINT32 step; - int data; - - DELTAT->now_step += DELTAT->step; - if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; - DELTAT->now_step &= (1<now_addr&1 ) - { - data = DELTAT->now_data & 0x0f; - - DELTAT->now_data = DELTAT->CPU_data; - - /* after we used CPU_data, we set BRDY bit in status register, - * which means we are ready to accept another byte of data */ - if(DELTAT->status_set_handler) - if(DELTAT->status_change_BRDY_bit) - (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); - } - else - { - data = DELTAT->now_data >> 4; - } - - DELTAT->now_addr++; - - /* store accumulator value */ - DELTAT->prev_acc = DELTAT->acc; - - /* Forecast to next Forecast */ - DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); - YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); - - /* delta to next delta */ - DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; - YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); - - - }while(--step); - - } - - /* ElSemi: Fix interpolator. */ - DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); - DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); - DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; - - /* output for work of output channels (outd[OPNxxxx])*/ - *(DELTAT->pan) += DELTAT->adpcml; -} - - - -/* ADPCM B (Delta-T control type) */ -void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT) -{ - -/* -some examples: -value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: - 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register - a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register - C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register - E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register - - 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 - 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 - -*/ - - if ( (DELTAT->portstate & 0xe0)==0xa0 ) - { - YM_DELTAT_synthesis_from_external_memory(DELTAT); - return; - } - - if ( (DELTAT->portstate & 0xe0)==0x80 ) - { - /* ADPCM synthesis from CPU-managed memory (from reg $08) */ - YM_DELTAT_synthesis_from_CPU_memory(DELTAT); /* change output based on data in ADPCM data reg ($08) */ - return; - } - -//todo: ADPCM analysis -// if ( (DELTAT->portstate & 0xe0)==0xc0 ) -// if ( (DELTAT->portstate & 0xe0)==0xe0 ) - - return; -} - +/* +** +** File: ymdeltat.c +** +** YAMAHA DELTA-T adpcm sound emulation subroutine +** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) +** +** Base program is YM2610 emulator by Hiromitsu Shioya. +** Written by Tatsuyuki Satoh +** Improvements by Jarek Burczynski (bujar at mame dot net) +** +** +** History: +** +** 03-08-2003 Jarek Burczynski: +** - fixed BRDY flag implementation. +** +** 24-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset +** +** 22-07-2003 Jarek Burczynski, Frits Hilderink: +** - fixed external memory support +** +** 15-06-2003 Jarek Burczynski: +** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) +** - implemented support for the Limit address register +** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM +** - implemented external memory access (read/write) via the ADPCM data reg reads/writes +** Thanks go to Frits Hilderink for the example code. +** +** 14-06-2003 Jarek Burczynski: +** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO +** - modified EOS handling +** +** 05-04-2003 Jarek Burczynski: +** - implemented partial support for external/processor memory on sample replay +** +** 01-12-2002 Jarek Burczynski: +** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi +** - renamed/removed some YM_DELTAT struct fields +** +** 28-12-2001 Acho A. Tang +** - added EOS status report on ADPCM playback. +** +** 05-08-2001 Jarek Burczynski: +** - now_step is initialized with 0 at the start of play. +** +** 12-06-2001 Jarek Burczynski: +** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. +** Checked on real YM2610 chip - address register is 24 bits wide. +** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. +** +** TO DO: +** Check size of the address register on the other chips.... +** +** Version 0.72 +** +** sound chips that have this unit: +** YM2608 OPNA +** YM2610/B OPNB +** Y8950 MSX AUDIO +** +*/ + +#include "mamedef.h" +#include +//#include "sndintrf.h" +#include "ymdeltat.h" + +#define YM_DELTAT_DELTA_MAX (24576) +#define YM_DELTAT_DELTA_MIN (127) +#define YM_DELTAT_DELTA_DEF (127) + +#define YM_DELTAT_DECODE_RANGE 32768 +#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) +#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) + + +/* Forecast to next Forecast (rate = *8) */ +/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ +static const INT32 ym_deltat_decode_tableB1[16] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15, +}; +/* delta to next delta (rate= *64) */ +/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ +static const INT32 ym_deltat_decode_tableB2[16] = { + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153 +}; + +#if 0 +void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT) +{ + logerror("BRDY_callback reached (flag set) !\n"); + + /* set BRDY bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); +} +#endif + +UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT) +{ + UINT8 v = 0; + + /* external memory read */ + if ( (DELTAT->portstate & 0xe0)==0x20 ) + { + /* two dummy reads */ + if (DELTAT->memread) + { + DELTAT->now_addr = DELTAT->start << 1; + DELTAT->memread--; + return 0; + } + + + if ( DELTAT->now_addr != (DELTAT->end<<1) ) + { + v = DELTAT->memory[DELTAT->now_addr>>1]; + + /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ + + DELTAT->now_addr+=2; /* two nibbles at a time */ + + /* reset BRDY bit in status register, which means we are reading the memory now */ + if(DELTAT->status_reset_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have another data ready. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + } + else + { + /* set EOS bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_EOS_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); + } + } + + return v; +} + + +/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ +static const UINT8 dram_rightshift[4]={3,0,0,0}; + +/* DELTA-T ADPCM write register */ +void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v) +{ + if(r>=0x10) return; + DELTAT->reg[r] = v; /* stock data */ + + switch( r ) + { + case 0x00: +/* +START: + Accessing *external* memory is started when START bit (D7) is set to "1", so + you must set all conditions needed for recording/playback before starting. + If you access *CPU-managed* memory, recording/playback starts after + read/write of ADPCM data register $08. + +REC: + 0 = ADPCM synthesis (playback) + 1 = ADPCM analysis (record) + +MEMDATA: + 0 = processor (*CPU-managed*) memory (means: using register $08) + 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) + + +SPOFF: + controls output pin that should disable the speaker while ADPCM analysis + +RESET and REPEAT only work with external memory. + + +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + /* handle emulation mode */ + if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) + { + v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ + } + + DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ + + if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ + { + /* set PCM BUSY bit */ + DELTAT->PCM_BSY = 1; + + /* start ADPCM */ + DELTAT->now_step = 0; + DELTAT->acc = 0; + DELTAT->prev_acc = 0; + DELTAT->adpcml = 0; + DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; + DELTAT->now_data = 0; + if (DELTAT->start > DELTAT->end) + logerror("DeltaT-Warning: Start: %06X, End: %06X\n", DELTAT->start, DELTAT->end); + } + + if( DELTAT->portstate&0x20 ) /* do we access external memory? */ + { + DELTAT->now_addr = DELTAT->start << 1; + DELTAT->memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ + + /* if yes, then let's check if ADPCM memory is mapped and big enough */ + if(DELTAT->memory == 0) + { +#ifdef _DEBUG + logerror("YM Delta-T ADPCM rom not mapped\n"); +#endif + DELTAT->portstate = 0x00; + DELTAT->PCM_BSY = 0; + } + else + { + if( DELTAT->end >= DELTAT->memory_size ) /* Check End in Range */ + { +#ifdef _DEBUG + logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end); +#endif + DELTAT->end = DELTAT->memory_size - 1; + } + if( DELTAT->start >= DELTAT->memory_size ) /* Check Start in Range */ + { +#ifdef _DEBUG + logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start); +#endif + DELTAT->portstate = 0x00; + DELTAT->PCM_BSY = 0; + } + } + } + else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ + { + DELTAT->now_addr = 0; + } + + if( DELTAT->portstate&0x01 ) + { + DELTAT->portstate = 0x00; + + /* clear PCM BUSY bit (in status register) */ + DELTAT->PCM_BSY = 0; + + /* set BRDY flag */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + } + break; + case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ + /* handle emulation mode */ + if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) + { + v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't have ROM/RAM memory flag bit. */ + } + + DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03]; + if ((DELTAT->control2 & 3) != (v & 3)) + { + /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ + if (DELTAT->DRAMportshift != dram_rightshift[v&3]) + { + DELTAT->DRAMportshift = dram_rightshift[v&3]; + + /* final shift value depends on chip type and memory type selected: + 8 for YM2610 (ROM only), + 5 for ROM for Y8950 and YM2608, + 5 for x8bit DRAMs for Y8950 and YM2608, + 2 for x1bit DRAMs for Y8950 and YM2608. + */ + + /* refresh addresses */ + DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); + DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); + DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; + DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); + } + } + DELTAT->control2 = v; + break; + case 0x02: /* Start Address L */ + case 0x03: /* Start Address H */ + DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); + /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/ + break; + case 0x04: /* Stop Address L */ + case 0x05: /* Stop Address H */ + DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); + DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; + /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->end );*/ + break; + case 0x06: /* Prescale L (ADPCM and Record frq) */ + case 0x07: /* Prescale H */ + break; + case 0x08: /* ADPCM data */ + +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + /* external memory write */ + if ( (DELTAT->portstate & 0xe0)==0x60 ) + { + if (DELTAT->memread) + { + DELTAT->now_addr = DELTAT->start << 1; + DELTAT->memread = 0; + } + + /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ + + if ( DELTAT->now_addr != (DELTAT->end<<1) ) + { + DELTAT->memory[DELTAT->now_addr>>1] = v; + DELTAT->now_addr+=2; /* two nibbles at a time */ + + /* reset BRDY bit in status register, which means we are processing the write */ + if(DELTAT->status_reset_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + + /* setup a timer that will callback us in 10 master clock cycles for Y8950 + * in the callback set the BRDY flag to 1 , which means we have written the data. + * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. + */ + /* set BRDY bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + + } + else + { + /* set EOS bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_EOS_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); + } + + return; + } + + /* ADPCM synthesis from CPU */ + if ( (DELTAT->portstate & 0xe0)==0x80 ) + { + DELTAT->CPU_data = v; + + /* Reset BRDY bit in status register, which means we are full of data */ + if(DELTAT->status_reset_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + return; + } + + break; + case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ + case 0x0a: /* DELTA-N H */ + DELTAT->delta = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]); + DELTAT->step = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) ); + /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/ + break; + case 0x0b: /* Output level control (volume, linear) */ + { + INT32 oldvol = DELTAT->volume; + DELTAT->volume = (v&0xff) * (DELTAT->output_range/256) / YM_DELTAT_DECODE_RANGE; +/* v * ((1<<16)>>8) >> 15; +* thus: v * (1<<8) >> 15; +* thus: output_range must be (1 << (15+8)) at least +* v * ((1<<23)>>8) >> 15; +* v * (1<<15) >> 15; +*/ + /*logerror("DELTAT vol = %2x\n",v&0xff);*/ + if( oldvol != 0 ) + { + DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume); + } + } + break; + case 0x0c: /* Limit Address L */ + case 0x0d: /* Limit Address H */ + DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); + /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/ + break; + } +} + +void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode) +{ + DELTAT->now_addr = 0; + DELTAT->now_step = 0; + DELTAT->step = 0; + DELTAT->start = 0; + DELTAT->end = 0; + DELTAT->limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ + DELTAT->volume = 0; + DELTAT->pan = &DELTAT->output_pointer[pan]; + DELTAT->acc = 0; + DELTAT->prev_acc = 0; + DELTAT->adpcmd = 127; + DELTAT->adpcml = 0; + DELTAT->emulation_mode = (UINT8)emulation_mode; + DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0; + DELTAT->control2 = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ + DELTAT->DRAMportshift = dram_rightshift[DELTAT->control2 & 3]; + + /* The flag mask register disables the BRDY after the reset, however + ** as soon as the mask is enabled the flag needs to be set. */ + + /* set BRDY bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); +} + +/*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs) +{ + int r; + + // to keep adpcml + DELTAT->volume = 0; + // update + for(r=1;r<16;r++) + YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]); + DELTAT->reg[0] = regs[0]; + + // current rom data + if (DELTAT->memory) + DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) ); + +} +//void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT) +void YM_DELTAT_savestate(YM_DELTAT *DELTAT) +{ +#ifdef __STATE_H__ + state_save_register_device_item(device, 0, DELTAT->portstate); + state_save_register_device_item(device, 0, DELTAT->now_addr); + state_save_register_device_item(device, 0, DELTAT->now_step); + state_save_register_device_item(device, 0, DELTAT->acc); + state_save_register_device_item(device, 0, DELTAT->prev_acc); + state_save_register_device_item(device, 0, DELTAT->adpcmd); + state_save_register_device_item(device, 0, DELTAT->adpcml); +#endif +}*/ + + +#define YM_DELTAT_Limit(val,max,min) \ +{ \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) +{ + UINT32 step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) + DELTAT->now_addr = 0; + + if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ + if( DELTAT->portstate&0x10 ){ + /* repeat start */ + DELTAT->now_addr = DELTAT->start<<1; + DELTAT->acc = 0; + DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; + DELTAT->prev_acc = 0; + }else{ + /* set EOS bit in status register */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_EOS_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); + + /* clear PCM BUSY bit (reflected in status register) */ + DELTAT->PCM_BSY = 0; + + DELTAT->portstate = 0; + DELTAT->adpcml = 0; + DELTAT->prev_acc = 0; + return; + } + } + + if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; + else + { + DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1)); + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + /* 12-06-2001 JB: */ + /* YM2610 address register is 24 bits wide.*/ + /* The "+1" is there because we use 1 bit more for nibble calculations.*/ + /* WARNING: */ + /* Side effect: we should take the size of the mapped ROM into account */ + //DELTAT->now_addr &= ( (1<<(24+1))-1); + DELTAT->now_addr &= DELTAT->memory_mask; + + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + /* ElSemi: Fix interpolator. */ + /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) +{ + UINT32 step; + int data; + + DELTAT->now_step += DELTAT->step; + if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; + DELTAT->now_step &= (1<now_addr&1 ) + { + data = DELTAT->now_data & 0x0f; + + DELTAT->now_data = DELTAT->CPU_data; + + /* after we used CPU_data, we set BRDY bit in status register, + * which means we are ready to accept another byte of data */ + if(DELTAT->status_set_handler) + if(DELTAT->status_change_BRDY_bit) + (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); + } + else + { + data = DELTAT->now_data >> 4; + } + + DELTAT->now_addr++; + + /* store accumulator value */ + DELTAT->prev_acc = DELTAT->acc; + + /* Forecast to next Forecast */ + DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); + YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); + + /* delta to next delta */ + DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; + YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); + + + }while(--step); + + } + + /* ElSemi: Fix interpolator. */ + DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); + DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); + DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; + + /* output for work of output channels (outd[OPNxxxx])*/ + *(DELTAT->pan) += DELTAT->adpcml; +} + + + +/* ADPCM B (Delta-T control type) */ +void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT) +{ + +/* +some examples: +value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: + 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register + a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register + C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register + E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register + + 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 + 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 + +*/ + + if ( (DELTAT->portstate & 0xe0)==0xa0 ) + { + YM_DELTAT_synthesis_from_external_memory(DELTAT); + return; + } + + if ( (DELTAT->portstate & 0xe0)==0x80 ) + { + /* ADPCM synthesis from CPU-managed memory (from reg $08) */ + YM_DELTAT_synthesis_from_CPU_memory(DELTAT); /* change output based on data in ADPCM data reg ($08) */ + return; + } + +//todo: ADPCM analysis +// if ( (DELTAT->portstate & 0xe0)==0xc0 ) +// if ( (DELTAT->portstate & 0xe0)==0xe0 ) + + return; +} + +void YM_DELTAT_calc_mem_mask(YM_DELTAT* DELTAT) +{ + UINT32 MaskSize; + + MaskSize = 0x01; + while(MaskSize < DELTAT->memory_size) + MaskSize <<= 1; + + DELTAT->memory_mask = (MaskSize << 1) - 1; // it's Mask<<1 because of the nibbles + + return; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/ymdeltat.h b/Frameworks/GME/vgmplay/chips/ymdeltat.h similarity index 90% rename from Frameworks/GME/gme/ymdeltat.h rename to Frameworks/GME/vgmplay/chips/ymdeltat.h index 9ae628757..7852caeda 100644 --- a/Frameworks/GME/gme/ymdeltat.h +++ b/Frameworks/GME/vgmplay/chips/ymdeltat.h @@ -1,94 +1,83 @@ -#pragma once - -#ifndef __YMDELTAT_H__ -#define __YMDELTAT_H__ - -#include "mamedef.h" - -#define YM_DELTAT_SHIFT (16) - -#define YM_DELTAT_EMULATION_MODE_NORMAL 0 -#define YM_DELTAT_EMULATION_MODE_YM2610 1 - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits); - - -/* DELTA-T (adpcm type B) struct */ -typedef struct deltat_adpcm_state { /* AT: rearranged and tigntened structure */ - UINT8 *memory; - INT32 *output_pointer;/* pointer of output pointers */ - INT32 *pan; /* pan : &output_pointer[pan] */ - double freqbase; -#if 0 - double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ - double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ -#endif - UINT32 memory_size; - int output_range; - UINT32 now_addr; /* current address */ - UINT32 now_step; /* currect step */ - UINT32 step; /* step */ - UINT32 start; /* start address */ - UINT32 limit; /* limit address */ - UINT32 end; /* end address */ - UINT32 delta; /* delta scale */ - INT32 volume; /* current volume */ - INT32 acc; /* shift Measurement value*/ - INT32 adpcmd; /* next Forecast */ - INT32 adpcml; /* current value */ - INT32 prev_acc; /* leveling value */ - UINT8 now_data; /* current rom data */ - UINT8 CPU_data; /* current data from reg 08 */ - UINT8 portstate; /* port status */ - UINT8 control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ - UINT8 portshift; /* address bits shift-left: - ** 8 for YM2610, - ** 5 for Y8950 and YM2608 */ - - UINT8 DRAMportshift; /* address bits shift-right: - ** 0 for ROM and x8bit DRAMs, - ** 3 for x1 DRAMs */ - - UINT8 memread; /* needed for reading/writing external memory */ - - /* handlers and parameters for the status flags support */ - STATUS_CHANGE_HANDLER status_set_handler; - STATUS_CHANGE_HANDLER status_reset_handler; - - /* note that different chips have these flags on different - ** bits of the status register - */ - void * status_change_which_chip; /* this chip id */ - UINT8 status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ - UINT8 status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ - UINT8 status_change_ZERO_bit; /* 1 if silence lasts for more than 290 miliseconds on ADPCM recording */ - - /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, - ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) - */ - UINT8 PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ - - UINT8 reg[16]; /* adpcm registers */ - UINT8 emulation_mode; /* which chip we're emulating */ -}YM_DELTAT; - -/*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/ - -UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT); -void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v); -void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode); -void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT); - -/*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs); -void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT);*/ +#pragma once -#ifdef __cplusplus -} +#define YM_DELTAT_SHIFT (16) + +#define YM_DELTAT_EMULATION_MODE_NORMAL 0 +#define YM_DELTAT_EMULATION_MODE_YM2610 1 + + +typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits); + + +/* DELTA-T (adpcm type B) struct */ +typedef struct deltat_adpcm_state { /* AT: rearranged and tigntened structure */ + UINT8 *memory; + INT32 *output_pointer;/* pointer of output pointers */ + INT32 *pan; /* pan : &output_pointer[pan] */ + double freqbase; +#if 0 + double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ + double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ #endif + UINT32 memory_size; + UINT32 memory_mask; + int output_range; + UINT32 now_addr; /* current address */ + UINT32 now_step; /* currect step */ + UINT32 step; /* step */ + UINT32 start; /* start address */ + UINT32 limit; /* limit address */ + UINT32 end; /* end address */ + UINT32 delta; /* delta scale */ + INT32 volume; /* current volume */ + INT32 acc; /* shift Measurement value*/ + INT32 adpcmd; /* next Forecast */ + INT32 adpcml; /* current value */ + INT32 prev_acc; /* leveling value */ + UINT8 now_data; /* current rom data */ + UINT8 CPU_data; /* current data from reg 08 */ + UINT8 portstate; /* port status */ + UINT8 control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ + UINT8 portshift; /* address bits shift-left: + ** 8 for YM2610, + ** 5 for Y8950 and YM2608 */ -#endif /* __YMDELTAT_H__ */ + UINT8 DRAMportshift; /* address bits shift-right: + ** 0 for ROM and x8bit DRAMs, + ** 3 for x1 DRAMs */ + + UINT8 memread; /* needed for reading/writing external memory */ + + /* handlers and parameters for the status flags support */ + STATUS_CHANGE_HANDLER status_set_handler; + STATUS_CHANGE_HANDLER status_reset_handler; + + /* note that different chips have these flags on different + ** bits of the status register + */ + void * status_change_which_chip; /* this chip id */ + UINT8 status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ + UINT8 status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ + UINT8 status_change_ZERO_bit; /* 1 if silence lasts for more than 290 miliseconds on ADPCM recording */ + + /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, + ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) + */ + UINT8 PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ + + UINT8 reg[16]; /* adpcm registers */ + UINT8 emulation_mode; /* which chip we're emulating */ +}YM_DELTAT; + +/*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/ + +UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT); +void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v); +void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode); +void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT); + +/*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs); +//void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT); +void YM_DELTAT_savestate(YM_DELTAT *DELTAT);*/ + +void YM_DELTAT_calc_mem_mask(YM_DELTAT* DELTAT); \ No newline at end of file diff --git a/Frameworks/GME/vgmplay/chips/ymf262.c b/Frameworks/GME/vgmplay/chips/ymf262.c new file mode 100644 index 000000000..9aebcfbae --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ymf262.c @@ -0,0 +1,2747 @@ +/* +** +** File: ymf262.c - software implementation of YMF262 +** FM sound generator type OPL3 +** +** Copyright Jarek Burczynski +** +** Version 0.2 +** + +Revision History: + +03-03-2003: initial release + - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips + - thanks to Stiletto for the datasheets + + Features as listed in 4MF262A6 data sheet: + 1. Registers are compatible with YM3812 (OPL2) FM sound source. + 2. Up to six sounds can be used as four-operator melody sounds for variety. + 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators). + 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody + sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators). + 5. 8 selectable waveforms. + 6. 4-channel sound output. + 7. YMF262 compabile DAC (YAC512) is available. + 8. LFO for vibrato and tremolo effedts. + 9. 2 programable timers. + 10. Shorter register access time compared with YM3812. + 11. 5V single supply silicon gate CMOS process. + 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S). + + +differences between OPL2 and OPL3 not documented in Yamaha datahasheets: +- sinus table is a little different: the negative part is off by one... + +- in order to enable selection of four different waveforms on OPL2 + one must set bit 5 in register 0x01(test). + on OPL3 this bit is ignored and 4-waveform select works *always*. + (Don't confuse this with OPL3's 8-waveform select.) + +- Envelope Generator: all 15 x rates take zero time on OPL3 + (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates + take zero time) + +- channel calculations: output of operator 1 is in perfect sync with + output of operator 2 on OPL3; on OPL and OPL2 output of operator 1 + is always delayed by one sample compared to output of operator 2 + + +differences between OPL2 and OPL3 shown in datasheets: +- YMF262 does not support CSM mode + + +*/ + +#include +#include "mamedef.h" +#include +#include +//#include "sndintrf.h" +#include "ymf262.h" + +#define NULL ((void *)0) + + +/* output final shift */ +#if (OPL3_SAMPLE_BITS==16) + #define FINAL_SH (0) + #define MAXOUT (+32767) + #define MINOUT (-32768) +#else + #define FINAL_SH (8) + #define MAXOUT (+127) + #define MINOUT (-128) +#endif + + +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = a; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = b; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + +//#define LOG_CYM_FILE 0 +//static FILE * cymfile = NULL; + + + + + +#define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */ + + +typedef struct{ + UINT32 ar; /* attack rate: AR<<2 */ + UINT32 dr; /* decay rate: DR<<2 */ + UINT32 rr; /* release rate:RR<<2 */ + UINT8 KSR; /* key scale rate */ + UINT8 ksl; /* keyscale level */ + UINT8 ksr; /* key scale rate: kcode>>KSR */ + UINT8 mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + UINT32 Cnt; /* frequency counter */ + UINT32 Incr; /* frequency counter step */ + UINT8 FB; /* feedback shift value */ + INT32 *connect; /* slot output pointer */ + INT32 op1_out[2]; /* slot1 output for feedback */ + UINT8 CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + UINT8 eg_type; /* percussive/non-percussive mode */ + UINT8 state; /* phase type */ + UINT32 TL; /* total level: TL << 2 */ + INT32 TLL; /* adjusted now TL */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level: sl_tab[SL] */ + + UINT32 eg_m_ar; /* (attack state) */ + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT32 eg_m_dr; /* (decay state) */ + UINT8 eg_sh_dr; /* (decay state) */ + UINT8 eg_sel_dr; /* (decay state) */ + UINT32 eg_m_rr; /* (release state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ + UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + UINT8 waveform_number; + unsigned int wavetable; + +//unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2 +unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2 + +} OPL3_SLOT; + +typedef struct{ + OPL3_SLOT SLOT[2]; + + UINT32 block_fnum; /* block+fnum */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 kcode; /* key code (for key scaling) */ + + /* + there are 12 2-operator channels which can be combined in pairs + to form six 4-operator channel, they are: + 0 and 3, + 1 and 4, + 2 and 5, + 9 and 12, + 10 and 13, + 11 and 14 + */ + UINT8 extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */ + UINT8 Muted; + +unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2 + +} OPL3_CH; + +/* OPL3 state */ +typedef struct { + OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */ + + UINT32 pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */ + UINT32 pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */ + UINT8 MuteSpc[5]; /* for the 5 Rhythm Channels */ + + signed int chanout[18]; /* 18 channels */ + signed int phase_modulation; /* phase modulation input (SLOT 2) */ + signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ + + UINT32 fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + UINT32 LFO_AM; + INT32 LFO_PM; + UINT8 lfo_am_depth; + UINT8 lfo_pm_depth_range; + UINT32 lfo_am_cnt; + UINT32 lfo_am_inc; + UINT32 lfo_pm_cnt; + UINT32 lfo_pm_inc; + + UINT32 noise_rng; /* 23 bit noise shift register */ + UINT32 noise_p; /* current noise 'phase' */ + UINT32 noise_f; /* current noise period */ + + UINT8 OPL3_mode; /* OPL3 extension enable flag */ + + UINT8 rhythm; /* Rhythm mode */ + + int T[2]; /* timer counters */ + UINT8 st[2]; /* timer enable */ + + UINT32 address; /* address register */ + UINT8 status; /* status flag */ + UINT8 statusmask; /* status mask */ + + UINT8 nts; /* NTS (note select) */ + + /* external event callback handlers */ + OPL3_TIMERHANDLER timer_handler;/* TIMER handler */ + void *TimerParam; /* TIMER parameter */ + OPL3_IRQHANDLER IRQHandler; /* IRQ handler */ + void *IRQParam; /* IRQ parameter */ + OPL3_UPDATEHANDLER UpdateHandler;/* stream update handler */ + void *UpdateParam; /* stream update parameter */ + + UINT8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + //attotime TimerBase; /* Timer base time (==sampling time)*/ +} OPL3; + + + +/* mapping of register number (offset) to slot number used by the emulator */ +static const int slot_array[32]= +{ + 0, 2, 4, 1, 3, 5,-1,-1, + 6, 8,10, 7, 9,11,-1,-1, + 12,14,16,13,15,17,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1 +}; + +/* key scale level */ +/* table is 3dB/octave , DV converts this into 6dB/octave */ +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) +static const UINT32 ksl_tab[8*16]= +{ + /* OCT 0 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + /* OCT 1 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV, + 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV, + /* OCT 2 */ + 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV, + 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV, + 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV, + 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV, + /* OCT 3 */ + 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV, + 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV, + 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV, + 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV, + /* OCT 4 */ + 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV, + 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV, + 9.000/DV, 9.750/DV,10.125/DV,10.500/DV, + 10.875/DV,11.250/DV,11.625/DV,12.000/DV, + /* OCT 5 */ + 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV, + 9.000/DV,10.125/DV,10.875/DV,11.625/DV, + 12.000/DV,12.750/DV,13.125/DV,13.500/DV, + 13.875/DV,14.250/DV,14.625/DV,15.000/DV, + /* OCT 6 */ + 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV, + 12.000/DV,13.125/DV,13.875/DV,14.625/DV, + 15.000/DV,15.750/DV,16.125/DV,16.500/DV, + 16.875/DV,17.250/DV,17.625/DV,18.000/DV, + /* OCT 7 */ + 0.000/DV, 9.000/DV,12.000/DV,13.875/DV, + 15.000/DV,16.125/DV,16.875/DV,17.625/DV, + 18.000/DV,18.750/DV,19.125/DV,19.500/DV, + 19.875/DV,20.250/DV,20.625/DV,21.000/DV +}; +#undef DV + +/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */ +static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 }; + + +/* sustain level table (3dB per step) */ +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) ) +static const UINT32 sl_tab[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const unsigned char eg_inc[15*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */ +/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/* note that there is no O(13) in this table - it's directly in the code */ +static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), +O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), + +/* rates 00-12 */ +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 13 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 14 */ +O( 8),O( 9),O(10),O(11), + +/* rate 15 */ +O(12),O(12),O(12),O(12), + +/* 16 dummy rates (same as 15 3) */ +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), +O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12), + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ +/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */ +/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +/* 16 infinite time rates */ +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), + +/* rates 00-12 */ +O(12),O(12),O(12),O(12), +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 16 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), + +}; +#undef O + + +/* multiple table */ +#define ML 2 +static const UINT8 mul_tab[16]= { +/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */ + 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML, + 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML +}; +#undef ML + +/* TL_TAB_LEN is calculated as: + +* (12+1)=13 - sinus amplitude bits (Y axis) +* additional 1: to compensate for calculations of negative part of waveform +* (if we don't add it then the greatest possible _negative_ value would be -2 +* and we really need -1 for waveform #7) +* 2 - sinus sign bit (Y axis) +* TL_RES_LEN - sinus resolution (X axis) +*/ +#define TL_TAB_LEN (13*2*TL_RES_LEN) +static signed int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +/* sin waveform table in 'decibel' scale */ +/* there are eight waveforms on OPL3 chips */ +static unsigned int sin_tab[SIN_LEN * 8]; + + +/* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples + + Length: 210 elements. + + Each of the elements has to be repeated + exactly 64 times (on 64 consecutive samples). + The whole table takes: 64 * 210 = 13440 samples. + + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (losing precision is important) +*/ + +#define LFO_AM_TAB_ELEMENTS 210 + +static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = { +0,0,0,0,0,0,0, +1,1,1,1, +2,2,2,2, +3,3,3,3, +4,4,4,4, +5,5,5,5, +6,6,6,6, +7,7,7,7, +8,8,8,8, +9,9,9,9, +10,10,10,10, +11,11,11,11, +12,12,12,12, +13,13,13,13, +14,14,14,14, +15,15,15,15, +16,16,16,16, +17,17,17,17, +18,18,18,18, +19,19,19,19, +20,20,20,20, +21,21,21,21, +22,22,22,22, +23,23,23,23, +24,24,24,24, +25,25,25,25, +26,26,26, +25,25,25,25, +24,24,24,24, +23,23,23,23, +22,22,22,22, +21,21,21,21, +20,20,20,20, +19,19,19,19, +18,18,18,18, +17,17,17,17, +16,16,16,16, +15,15,15,15, +14,14,14,14, +13,13,13,13, +12,12,12,12, +11,11,11,11, +10,10,10,10, +9,9,9,9, +8,8,8,8, +7,7,7,7, +6,6,6,6, +5,5,5,5, +4,4,4,4, +3,3,3,3, +2,2,2,2, +1,1,1,1 +}; + +/* LFO Phase Modulation table (verified on real YM3812) */ +static const INT8 lfo_pm_table[8*8*2] = { + +/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */ +0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */ +1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */ +2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/ +5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/ + +/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */ +3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/ +7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/ +}; + + +/* lock level of common table */ +static int num_lock = 0; + +/* work table */ +#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1]) +#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2]) +#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1]) +#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2]) + + + +/*INLINE int limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +}*/ + + +/* status set and IRQ handling */ +INLINE void OPL3_STATUS_SET(OPL3 *chip,int flag) +{ + /* set status flag masking out disabled IRQs */ + chip->status |= (flag & chip->statusmask); + if(!(chip->status & 0x80)) + { + if(chip->status & 0x7f) + { /* IRQ on */ + chip->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +INLINE void OPL3_STATUS_RESET(OPL3 *chip,int flag) +{ + /* reset status flag */ + chip->status &= ~flag; + if(chip->status & 0x80) + { + if (!(chip->status & 0x7f)) + { + chip->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0); + } + } +} + +/* IRQ mask set */ +INLINE void OPL3_STATUSMASK_SET(OPL3 *chip,int flag) +{ + chip->statusmask = flag; + /* IRQ handling check */ + OPL3_STATUS_SET(chip,0); + OPL3_STATUS_RESET(chip,0); +} + + +/* advance LFO to next sample */ +INLINE void advance_lfo(OPL3 *chip) +{ + UINT8 tmp; + + /* LFO */ + chip->lfo_am_cnt += chip->lfo_am_inc; + if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ]; + + if (chip->lfo_am_depth) + chip->LFO_AM = tmp; + else + chip->LFO_AM = tmp>>2; + + chip->lfo_pm_cnt += chip->lfo_pm_inc; + chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range; +} + +/* advance to next sample */ +INLINE void advance(OPL3 *chip) +{ + OPL3_CH *CH; + OPL3_SLOT *op; + int i; + + chip->eg_timer += chip->eg_timer_add; + + while (chip->eg_timer >= chip->eg_timer_overflow) + { + chip->eg_timer -= chip->eg_timer_overflow; + + chip->eg_cnt++; + + for (i=0; i<9*2*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; +#if 1 + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_ar) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + break; + + case EG_DEC: /* decay phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_dr) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ +// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_rr) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ +// if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) ) + if ( !(chip->eg_cnt & op->eg_m_rr) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } +#endif + } + } + + for (i=0; i<9*2*2; i++) + { + CH = &chip->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Phase Generator */ + if(op->vib) + { + UINT8 block; + unsigned int block_fnum = CH->block_fnum; + + unsigned int fnum_lfo = (block_fnum&0x0380) >> 7; + + signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ]; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + } + else /* LFO phase modulation = zero */ + { + op->Cnt += op->Incr; + } + } + else /* LFO phase modulation disabled for this operator */ + { + op->Cnt += op->Incr; + } + } + + /* The Noise Generator of the YM3812 is 23-bit shift register. + * Period is equal to 2^23-2 samples. + * Register works at sampling frequency of the chip, so output + * can change on every sample. + * + * Output of the register and input to the bit 22 is: + * bit0 XOR bit14 XOR bit15 XOR bit22 + * + * Simply use bit 22 as the noise output. + */ + + chip->noise_p += chip->noise_f; + i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */ + chip->noise_p &= FREQ_MASK; + while (i) + { + /* + UINT32 j; + j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1; + chip->noise_rng = (j<<22) | (chip->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302; + chip->noise_rng >>= 1; + + i--; + } +} + + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) +{ + UINT32 p; + + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + + +#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask)) + +/* calculate output of a standard 2 operator channel + (or 1st part of a 4-op channel) */ +INLINE void chan_calc( OPL3 *chip, OPL3_CH *CH ) +{ + OPL3_SLOT *SLOT; + unsigned int env; + signed int out; + + if (CH->Muted) + return; + + chip->phase_modulation = 0; + chip->phase_modulation2= 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + *SLOT->connect += SLOT->op1_out[1]; +//logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env ); + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); + +//logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env ); + +} + +/* calculate output of a 2nd part of 4-op channel */ +INLINE void chan_calc_ext( OPL3 *chip, OPL3_CH *CH ) +{ + OPL3_SLOT *SLOT; + unsigned int env; + + if (CH->Muted) + return; + + chip->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable ); + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET ) + *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable); + +} + +/* + operators used in the rhythm sounds generation process: + + Envelope Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal + 6 / 0 12 50 70 90 f0 + + 6 / 1 15 53 73 93 f3 + + 7 / 0 13 51 71 91 f1 + + 7 / 1 16 54 74 94 f4 + + 8 / 0 14 52 72 92 f2 + + 8 / 1 17 55 75 95 f5 + + + Phase Generator: + +channel operator register number Bass High Snare Tom Top +/ slot number MULTIPLE Drum Hat Drum Tom Cymbal + 6 / 0 12 30 + + 6 / 1 15 33 + + 7 / 0 13 31 + + + + 7 / 1 16 34 ----- n o t u s e d ----- + 8 / 0 14 32 + + 8 / 1 17 35 + + + +channel operator register number Bass High Snare Tom Top +number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal + 6 12,15 B6 A6 + + + 7 13,16 B7 A7 + + + + + 8 14,17 B8 A8 + + + + +*/ + +/* calculate rhythm */ + +INLINE void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise ) +{ + OPL3_SLOT *SLOT; + signed int *chanout = chip->chanout; + signed int out; + unsigned int env; + + + /* Bass Drum (verified on real YM3812): + - depends on the channel 6 'connect' register: + when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out) + when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored + - output sample always is multiplied by 2 + */ + + chip->phase_modulation = 0; + + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + chip->phase_modulation = SLOT->op1_out[0]; + //else ignore output of operator 1 + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } + + /* SLOT 2 */ + SLOT++; + env = volume_calc(SLOT); + if( env < ENV_QUIET && ! chip->MuteSpc[0] ) + chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2; + + + /* Phase generation is based on: */ + // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) + // SD (16) channel 7->slot 1 + // TOM (14) channel 8->slot 1 + // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) + + /* Envelope generation based on: */ + // HH channel 7->slot1 + // SD channel 7->slot2 + // TOM channel 8->slot1 + // TOP channel 8->slot2 + + + /* The following formulas can be well optimized. + I leave them in direct form for now (in case I've missed something). + */ + + /* High Hat (verified on real YM3812) */ + env = volume_calc(SLOT7_1); + if( env < ENV_QUIET && ! chip->MuteSpc[4] ) + { + + /* high hat phase generation: + phase = d0 or 234 (based on frequency only) + phase = 34 or 2d0 (based on noise) + */ + + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0xd0; */ + /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ + UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ + if (res2) + phase = (0x200|(0xd0>>2)); + + + /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */ + /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */ + if (phase&0x200) + { + if (noise) + phase = 0x200|0xd0; + } + else + /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */ + /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */ + { + if (noise) + phase = 0xd0>>2; + } + + chanout[7] += op_calc(phase<wavetable) * 2; + } + + /* Snare Drum (verified on real YM3812) */ + env = volume_calc(SLOT7_2); + if( env < ENV_QUIET && ! chip->MuteSpc[1] ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; + + /* when bit8 = 0 phase = 0x100; */ + /* when bit8 = 1 phase = 0x200; */ + UINT32 phase = bit8 ? 0x200 : 0x100; + + /* Noise bit XOR'es phase by 0x100 */ + /* when noisebit = 0 pass the phase from calculation above */ + /* when noisebit = 1 phase ^= 0x100; */ + /* in other words: phase ^= (noisebit<<8); */ + if (noise) + phase ^= 0x100; + + chanout[7] += op_calc(phase<wavetable) * 2; + } + + /* Tom Tom (verified on real YM3812) */ + env = volume_calc(SLOT8_1); + if( env < ENV_QUIET && ! chip->MuteSpc[2] ) + chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; + + /* Top Cymbal (verified on real YM3812) */ + env = volume_calc(SLOT8_2); + if( env < ENV_QUIET && ! chip->MuteSpc[3] ) + { + /* base frequency derived from operator 1 in channel 7 */ + unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + + unsigned char res1 = (bit2 ^ bit7) | bit3; + + /* when res1 = 0 phase = 0x000 | 0x100; */ + /* when res1 = 1 phase = 0x200 | 0x100; */ + UINT32 phase = res1 ? 0x300 : 0x100; + + /* enable gate based on frequency of operator 2 in channel 8 */ + unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + + unsigned char res2 = (bit3e ^ bit5e); + /* when res2 = 0 pass the phase from calculation above (res1); */ + /* when res2 = 1 phase = 0x200 | 0x100; */ + if (res2) + phase = 0x300; + + chanout[8] += op_calc(phase<wavetable) * 2; + } + +} + + +/* generic table initialize */ +static int init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 1; /* 12 bits here (as in real chip) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */ + + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */ + } + #if 0 + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */ + logerror("\n"); + + logerror("tl %04i", x*2); + for (i=0; i<13; i++) + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */ + logerror("\n"); + #endif + } + + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + + /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/ + } + + for (i=0; i>1) ]; + + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ + /* abs(output only first quarter of the sinus waveform) */ + + if (i & (1<<(SIN_BITS-2)) ) + sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)]; + + /* waveform 4: */ + /* /\ ____/\ ____*/ + /* \/ \/ */ + /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[4*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[4*SIN_LEN+i] = sin_tab[i*2]; + + /* waveform 5: */ + /* /\/\____/\/\____*/ + /* */ + /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[5*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ]; + + /* waveform 6: ____ ____ */ + /* */ + /* ____ ____*/ + /* output maximum in half the cycle and output minimum on the other half of cycle */ + + if (i & (1<<(SIN_BITS-1)) ) + sin_tab[6*SIN_LEN+i] = 1; /* negative */ + else + sin_tab[6*SIN_LEN+i] = 0; /* positive */ + + /* waveform 7: */ + /* |\____ |\____ */ + /* \| \|*/ + /* output sawtooth waveform */ + + if (i & (1<<(SIN_BITS-1)) ) + x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */ + else + x = i*16; /*positive: from 0 to 8176 */ + + if (x > TL_TAB_LEN) + x = TL_TAB_LEN; /* clip to the allowed range */ + + sin_tab[7*SIN_LEN+i] = x; + + //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] ); + //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] ); + //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] ); + //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] ); + //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] ); + //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] ); + //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] ); + } + /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/ + +#ifdef SAVE_SAMPLE + sample[0]=fopen("sampsum.pcm","wb"); +#endif + + return 1; +} + +static void OPLCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif +} + + + +static void OPL3_initalize(OPL3 *chip) +{ + int i; + + /* frequency base */ + chip->freqbase = (chip->rate) ? ((double)chip->clock / (8.0*36)) / chip->rate : 0; +#if 0 + chip->rate = (double)chip->clock / (8.0*36); + chip->freqbase = 1.0; +#endif + + /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */ + + /* Timer base time */ + //chip->TimerBase = attotime_mul(ATTOTIME_IN_HZ(chip->clock), 8*36); + + /* make fnumber -> increment counter table */ + for( i=0 ; i < 1024 ; i++ ) + { + /* opn phase increment counter = 20bit */ + chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ +#if 0 + logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n", + i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 ); +#endif + } + +#if 0 + for( i=0 ; i < 16 ; i++ ) + { + logerror("YMF262.C: sl_tab[%i] = %08x\n", + i, sl_tab[i] ); + } + for( i=0 ; i < 8 ; i++ ) + { + int j; + logerror("YMF262.C: ksl_tab[oct=%2i] =",i); + for (j=0; j<16; j++) + { + logerror("%08x ", ksl_tab[i*16+j] ); + } + logerror("\n"); + } +#endif + + + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ + chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; + + /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ + chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase; + + /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/ + + /* Noise generator: a step takes 1 sample */ + chip->noise_f = (1.0 / 1.0) * (1<freqbase; + + chip->eg_timer_add = (1<freqbase; + chip->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, chip->eg_timer_overflow);*/ + +} + +INLINE void FM_KEYON(OPL3_SLOT *SLOT, UINT32 key_set) +{ + if( !SLOT->key ) + { + /* restart Phase Generator */ + SLOT->Cnt = 0; + /* phase -> Attack */ + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +INLINE void FM_KEYOFF(OPL3_SLOT *SLOT, UINT32 key_clr) +{ + if( SLOT->key ) + { + SLOT->key &= key_clr; + + if( !SLOT->key ) + { + /* phase -> Release */ + if (SLOT->state>EG_REL) + SLOT->state = EG_REL; + } + } +} + +/* update phase increment counter of operator (also update the EG rates if necessary) */ +INLINE void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT) +{ + int ksr; + + /* (frequency) phase increment counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 16+60) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; + } +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = mul_tab[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_type = (v&0x20); + SLOT->vib = (v&0x40); + SLOT->AMmask = (v&0x80) ? ~0 : 0; + + if (chip->OPL3_mode & 1) + { + int chan_no = slot/2; + + /* in OPL3 mode */ + //DO THIS: + //if this is one of the slots of 1st channel forming up a 4-op channel + //do normal operation + //else normal 2 operator function + //OR THIS: + //if this is one of the slots of 2nd channel forming up a 4-op channel + //update it using channel data of 1st channel of a pair + //else normal 2 operator function + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + else + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + break; + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + /* update this SLOT using frequency data for 1st channel of a pair */ + CALC_FCSLOT(CH-3,SLOT); + } + else + { + /* normal */ + CALC_FCSLOT(CH,SLOT); + } + break; + default: + /* normal */ + CALC_FCSLOT(CH,SLOT); + break; + } + } + else + { + /* in OPL2 mode */ + CALC_FCSLOT(CH,SLOT); + } +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ksl = ksl_shift[v >> 6]; + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ + + if (chip->OPL3_mode & 1) + { + int chan_no = slot/2; + + /* in OPL3 mode */ + //DO THIS: + //if this is one of the slots of 1st channel forming up a 4-op channel + //do normal operation + //else normal 2 operator function + //OR THIS: + //if this is one of the slots of 2nd channel forming up a 4-op channel + //update it using channel data of 1st channel of a pair + //else normal 2 operator function + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + else + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + break; + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + /* update this SLOT using frequency data for 1st channel of a pair */ + SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl); + } + else + { + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + break; + default: + /* normal */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + break; + } + } + else + { + /* in OPL2 mode */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } + +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + + if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */ + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(OPL3 *chip,int slot,int v) +{ + OPL3_CH *CH = &chip->P_CH[slot/2]; + OPL3_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->sl = sl_tab[ v>>4 ]; + + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; +} + + +static void update_channels(OPL3 *chip, OPL3_CH *CH) +{ + /* update channel passed as a parameter and a channel at CH+=3; */ + if (CH->extended) + { /* we've just switched to combined 4 operator mode */ + + } + else + { /* we've just switched to normal 2 operator mode */ + + } + +} + +/* write a value v to register r on OPL chip */ +static void OPL3WriteReg(OPL3 *chip, int r, int v) +{ + OPL3_CH *CH; + signed int *chanout = chip->chanout; + unsigned int ch_offset = 0; + int slot; + int block_fnum; + + + + /*if (LOG_CYM_FILE && (cymfile) && ((r&255)!=0) && (r!=255) ) + { + if (r>0xff) + fputc( (unsigned char)0xff, cymfile );//mark writes to second register set + + fputc( (unsigned char)r&0xff, cymfile ); + fputc( (unsigned char)v, cymfile ); + }*/ + + if(r&0x100) + { + switch(r) + { + case 0x101: /* test register */ + return; + + case 0x104: /* 6 channels enable */ + { + UINT8 prev; + + CH = &chip->P_CH[0]; /* channel 0 */ + prev = CH->extended; + CH->extended = (v>>0) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 1 */ + prev = CH->extended; + CH->extended = (v>>1) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 2 */ + prev = CH->extended; + CH->extended = (v>>2) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + + + CH = &chip->P_CH[9]; /* channel 9 */ + prev = CH->extended; + CH->extended = (v>>3) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 10 */ + prev = CH->extended; + CH->extended = (v>>4) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + CH++; /* channel 11 */ + prev = CH->extended; + CH->extended = (v>>5) & 1; + if(prev != CH->extended) + update_channels(chip, CH); + + } + return; + + case 0x105: /* OPL3 extensions enable register */ + + chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */ + + /* following behaviour was tested on real YMF262, + switching OPL3/OPL2 modes on the fly: + - does not change the waveform previously selected (unless when ....) + - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....) + - does not disable channels 9-17 on OPL3->OPL2 switch + - does not switch 4 operator channels back to 2 operator channels + */ + + return; + + default: +#ifdef _DEBUG + if (r < 0x120) + logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v); +#endif + break; + } + + ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */ + } + + /* adjust bus to 8 bits */ + r &= 0xff; + v &= 0xff; + + + switch(r&0xe0) + { + case 0x00: /* 00-1f:control */ + switch(r&0x1f) + { + case 0x01: /* test register */ + break; + case 0x02: /* Timer 1 */ + chip->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + chip->T[1] = (256-v)*16; + break; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flags clear */ + OPL3_STATUS_RESET(chip,0x60); + } + else + { /* set IRQ mask ,timer enable */ + UINT8 st1 = v & 1; + UINT8 st2 = (v>>1) & 1; + + /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */ + OPL3_STATUS_RESET(chip, v & 0x60); + OPL3_STATUSMASK_SET(chip, (~v) & 0x60 ); + + /* timer 2 */ + if(chip->st[1] != st2) + { + //attotime period = st2 ? attotime_mul(chip->TimerBase, chip->T[1]) : attotime_zero; + chip->st[1] = st2; + //if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period); + } + /* timer 1 */ + if(chip->st[0] != st1) + { + //attotime period = st1 ? attotime_mul(chip->TimerBase, chip->T[0]) : attotime_zero; + chip->st[0] = st1; + //if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period); + } + } + break; + case 0x08: /* x,NTS,x,x, x,x,x,x */ + chip->nts = v; + break; + + default: +#ifdef _DEBUG + logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v); +#endif + break; + } + break; + case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(chip, slot + ch_offset*2, v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(chip, slot + ch_offset*2, v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(chip, slot + ch_offset*2, v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(chip, slot + ch_offset*2, v); + break; + case 0xa0: + if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */ + { + if (ch_offset != 0) /* 0xbd register is present in set #1 only */ + return; + + chip->lfo_am_depth = v & 0x80; + chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0; + + chip->rhythm = v&0x3f; + + if(chip->rhythm&0x20) + { + /* BD key on/off */ + if(v&0x10) + { + FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2); + FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2); + } + else + { + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); + } + /* HH key on/off */ + if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2); + else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key on/off */ + if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2); + else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key on/off */ + if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2); + else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY key on/off */ + if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2); + else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + else + { + /* BD key off */ + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2); + FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2); + /* HH key off */ + FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2); + /* SD key off */ + FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2); + /* TOM key off */ + FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2); + /* TOP-CY off */ + FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2); + } + return; + } + + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &chip->P_CH[(r&0x0f) + ch_offset]; + + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + + if (chip->OPL3_mode & 1) + { + int chan_no = (r&0x0f) + ch_offset; + + /* in OPL3 mode */ + //DO THIS: + //if this is 1st channel forming up a 4-op channel + //ALSO keyon/off slots of 2nd channel forming up 4-op channel + //else normal 2 operator function keyon/off + //OR THIS: + //if this is 2nd channel forming up 4-op channel just do nothing + //else normal 2 operator function keyon/off + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + //if this is 1st channel forming up a 4-op channel + //ALSO keyon/off slots of 2nd channel forming up 4-op channel + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + FM_KEYON (&(CH+3)->SLOT[SLOT1], 1); + FM_KEYON (&(CH+3)->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1); + FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1); + } + } + else + { + //else normal 2 operator function keyon/off + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + //if this is 2nd channel forming up 4-op channel just do nothing + } + else + { + //else normal 2 operator function keyon/off + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + break; + + default: + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + break; + } + } + else + { + if(v&0x20) + { + FM_KEYON (&CH->SLOT[SLOT1], 1); + FM_KEYON (&CH->SLOT[SLOT2], 1); + } + else + { + FM_KEYOFF(&CH->SLOT[SLOT1],~1); + FM_KEYOFF(&CH->SLOT[SLOT2],~1); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + UINT8 block = block_fnum >> 10; + + CH->block_fnum = block_fnum; + + CH->ksl_base = ksl_tab[block_fnum>>6]; + CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block); + + /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */ + CH->kcode = (CH->block_fnum&0x1c00)>>9; + + /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */ + /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ + /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ + if (chip->nts&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + else + CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ + + if (chip->OPL3_mode & 1) + { + int chan_no = (r&0x0f) + ch_offset; + /* in OPL3 mode */ + //DO THIS: + //if this is 1st channel forming up a 4-op channel + //ALSO update slots of 2nd channel forming up 4-op channel + //else normal 2 operator function keyon/off + //OR THIS: + //if this is 2nd channel forming up 4-op channel just do nothing + //else normal 2 operator function keyon/off + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + //if this is 1st channel forming up a 4-op channel + //ALSO update slots of 2nd channel forming up 4-op channel + + /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl); + (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl); + + /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]); + } + else + { + //else normal 2 operator function + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + //if this is 2nd channel forming up 4-op channel just do nothing + } + else + { + //else normal 2 operator function + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + break; + + default: + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + break; + } + } + else + { + /* in OPL2 mode */ + + /* refresh Total Level in both SLOTs of this channel */ + CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl); + CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl); + + /* refresh frequency counter in both SLOTs of this channel */ + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + } + break; + + case 0xc0: + /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */ + if( (r&0xf) > 8) return; + + CH = &chip->P_CH[(r&0xf) + ch_offset]; + + if( chip->OPL3_mode & 1 ) + { + int base = ((r&0xf) + ch_offset) * 4; + + /* OPL3 mode */ + chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */ + chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */ + chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */ + chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */ + } + else + { + int base = ((r&0xf) + ch_offset) * 4; + + /* OPL2 mode - always enabled */ + chip->pan[ base ] = ~0; /* ch.A */ + chip->pan[ base +1 ] = ~0; /* ch.B */ + chip->pan[ base +2 ] = ~0; /* ch.C */ + chip->pan[ base +3 ] = ~0; /* ch.D */ + } + + chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */ + + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + + if( chip->OPL3_mode & 1 ) + { + int chan_no = (r&0x0f) + ch_offset; + + switch(chan_no) + { + case 0: case 1: case 2: + case 9: case 10: case 11: + if (CH->extended) + { + UINT8 conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0); + switch(conn) + { + case 0: + /* 1 -> 2 -> 3 -> 4 - out */ + + CH->SLOT[SLOT1].connect = &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chip->phase_modulation2; + (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation; + (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; + break; + case 1: + /* 1 -> 2 -\ + 3 -> 4 -+- out */ + + CH->SLOT[SLOT1].connect = &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; + (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation; + (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; + break; + case 2: + /* 1 -----------\ + 2 -> 3 -> 4 -+- out */ + + CH->SLOT[SLOT1].connect = &chanout[ chan_no ]; + CH->SLOT[SLOT2].connect = &chip->phase_modulation2; + (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation; + (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; + break; + case 3: + /* 1 ------\ + 2 -> 3 -+- out + 4 ------/ */ + CH->SLOT[SLOT1].connect = &chanout[ chan_no ]; + CH->SLOT[SLOT2].connect = &chip->phase_modulation2; + (CH+3)->SLOT[SLOT1].connect = &chanout[ chan_no + 3 ]; + (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ]; + break; + } + } + else + { + /* 2 operators mode */ + CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; + } + break; + + case 3: case 4: case 5: + case 12: case 13: case 14: + if ((CH-3)->extended) + { + UINT8 conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0); + switch(conn) + { + case 0: + /* 1 -> 2 -> 3 -> 4 - out */ + + (CH-3)->SLOT[SLOT1].connect = &chip->phase_modulation; + (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2; + CH->SLOT[SLOT1].connect = &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; + break; + case 1: + /* 1 -> 2 -\ + 3 -> 4 -+- out */ + + (CH-3)->SLOT[SLOT1].connect = &chip->phase_modulation; + (CH-3)->SLOT[SLOT2].connect = &chanout[ chan_no - 3 ]; + CH->SLOT[SLOT1].connect = &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; + break; + case 2: + /* 1 -----------\ + 2 -> 3 -> 4 -+- out */ + + (CH-3)->SLOT[SLOT1].connect = &chanout[ chan_no - 3 ]; + (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2; + CH->SLOT[SLOT1].connect = &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; + break; + case 3: + /* 1 ------\ + 2 -> 3 -+- out + 4 ------/ */ + (CH-3)->SLOT[SLOT1].connect = &chanout[ chan_no - 3 ]; + (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2; + CH->SLOT[SLOT1].connect = &chanout[ chan_no ]; + CH->SLOT[SLOT2].connect = &chanout[ chan_no ]; + break; + } + } + else + { + /* 2 operators mode */ + CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; + } + break; + + default: + /* 2 operators mode */ + CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; + break; + } + } + else + { + /* OPL2 mode - always 2 operators mode */ + CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation; + CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset]; + } + break; + + case 0xe0: /* waveform select */ + slot = slot_array[r&0x1f]; + if(slot < 0) return; + + slot += ch_offset*2; + + CH = &chip->P_CH[slot/2]; + + + /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */ + v &= 7; + CH->SLOT[slot&1].waveform_number = v; + + /* ... but select only waveforms 0-3 in OPL2 mode */ + if( !(chip->OPL3_mode & 1) ) + { + v &= 3; /* we're in OPL2 mode */ + } + CH->SLOT[slot&1].wavetable = v * SIN_LEN; + break; + } +} + +/*static TIMER_CALLBACK( cymfile_callback ) +{ + if (cymfile) + { + fputc( (unsigned char)0, cymfile ); + } +}*/ + +/* lock/unlock for common table */ +static int OPL3_LockTable() +{ + num_lock++; + if(num_lock>1) return 0; + + /* first time */ + + if( !init_tables() ) + { + num_lock--; + return -1; + } + + /*if (LOG_CYM_FILE) + { + cymfile = fopen("ymf262_.cym","wb"); + if (cymfile) + timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer + else + logerror("Could not create ymf262_.cym file\n"); + }*/ + + return 0; +} + +static void OPL3_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + + /* last time */ + + OPLCloseTable(); + + /*if (LOG_CYM_FILE) + fclose (cymfile); + cymfile = NULL;*/ +} + +static void OPL3ResetChip(OPL3 *chip) +{ + int c,s; + + chip->eg_timer = 0; + chip->eg_cnt = 0; + + chip->noise_rng = 1; /* noise shift register */ + chip->nts = 0; /* note split */ + OPL3_STATUS_RESET(chip,0x60); + + /* reset with register write */ + OPL3WriteReg(chip,0x01,0); /* test register */ + OPL3WriteReg(chip,0x02,0); /* Timer1 */ + OPL3WriteReg(chip,0x03,0); /* Timer2 */ + OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */ + + +//FIX IT registers 101, 104 and 105 + + +//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) + for(c = 0xff ; c >= 0x20 ; c-- ) + OPL3WriteReg(chip,c,0); +//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) + for(c = 0x1ff ; c >= 0x120 ; c-- ) + OPL3WriteReg(chip,c,0); + + + + /* reset operator parameters */ + for( c = 0 ; c < 9*2 ; c++ ) + { + OPL3_CH *CH = &chip->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +} + +/* Create one of virtual YMF262 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static OPL3 *OPL3Create(int clock, int rate, int type) +{ + OPL3 *chip; + + if (OPL3_LockTable() == -1) return NULL; + + /* allocate memory block */ + chip = (OPL3 *)malloc(sizeof(OPL3)); + + if (chip==NULL) + return NULL; + + /* clear */ + memset(chip, 0, sizeof(OPL3)); + + chip->type = type; + chip->clock = clock; + chip->rate = rate; + + /* init global tables */ + OPL3_initalize(chip); + + /* reset chip */ + OPL3ResetChip(chip); + return chip; +} + +/* Destroy one of virtual YMF262 */ +static void OPL3Destroy(OPL3 *chip) +{ + OPL3_UnLockTable(); + free(chip); +} + + +/* Optional handlers */ + +static void OPL3SetTimerHandler(OPL3 *chip,OPL3_TIMERHANDLER timer_handler,void *param) +{ + chip->timer_handler = timer_handler; + chip->TimerParam = param; +} +static void OPL3SetIRQHandler(OPL3 *chip,OPL3_IRQHANDLER IRQHandler,void *param) +{ + chip->IRQHandler = IRQHandler; + chip->IRQParam = param; +} +static void OPL3SetUpdateHandler(OPL3 *chip,OPL3_UPDATEHANDLER UpdateHandler,void *param) +{ + chip->UpdateHandler = UpdateHandler; + chip->UpdateParam = param; +} + +/* YMF262 I/O interface */ +static int OPL3Write(OPL3 *chip, int a, int v) +{ + /* data bus is 8 bits */ + v &= 0xff; + + switch(a&3) + { + case 0: /* address port 0 (register set #1) */ + chip->address = v; + break; + + case 1: /* data port - ignore A1 */ + case 3: /* data port - ignore A1 */ + if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam/*,0*/); + OPL3WriteReg(chip,chip->address,v); + break; + + case 2: /* address port 1 (register set #2) */ + + /* verified on real YMF262: + in OPL3 mode: + address line A1 is stored during *address* write and ignored during *data* write. + + in OPL2 mode: + register set#2 writes go to register set#1 (ignoring A1) + verified on registers from set#2: 0x01, 0x04, 0x20-0xef + The only exception is register 0x05. + */ + if( chip->OPL3_mode & 1 ) + { + /* OPL3 mode */ + chip->address = v | 0x100; + } + else + { + /* in OPL2 mode the only accessible in set #2 is register 0x05 */ + if( v==5 ) + chip->address = v | 0x100; + else + chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */ + } + break; + } + + return chip->status>>7; +} + +static unsigned char OPL3Read(OPL3 *chip,int a) +{ + if( a==0 ) + { + /* status port */ + return chip->status; + } + + return 0x00; /* verified on real YMF262 */ +} + + + +static int OPL3TimerOver(OPL3 *chip,int c) +{ + if( c ) + { /* Timer B */ + OPL3_STATUS_SET(chip,0x20); + } + else + { /* Timer A */ + OPL3_STATUS_SET(chip,0x40); + } + /* reload timer */ + //if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,attotime_mul(chip->TimerBase, chip->T[c])); + return chip->status>>7; +} + + + + +void * ymf262_init(int clock, int rate) +{ + return OPL3Create(clock,rate,OPL3_TYPE_YMF262); +} + +void ymf262_shutdown(void *chip) +{ + OPL3Destroy((OPL3 *)chip); +} +void ymf262_reset_chip(void *chip) +{ + OPL3ResetChip((OPL3 *)chip); +} + +int ymf262_write(void *chip, int a, int v) +{ + return OPL3Write((OPL3 *)chip, a, v); +} + +unsigned char ymf262_read(void *chip, int a) +{ + /* Note on status register: */ + + /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */ + + /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */ + /* which can be used to identify the chip */ + + /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */ + + return OPL3Read((OPL3 *)chip, a); +} +int ymf262_timer_over(void *chip, int c) +{ + return OPL3TimerOver((OPL3 *)chip, c); +} + +void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, void *param) +{ + OPL3SetTimerHandler((OPL3 *)chip, timer_handler, param); +} +void ymf262_set_irq_handler(void *chip,OPL3_IRQHANDLER IRQHandler,void *param) +{ + OPL3SetIRQHandler((OPL3 *)chip, IRQHandler, param); +} +void ymf262_set_update_handler(void *chip,OPL3_UPDATEHANDLER UpdateHandler,void *param) +{ + OPL3SetUpdateHandler((OPL3 *)chip, UpdateHandler, param); +} + +void ymf262_set_mutemask(void *chip, UINT32 MuteMask) +{ + OPL3 *opl3 = (OPL3 *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 18; CurChn ++) + opl3->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + for (CurChn = 0; CurChn < 5; CurChn ++) + opl3->MuteSpc[CurChn] = (MuteMask >> (CurChn + 18)) & 0x01; + + return; +} + + +/* +** Generate samples for one of the YMF262's +** +** 'which' is the virtual YMF262 number +** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D +** 'length' is the number of samples that should be generated +*/ +void ymf262_update_one(void *_chip, OPL3SAMPLE **buffers, int length) +{ + OPL3 *chip = (OPL3 *)_chip; + UINT8 rhythm = chip->rhythm&0x20; + + OPL3SAMPLE *ch_a = buffers[0]; + OPL3SAMPLE *ch_b = buffers[1]; + //OPL3SAMPLE *ch_c = buffers[2]; + //OPL3SAMPLE *ch_d = buffers[3]; + + int i; + int chn; + + for( i=0; i < length ; i++ ) + { + int a,b,c,d; + + + advance_lfo(chip); + + /* clear channel outputs */ + memset(chip->chanout, 0, sizeof(signed int) * 18); + +#if 1 + /* register set #1 */ + chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */ + if (chip->P_CH[0].extended) + chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */ + else + chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */ + + + chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */ + if (chip->P_CH[1].extended) + chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */ + else + chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */ + + + chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */ + if (chip->P_CH[2].extended) + chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */ + else + chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */ + + + if(!rhythm) + { + chan_calc(chip, &chip->P_CH[6]); + chan_calc(chip, &chip->P_CH[7]); + chan_calc(chip, &chip->P_CH[8]); + } + else /* Rhythm part */ + { + chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 ); + } + + /* register set #2 */ + chan_calc(chip, &chip->P_CH[ 9]); + if (chip->P_CH[9].extended) + chan_calc_ext(chip, &chip->P_CH[12]); + else + chan_calc(chip, &chip->P_CH[12]); + + + chan_calc(chip, &chip->P_CH[10]); + if (chip->P_CH[10].extended) + chan_calc_ext(chip, &chip->P_CH[13]); + else + chan_calc(chip, &chip->P_CH[13]); + + + chan_calc(chip, &chip->P_CH[11]); + if (chip->P_CH[11].extended) + chan_calc_ext(chip, &chip->P_CH[14]); + else + chan_calc(chip, &chip->P_CH[14]); + + + /* channels 15,16,17 are fixed 2-operator channels only */ + chan_calc(chip, &chip->P_CH[15]); + chan_calc(chip, &chip->P_CH[16]); + chan_calc(chip, &chip->P_CH[17]); +#endif + + /* accumulator register set #1 */ + a = chip->chanout[0] & chip->pan[0]; + b = chip->chanout[0] & chip->pan[1]; + c = chip->chanout[0] & chip->pan[2]; + d = chip->chanout[0] & chip->pan[3]; +#if 1 + a += chip->chanout[1] & chip->pan[4]; + b += chip->chanout[1] & chip->pan[5]; + c += chip->chanout[1] & chip->pan[6]; + d += chip->chanout[1] & chip->pan[7]; + a += chip->chanout[2] & chip->pan[8]; + b += chip->chanout[2] & chip->pan[9]; + c += chip->chanout[2] & chip->pan[10]; + d += chip->chanout[2] & chip->pan[11]; + + a += chip->chanout[3] & chip->pan[12]; + b += chip->chanout[3] & chip->pan[13]; + c += chip->chanout[3] & chip->pan[14]; + d += chip->chanout[3] & chip->pan[15]; + a += chip->chanout[4] & chip->pan[16]; + b += chip->chanout[4] & chip->pan[17]; + c += chip->chanout[4] & chip->pan[18]; + d += chip->chanout[4] & chip->pan[19]; + a += chip->chanout[5] & chip->pan[20]; + b += chip->chanout[5] & chip->pan[21]; + c += chip->chanout[5] & chip->pan[22]; + d += chip->chanout[5] & chip->pan[23]; + + a += chip->chanout[6] & chip->pan[24]; + b += chip->chanout[6] & chip->pan[25]; + c += chip->chanout[6] & chip->pan[26]; + d += chip->chanout[6] & chip->pan[27]; + a += chip->chanout[7] & chip->pan[28]; + b += chip->chanout[7] & chip->pan[29]; + c += chip->chanout[7] & chip->pan[30]; + d += chip->chanout[7] & chip->pan[31]; + a += chip->chanout[8] & chip->pan[32]; + b += chip->chanout[8] & chip->pan[33]; + c += chip->chanout[8] & chip->pan[34]; + d += chip->chanout[8] & chip->pan[35]; + + /* accumulator register set #2 */ + a += chip->chanout[9] & chip->pan[36]; + b += chip->chanout[9] & chip->pan[37]; + c += chip->chanout[9] & chip->pan[38]; + d += chip->chanout[9] & chip->pan[39]; + a += chip->chanout[10] & chip->pan[40]; + b += chip->chanout[10] & chip->pan[41]; + c += chip->chanout[10] & chip->pan[42]; + d += chip->chanout[10] & chip->pan[43]; + a += chip->chanout[11] & chip->pan[44]; + b += chip->chanout[11] & chip->pan[45]; + c += chip->chanout[11] & chip->pan[46]; + d += chip->chanout[11] & chip->pan[47]; + + a += chip->chanout[12] & chip->pan[48]; + b += chip->chanout[12] & chip->pan[49]; + c += chip->chanout[12] & chip->pan[50]; + d += chip->chanout[12] & chip->pan[51]; + a += chip->chanout[13] & chip->pan[52]; + b += chip->chanout[13] & chip->pan[53]; + c += chip->chanout[13] & chip->pan[54]; + d += chip->chanout[13] & chip->pan[55]; + a += chip->chanout[14] & chip->pan[56]; + b += chip->chanout[14] & chip->pan[57]; + c += chip->chanout[14] & chip->pan[58]; + d += chip->chanout[14] & chip->pan[59]; + + a += chip->chanout[15] & chip->pan[60]; + b += chip->chanout[15] & chip->pan[61]; + c += chip->chanout[15] & chip->pan[62]; + d += chip->chanout[15] & chip->pan[63]; + a += chip->chanout[16] & chip->pan[64]; + b += chip->chanout[16] & chip->pan[65]; + c += chip->chanout[16] & chip->pan[66]; + d += chip->chanout[16] & chip->pan[67]; + a += chip->chanout[17] & chip->pan[68]; + b += chip->chanout[17] & chip->pan[69]; + c += chip->chanout[17] & chip->pan[70]; + d += chip->chanout[17] & chip->pan[71]; +#endif + a >>= FINAL_SH; + b >>= FINAL_SH; + c >>= FINAL_SH; + d >>= FINAL_SH; + + /* limit check */ + //a = limit( a , MAXOUT, MINOUT ); + //b = limit( b , MAXOUT, MINOUT ); + //c = limit( c , MAXOUT, MINOUT ); + //d = limit( d , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + if (which==0) + { + SAVE_ALL_CHANNELS + } + #endif + + /* store to sound buffer */ + ch_a[i] = a+c; + ch_b[i] = b+d; + //ch_c[i] = c; + //ch_d[i] = d; + + advance(chip); + } + +} + diff --git a/Frameworks/GME/vgmplay/chips/ymf262.h b/Frameworks/GME/vgmplay/chips/ymf262.h new file mode 100644 index 000000000..0af7901e5 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ymf262.h @@ -0,0 +1,49 @@ +#pragma once + +//#include "attotime.h" + +/* select number of output bits: 8 or 16 */ +#define OPL3_SAMPLE_BITS 16 + +/* compiler dependence */ +//#ifndef __OSDCOMM_H__ +//#define __OSDCOMM_H__ +/*typedef unsigned char UINT8; // unsigned 8bit +typedef unsigned short UINT16; // unsigned 16bit +typedef unsigned int UINT32; // unsigned 32bit +typedef signed char INT8; // signed 8bit +typedef signed short INT16; // signed 16bit +typedef signed int INT32; // signed 32bit*/ +//#endif + +typedef stream_sample_t OPL3SAMPLE; +/* +#if (OPL3_SAMPLE_BITS==16) +typedef INT16 OPL3SAMPLE; +#endif +#if (OPL3_SAMPLE_BITS==8) +typedef INT8 OPL3SAMPLE; +#endif +*/ + +//typedef void (*OPL3_TIMERHANDLER)(void *param,int timer,attotime period); +typedef void (*OPL3_TIMERHANDLER)(void *param,int timer,int period); +typedef void (*OPL3_IRQHANDLER)(void *param,int irq); +typedef void (*OPL3_UPDATEHANDLER)(void *param/*,int min_interval_us*/); + + +void *ymf262_init(int clock, int rate); +void ymf262_shutdown(void *chip); +void ymf262_reset_chip(void *chip); +int ymf262_write(void *chip, int a, int v); +unsigned char ymf262_read(void *chip, int a); +int ymf262_timer_over(void *chip, int c); +void ymf262_update_one(void *chip, OPL3SAMPLE **buffers, int length); + +void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, void *param); +void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, void *param); +void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, void *param); + +void ymf262_set_emu_core(UINT8 Emulator); +void ymf262_set_mutemask(void *chip, UINT32 MuteMask); + diff --git a/Frameworks/GME/vgmplay/chips/ymf271.c b/Frameworks/GME/vgmplay/chips/ymf271.c new file mode 100644 index 000000000..e41fb3579 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ymf271.c @@ -0,0 +1,1982 @@ +/* + Yamaha YMF271-F "OPX" emulator v0.1 + By R. Belmont. + Based in part on YMF278B emulator by R. Belmont and O. Galibert. + 12June04 update by Toshiaki Nijiura + Copyright R. Belmont. + + This software is dual-licensed: it may be used in MAME and properly licensed + MAME derivatives under the terms of the MAME license. For use outside of + MAME and properly licensed derivatives, it is available under the + terms of the GNU Lesser General Public License (LGPL), version 2.1. + You may read the LGPL at http://www.gnu.org/licenses/lgpl.html + + TODO: + - A/L bit (alternate loop) + - EN and EXT Out bits + - Src B and Src NOTE bits + - statusreg Busy and End bits + - timer register 0x11 + - ch2/ch3 (4 speakers) + - PFM (FM using external PCM waveform) + - detune (should be same as on other Yamaha chips) + - Acc On bit (some sound effects in viprp1?). The documentation says + "determines if slot output is accumulated(1), or output directly(0)" + - Is memory handling 100% correct? At the moment, seibuspi.c is the only + hardware currently emulated that uses external handlers. +*/ + +#include +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +#ifdef _DEBUG +#include +#endif +#include +#include +#include "ymf271.h" + +#ifndef __cplusplus // C++ already has the bool-type +#define false 0x00 +#define true 0x01 +typedef unsigned char bool; +#endif // !__cplusplus + +#define NULL ((void *)0) + +//#define DEVCB_NULL { DEVCB_TYPE_NULL } +#define DEVCB_NULL DEVCB_TYPE_NULL +#define DEVCB_TYPE_NULL (0) + +#define VERBOSE (1) + +#define STD_CLOCK (16934400) + +#define MAXOUT (+32767) +#define MINOUT (-32768) + +#define SIN_BITS 10 +#define SIN_LEN (1<> 4; return (entry & 0x0f) << 7; + if(ex) } + return (ma | 16) << (ex+6); else + else { + return ma << 7; int shift = 6 + (entry >> 4); +} return (0x10 + (entry & 0x0f)) << shift; + } +lfo_freq = 44100 / lfo_period } + +*/ + +static const double LFO_frequency_table[256] = +{ + 0.00066, 0.00068, 0.00070, 0.00073, 0.00075, 0.00078, 0.00081, 0.00084, + 0.00088, 0.00091, 0.00096, 0.00100, 0.00105, 0.00111, 0.00117, 0.00124, + 0.00131, 0.00136, 0.00140, 0.00145, 0.00150, 0.00156, 0.00162, 0.00168, + 0.00175, 0.00183, 0.00191, 0.00200, 0.00210, 0.00221, 0.00234, 0.00247, + 0.00263, 0.00271, 0.00280, 0.00290, 0.00300, 0.00312, 0.00324, 0.00336, + 0.00350, 0.00366, 0.00382, 0.00401, 0.00421, 0.00443, 0.00467, 0.00495, + 0.00526, 0.00543, 0.00561, 0.00580, 0.00601, 0.00623, 0.00647, 0.00673, + 0.00701, 0.00731, 0.00765, 0.00801, 0.00841, 0.00885, 0.00935, 0.00990, + 0.01051, 0.01085, 0.01122, 0.01160, 0.01202, 0.01246, 0.01294, 0.01346, + 0.01402, 0.01463, 0.01529, 0.01602, 0.01682, 0.01771, 0.01869, 0.01979, + 0.02103, 0.02171, 0.02243, 0.02320, 0.02403, 0.02492, 0.02588, 0.02692, + 0.02804, 0.02926, 0.03059, 0.03204, 0.03365, 0.03542, 0.03738, 0.03958, + 0.04206, 0.04341, 0.04486, 0.04641, 0.04807, 0.04985, 0.05176, 0.05383, + 0.05608, 0.05851, 0.06117, 0.06409, 0.06729, 0.07083, 0.07477, 0.07917, + 0.08411, 0.08683, 0.08972, 0.09282, 0.09613, 0.09969, 0.10353, 0.10767, + 0.11215, 0.11703, 0.12235, 0.12817, 0.13458, 0.14167, 0.14954, 0.15833, + 0.16823, 0.17365, 0.17944, 0.18563, 0.19226, 0.19938, 0.20705, 0.21533, + 0.22430, 0.23406, 0.24470, 0.25635, 0.26917, 0.28333, 0.29907, 0.31666, + 0.33646, 0.34731, 0.35889, 0.37126, 0.38452, 0.39876, 0.41410, 0.43066, + 0.44861, 0.46811, 0.48939, 0.51270, 0.53833, 0.56666, 0.59814, 0.63333, + 0.67291, 0.69462, 0.71777, 0.74252, 0.76904, 0.79753, 0.82820, 0.86133, + 0.89722, 0.93623, 0.97878, 1.02539, 1.07666, 1.13333, 1.19629, 1.26666, + 1.34583, 1.38924, 1.43555, 1.48505, 1.53809, 1.59509, 1.65640, 1.72266, + 1.79443, 1.87245, 1.95756, 2.05078, 2.15332, 2.26665, 2.39258, 2.53332, + 2.69165, 2.77848, 2.87109, 2.97010, 3.07617, 3.19010, 3.31280, 3.44531, + 3.58887, 3.74490, 3.91513, 4.10156, 4.30664, 4.53331, 4.78516, 5.06664, + 5.38330, 5.55696, 5.74219, 5.94019, 6.15234, 6.38021, 6.62560, 6.89062, + 7.17773, 7.48981, 7.83026, 8.20312, 8.61328, 9.06661, 9.57031, 10.13327, + 10.76660, 11.11391, 11.48438, 11.88039, 12.30469, 12.76042, 13.25120, 13.78125, + 14.35547, 14.97962, 15.66051, 16.40625, 17.22656, 18.13322, 19.14062, 20.26654, + 21.53320, 22.96875, 24.60938, 26.50240, 28.71094, 31.32102, 34.45312, 38.28125, + 43.06641, 49.21875, 57.42188, 68.90625, 86.13281, 114.84375, 172.26562, 344.53125 +}; + +static const int RKS_Table[32][8] = +{ + { 0, 0, 0, 0, 0, 2, 4, 8 }, + { 0, 0, 0, 0, 1, 3, 5, 9 }, + { 0, 0, 0, 1, 2, 4, 6, 10 }, + { 0, 0, 0, 1, 3, 5, 7, 11 }, + { 0, 0, 1, 2, 4, 6, 8, 12 }, + { 0, 0, 1, 2, 5, 7, 9, 13 }, + { 0, 0, 1, 3, 6, 8, 10, 14 }, + { 0, 0, 1, 3, 7, 9, 11, 15 }, + { 0, 1, 2, 4, 8, 10, 12, 16 }, + { 0, 1, 2, 4, 9, 11, 13, 17 }, + { 0, 1, 2, 5, 10, 12, 14, 18 }, + { 0, 1, 2, 5, 11, 13, 15, 19 }, + { 0, 1, 3, 6, 12, 14, 16, 20 }, + { 0, 1, 3, 6, 13, 15, 17, 21 }, + { 0, 1, 3, 7, 14, 16, 18, 22 }, + { 0, 1, 3, 7, 15, 17, 19, 23 }, + { 0, 2, 4, 8, 16, 18, 20, 24 }, + { 0, 2, 4, 8, 17, 19, 21, 25 }, + { 0, 2, 4, 9, 18, 20, 22, 26 }, + { 0, 2, 4, 9, 19, 21, 23, 27 }, + { 0, 2, 5, 10, 20, 22, 24, 28 }, + { 0, 2, 5, 10, 21, 23, 25, 29 }, + { 0, 2, 5, 11, 22, 24, 26, 30 }, + { 0, 2, 5, 11, 23, 25, 27, 31 }, + { 0, 3, 6, 12, 24, 26, 28, 31 }, + { 0, 3, 6, 12, 25, 27, 29, 31 }, + { 0, 3, 6, 13, 26, 28, 30, 31 }, + { 0, 3, 6, 13, 27, 29, 31, 31 }, + { 0, 3, 7, 14, 28, 30, 31, 31 }, + { 0, 3, 7, 14, 29, 31, 31, 31 }, + { 0, 3, 7, 15, 30, 31, 31, 31 }, + { 0, 3, 7, 15, 31, 31, 31, 31 }, +}; + +static const double multiple_table[16] = { 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + +static const double pow_table[16] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0.5, 1, 2, 4, 8, 16, 32, 64 }; + +static const double fs_frequency[4] = { 1.0/1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0 }; + +static const double channel_attenuation_table[16] = +{ + 0.0, 2.5, 6.0, 8.5, 12.0, 14.5, 18.1, 20.6, 24.1, 26.6, 30.1, 32.6, 36.1, 96.1, 96.1, 96.1 +}; + +static const int modulation_level[8] = { 16, 8, 4, 2, 1, 32, 64, 128 }; + +// feedback_level * 16 +static const int feedback_level[8] = { 0, 1, 2, 4, 8, 16, 32, 64 }; + +// slot mapping assists +static const int fm_tab[16] = { 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8, -1, 9, 10, 11, -1 }; +static const int pcm_tab[16] = { 0, 4, 8, -1, 12, 16, 20, -1, 24, 28, 32, -1, 36, 40, 44, -1 }; + + +typedef struct +{ + UINT8 ext_en; + UINT8 ext_out; + UINT8 lfoFreq; + UINT8 lfowave; + UINT8 pms, ams; + UINT8 detune; + UINT8 multiple; + UINT8 tl; + UINT8 keyscale; + UINT8 ar; + UINT8 decay1rate, decay2rate; + UINT8 decay1lvl; + UINT8 relrate; + UINT8 block; + UINT8 fns_hi; + UINT32 fns; + UINT8 feedback; + UINT8 waveform; + UINT8 accon; + UINT8 algorithm; + UINT8 ch0_level, ch1_level, ch2_level, ch3_level; + + UINT32 startaddr; + UINT32 loopaddr; + UINT32 endaddr; + UINT8 altloop; + UINT8 fs; + UINT8 srcnote, srcb; + + UINT32 step; + UINT64 stepptr; + + UINT8 active; + UINT8 bits; + + // envelope generator + INT32 volume; + INT32 env_state; + INT32 env_attack_step; // volume increase step in attack state + INT32 env_decay1_step; + INT32 env_decay2_step; + INT32 env_release_step; + + INT64 feedback_modulation0; + INT64 feedback_modulation1; + + INT32 lfo_phase, lfo_step; + INT32 lfo_amplitude; + double lfo_phasemod; +} YMF271Slot; + +typedef struct +{ + UINT8 sync, pfm; + UINT8 Muted; +} YMF271Group; + +typedef struct +{ + // lookup tables + INT16 *lut_waves[8]; + double *lut_plfo[4][8]; + int *lut_alfo[4]; + double lut_ar[64]; + double lut_dc[64]; + double lut_lfo[256]; + int lut_attenuation[16]; + int lut_total_level[128]; + int lut_env_volume[256]; + + YMF271Slot slots[48]; + YMF271Group groups[12]; + + UINT8 regs_main[0x10]; + + UINT32 timerA, timerB; + UINT32 timerAVal, timerBVal; + UINT32 irqstate; + UINT8 status; + UINT8 enable; + + UINT32 ext_address; + UINT8 ext_rw; + UINT8 ext_readlatch; + + UINT8 *mem_base; + UINT32 mem_size; + UINT32 clock; + + //emu_timer *timA, *timB; + //sound_stream * stream; + INT32 *mix_buffer; + //const device_config *device; + + //devcb_resolved_read8 ext_mem_read; + //devcb_resolved_write8 ext_mem_write; + //void (*irq_callback)(const device_config *, int); + //void (*irq_callback)(int); +} YMF271Chip; + + +/*INLINE YMF271Chip *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == SOUND); + assert(sound_get_type(device) == SOUND_YMF271); + return (YMF271Chip *)device->token; +}*/ + +static UINT8 ymf271_read_memory(YMF271Chip *chip, UINT32 offset); + + +INLINE void calculate_step(YMF271Slot *slot) +{ + double st; + + if (slot->waveform == 7) + { + // external waveform (PCM) + st = (double)(2 * (slot->fns | 2048)) * pow_table[slot->block] * fs_frequency[slot->fs]; + st = st * multiple_table[slot->multiple]; + + // LFO phase modulation + st *= slot->lfo_phasemod; + + st /= (double)(524288/65536); // pre-multiply with 65536 + + slot->step = (UINT32)st; + } + else + { + // internal waveform (FM) + st = (double)(2 * slot->fns) * pow_table[slot->block]; + st = st * multiple_table[slot->multiple] * (double)(SIN_LEN); + + // LFO phase modulation + st *= slot->lfo_phasemod; + + st /= (double)(536870912/65536); // pre-multiply with 65536 + + slot->step = (UINT32)st; + } +} + +INLINE bool check_envelope_end(YMF271Slot *slot) +{ + if (slot->volume <= 0) + { + slot->active = 0; + slot->volume = 0; + return true; + } + return false; +} + +static void update_envelope(YMF271Slot *slot) +{ + switch (slot->env_state) + { + case ENV_ATTACK: + { + slot->volume += slot->env_attack_step; + + if (slot->volume >= (255 << ENV_VOLUME_SHIFT)) + { + slot->volume = (255 << ENV_VOLUME_SHIFT); + slot->env_state = ENV_DECAY1; + } + break; + } + + case ENV_DECAY1: + { + int decay_level = 255 - (slot->decay1lvl << 4); + slot->volume -= slot->env_decay1_step; + + if (!check_envelope_end(slot) && (slot->volume >> ENV_VOLUME_SHIFT) <= decay_level) + { + slot->env_state = ENV_DECAY2; + } + break; + } + + case ENV_DECAY2: + { + slot->volume -= slot->env_decay2_step; + check_envelope_end(slot); + break; + } + + case ENV_RELEASE: + { + slot->volume -= slot->env_release_step; + check_envelope_end(slot); + break; + } + } +} + +INLINE int get_keyscaled_rate(int rate, int keycode, int keyscale) +{ + int newrate = rate + RKS_Table[keycode][keyscale]; + + if (newrate > 63) + { + newrate = 63; + } + if (newrate < 0) + { + newrate = 0; + } + return newrate; +} + +INLINE int get_internal_keycode(int block, int fns) +{ + int n43; + if (fns < 0x780) + { + n43 = 0; + } + else if (fns < 0x900) + { + n43 = 1; + } + else if (fns < 0xa80) + { + n43 = 2; + } + else + { + n43 = 3; + } + + return ((block & 7) * 4) + n43; +} + +INLINE int get_external_keycode(int block, int fns) +{ + int n43; + if (fns < 0x100) + { + n43 = 0; + } + else if (fns < 0x300) + { + n43 = 1; + } + else if (fns < 0x500) + { + n43 = 2; + } + else + { + n43 = 3; + } + + return ((block & 7) * 4) + n43; +} + +static void init_envelope(YMF271Chip *chip, YMF271Slot *slot) +{ + int keycode, rate; + int decay_level = 255 - (slot->decay1lvl << 4); + + if (slot->waveform != 7) + { + keycode = get_internal_keycode(slot->block, slot->fns); + } + else + { + keycode = get_external_keycode(slot->block, slot->fns & 0x7ff); + /* keycode = (keycode + slot->srcb * 4 + slot->srcnote) / 2; */ // not sure + } + + // init attack state + rate = get_keyscaled_rate(slot->ar * 2, keycode, slot->keyscale); + slot->env_attack_step = (rate < 4) ? 0 : (int)(((double)(255-0) / chip->lut_ar[rate]) * 65536.0); + + // init decay1 state + rate = get_keyscaled_rate(slot->decay1rate * 2, keycode, slot->keyscale); + slot->env_decay1_step = (rate < 4) ? 0 : (int)(((double)(255-decay_level) / chip->lut_dc[rate]) * 65536.0); + + // init decay2 state + rate = get_keyscaled_rate(slot->decay2rate * 2, keycode, slot->keyscale); + slot->env_decay2_step = (rate < 4) ? 0 : (int)(((double)(255-0) / chip->lut_dc[rate]) * 65536.0); + + // init release state + rate = get_keyscaled_rate(slot->relrate * 4, keycode, slot->keyscale); + slot->env_release_step = (rate < 4) ? 0 : (int)(((double)(255-0) / chip->lut_ar[rate]) * 65536.0); + + slot->volume = (255-160) << ENV_VOLUME_SHIFT; // -60db + slot->env_state = ENV_ATTACK; +} + +static void init_lfo(YMF271Chip *chip, YMF271Slot *slot) +{ + slot->lfo_phase = 0; + slot->lfo_amplitude = 0; + slot->lfo_phasemod = 0; + + slot->lfo_step = (int)((((double)LFO_LENGTH * chip->lut_lfo[slot->lfoFreq]) / 44100.0) * 256.0); +} + +INLINE void update_lfo(YMF271Chip *chip, YMF271Slot *slot) +{ + slot->lfo_phase += slot->lfo_step; + + slot->lfo_amplitude = chip->lut_alfo[slot->lfowave][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)]; + slot->lfo_phasemod = chip->lut_plfo[slot->lfowave][slot->pms][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)]; + + calculate_step(slot); +} + +INLINE int calculate_slot_volume(YMF271Chip *chip, YMF271Slot *slot) +{ + // Note: Actually everyone of these stores only INT32 (16.16 fixed point), + // but the calculations need INT64. + INT32 volume; + INT64 env_volume; + INT64 lfo_volume = 65536; + + switch (slot->ams) + { + case 0: lfo_volume = 65536; break; // 0dB + case 1: lfo_volume = 65536 - ((slot->lfo_amplitude * 33124) >> 16); break; // 5.90625dB + case 2: lfo_volume = 65536 - ((slot->lfo_amplitude * 16742) >> 16); break; // 11.8125dB + case 3: lfo_volume = 65536 - ((slot->lfo_amplitude * 4277) >> 16); break; // 23.625dB + } + + env_volume = (chip->lut_env_volume[255 - (slot->volume >> ENV_VOLUME_SHIFT)] * lfo_volume) >> 16; + + volume = (env_volume * chip->lut_total_level[slot->tl]) >> 16; + + return volume; +} + +static void update_pcm(YMF271Chip *chip, int slotnum, INT32 *mixp, int length) +{ + int i; + INT64 final_volume; + INT16 sample; + INT64 ch0_vol, ch1_vol; //, ch2_vol, ch3_vol; + + YMF271Slot *slot = &chip->slots[slotnum]; + + if (!slot->active) + { + return; + } + +#ifdef _DEBUG + if (slot->waveform != 7) + { + logerror("Waveform %d in update_pcm !!!\n", slot->waveform); + } +#endif + + for (i = 0; i < length; i++) + { + // loop + if ((slot->stepptr>>16) > slot->endaddr) + { + slot->stepptr = slot->stepptr - ((UINT64)slot->endaddr<<16) + ((UINT64)slot->loopaddr<<16); + if ((slot->stepptr>>16) > slot->endaddr) + { + // overflow + slot->stepptr &= 0xffff; + slot->stepptr |= ((UINT64)slot->loopaddr<<16); + if ((slot->stepptr>>16) > slot->endaddr) + { + // still overflow? (triggers in rdft2, rarely) + slot->stepptr &= 0xffff; + slot->stepptr |= ((UINT64)slot->endaddr<<16); + } + } + } + + if (slot->bits == 8) + { + // 8bit + sample = ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>16))<<8; + } + else + { + // 12bit + if (slot->stepptr & 0x10000) + sample = ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3 + 2)<<8 | ((ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3 + 1) << 4) & 0xf0); + else + sample = ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3)<<8 | (ymf271_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3 + 1) & 0xf0); + } + + update_envelope(slot); + update_lfo(chip, slot); + + final_volume = calculate_slot_volume(chip, slot); + + ch0_vol = (final_volume * chip->lut_attenuation[slot->ch0_level]) >> 16; + ch1_vol = (final_volume * chip->lut_attenuation[slot->ch1_level]) >> 16; +// ch2_vol = (final_volume * chip->lut_attenuation[slot->ch2_level]) >> 16; +// ch3_vol = (final_volume * chip->lut_attenuation[slot->ch3_level]) >> 16; + + if (ch0_vol > 65536) ch0_vol = 65536; + if (ch1_vol > 65536) ch1_vol = 65536; + + *mixp++ += (sample * ch0_vol) >> 16; + *mixp++ += (sample * ch1_vol) >> 16; + + // go to next step + slot->stepptr += slot->step; + } +} + +// calculates the output of one FM operator +static INT64 calculate_op(YMF271Chip *chip, int slotnum, INT64 inp) +{ + YMF271Slot *slot = &chip->slots[slotnum]; + INT64 env, slot_output, slot_input = 0; + + update_envelope(slot); + update_lfo(chip, slot); + env = calculate_slot_volume(chip, slot); + + if (inp == OP_INPUT_FEEDBACK) + { + // from own feedback + slot_input = (slot->feedback_modulation0 + slot->feedback_modulation1) / 2; + slot->feedback_modulation0 = slot->feedback_modulation1; + } + else if (inp != OP_INPUT_NONE) + { + // from previous slot output + slot_input = ((inp << (SIN_BITS-2)) * modulation_level[slot->feedback]); + } + + slot_output = chip->lut_waves[slot->waveform][((slot->stepptr + slot_input) >> 16) & SIN_MASK]; + slot_output = (slot_output * env) >> 16; + slot->stepptr += slot->step; + + return slot_output; +} + +static void set_feedback(YMF271Chip *chip, int slotnum, INT64 inp) +{ + YMF271Slot *slot = &chip->slots[slotnum]; + slot->feedback_modulation1 = (((inp << (SIN_BITS-2)) * feedback_level[slot->feedback]) / 16); +} + +//static STREAM_UPDATE( ymf271_update ) +void ymf271_update(void *param, stream_sample_t **outputs, int samples) +{ + int i, j; + int op; + INT32 *mixp; + YMF271Chip *chip = (YMF271Chip *)param; + + memset(chip->mix_buffer, 0, sizeof(chip->mix_buffer[0])*samples*2); + + for (j = 0; j < 12; j++) + { + YMF271Group *slot_group = &chip->groups[j]; + mixp = &chip->mix_buffer[0]; + + if (slot_group->Muted) + continue; + +#ifdef _DEBUG + if (slot_group->pfm && slot_group->sync != 3) + { + logerror("ymf271 Group %d: PFM, Sync = %d, Waveform Slot1 = %d, Slot2 = %d, Slot3 = %d, Slot4 = %d\n", + j, slot_group->sync, chip->slots[j+0].waveform, chip->slots[j+12].waveform, chip->slots[j+24].waveform, chip->slots[j+36].waveform); + } +#endif + + switch (slot_group->sync) + { + // 4 operator FM + case 0: + { + int slot1 = j + (0*12); + int slot2 = j + (1*12); + int slot3 = j + (2*12); + int slot4 = j + (3*12); + //mixp = chip->mix_buffer; + + if (chip->slots[slot1].active) + { + for (i = 0; i < samples; i++) + { + INT64 output1 = 0, output2 = 0, output3 = 0, output4 = 0; + INT64 phase_mod1 = 0, phase_mod2 = 0, phase_mod3 = 0; + switch (chip->slots[slot1].algorithm) + { + // <--------| + // +--[S1]--|--+--[S3]--+--[S2]--+--[S4]--> + case 0: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + phase_mod2 = calculate_op(chip, slot2, phase_mod3); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // <-----------------| + // +--[S1]--+--[S3]--|--+--[S2]--+--[S4]--> + case 1: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + phase_mod2 = calculate_op(chip, slot2, phase_mod3); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // <--------| + // +--[S1]--| + // | + // --[S3]--+--[S2]--+--[S4]--> + case 2: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + phase_mod2 = calculate_op(chip, slot2, (phase_mod1 + phase_mod3) / 1); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // <--------| + // +--[S1]--| + // | + // --[S3]--+--[S2]--+--[S4]--> + case 3: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + phase_mod2 = calculate_op(chip, slot2, phase_mod3); + output4 = calculate_op(chip, slot4, (phase_mod1 + phase_mod2) / 1); + break; + + // --[S2]--| + // <--------| | + // +--[S1]--|--+--[S3]--+--[S4]--> + case 4: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, (phase_mod3 + phase_mod2) / 1); + break; + + // --[S2]-----| + // <-----------------| | + // +--[S1]--+--[S3]--|--+--[S4]--> + case 5: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, (phase_mod3 + phase_mod2) / 1); + break; + + // --[S2]-----+--[S4]--| + // | + // <--------| | + // +--[S1]--|--+--[S3]--+--> + case 6: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output3 = calculate_op(chip, slot3, phase_mod1); + phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // --[S2]--+--[S4]-----| + // | + // <-----------------| | + // +--[S1]--+--[S3]--|--+--> + case 7: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + output3 = phase_mod3; + phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // --[S3]--+--[S2]--+--[S4]--| + // | + // <--------| | + // +--[S1]--|-----------------+--> + case 8: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + phase_mod2 = calculate_op(chip, slot2, phase_mod3); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // <--------| + // +--[S1]--| + // | + // --[S3]--| | + // --[S2]--+--[S4]--+--> + case 9: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, (phase_mod3 + phase_mod2) / 1); + break; + + // --[S4]--| + // --[S2]--| + // <--------| | + // +--[S1]--|--+--[S3]--+--> + case 10: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output3 = calculate_op(chip, slot3, phase_mod1); + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, OP_INPUT_NONE); + break; + + // --[S4]-----| + // --[S2]-----| + // <-----------------| | + // +--[S1]--+--[S3]--|--+--> + case 11: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + output3 = phase_mod3; + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, OP_INPUT_NONE); + break; + + // |--+--[S4]--| + // <--------| |--+--[S3]--| + // +--[S1]--|--|--+--[S2]--+--> + case 12: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output3 = calculate_op(chip, slot3, phase_mod1); + output2 = calculate_op(chip, slot2, phase_mod1); + output4 = calculate_op(chip, slot4, phase_mod1); + break; + + // --[S3]--+--[S2]--| + // | + // --[S4]-----------| + // <--------| | + // +--[S1]--|--------+--> + case 13: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + output2 = calculate_op(chip, slot2, phase_mod3); + output4 = calculate_op(chip, slot4, OP_INPUT_NONE); + break; + + // --[S2]-----+--[S4]--| + // | + // <--------| +--[S3]--| + // +--[S1]--|--|--------+--> + case 14: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + output3 = calculate_op(chip, slot3, phase_mod1); + phase_mod2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, phase_mod2); + break; + + // --[S4]-----| + // --[S2]-----| + // --[S3]-----| + // <--------| | + // +--[S1]--|--+--> + case 15: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + output3 = calculate_op(chip, slot3, OP_INPUT_NONE); + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + output4 = calculate_op(chip, slot4, OP_INPUT_NONE); + break; + } + + *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch0_level]) + + (output2 * chip->lut_attenuation[chip->slots[slot2].ch0_level]) + + (output3 * chip->lut_attenuation[chip->slots[slot3].ch0_level]) + + (output4 * chip->lut_attenuation[chip->slots[slot4].ch0_level])) >> 16; + *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch1_level]) + + (output2 * chip->lut_attenuation[chip->slots[slot2].ch1_level]) + + (output3 * chip->lut_attenuation[chip->slots[slot3].ch1_level]) + + (output4 * chip->lut_attenuation[chip->slots[slot4].ch1_level])) >> 16; + } + } + break; + } + + // 2x 2 operator FM + case 1: + { + for (op = 0; op < 2; op++) + { + int slot1 = j + ((op + 0) * 12); + int slot3 = j + ((op + 2) * 12); + + mixp = chip->mix_buffer; + if (chip->slots[slot1].active) + { + for (i = 0; i < samples; i++) + { + INT64 output1 = 0, output3 = 0; + INT64 phase_mod1, phase_mod3 = 0; + switch (chip->slots[slot1].algorithm & 3) + { + // <--------| + // +--[S1]--|--+--[S3]--> + case 0: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output3 = calculate_op(chip, slot3, phase_mod1); + break; + + // <-----------------| + // +--[S1]--+--[S3]--|--> + case 1: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + output3 = phase_mod3; + break; + + // --[S3]-----| + // <--------| | + // +--[S1]--|--+--> + case 2: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + output3 = calculate_op(chip, slot3, OP_INPUT_NONE); + break; + // + // <--------| +--[S3]--| + // +--[S1]--|--|--------+--> + case 3: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + output3 = calculate_op(chip, slot3, phase_mod1); + break; + } + + *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch0_level]) + + (output3 * chip->lut_attenuation[chip->slots[slot3].ch0_level])) >> 16; + *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch1_level]) + + (output3 * chip->lut_attenuation[chip->slots[slot3].ch1_level])) >> 16; + } + } + } + break; + } + + // 3 operator FM + PCM + case 2: + { + int slot1 = j + (0*12); + int slot2 = j + (1*12); + int slot3 = j + (2*12); + //mixp = chip->mix_buffer; + + if (chip->slots[slot1].active) + { + for (i = 0; i < samples; i++) + { + INT64 output1 = 0, output2 = 0, output3 = 0; + INT64 phase_mod1 = 0, phase_mod3 = 0; + switch (chip->slots[slot1].algorithm & 7) + { + // <--------| + // +--[S1]--|--+--[S3]--+--[S2]--> + case 0: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + output2 = calculate_op(chip, slot2, phase_mod3); + break; + + // <-----------------| + // +--[S1]--+--[S3]--|--+--[S2]--> + case 1: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + output2 = calculate_op(chip, slot2, phase_mod3); + break; + + // --[S3]-----| + // <--------| | + // +--[S1]--|--+--[S2]--> + case 2: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + output2 = calculate_op(chip, slot2, (phase_mod1 + phase_mod3) / 1); + break; + + // --[S3]--+--[S2]--| + // <--------| | + // +--[S1]--|--------+--> + case 3: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + phase_mod3 = calculate_op(chip, slot3, OP_INPUT_NONE); + output2 = calculate_op(chip, slot2, phase_mod3); + break; + + // --[S2]--| + // <--------| | + // +--[S1]--|--+--[S3]--+--> + case 4: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output3 = calculate_op(chip, slot3, phase_mod1); + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + break; + + // --[S2]--| + // <-----------------| | + // +--[S1]--+--[S3]--|--+--> + case 5: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + phase_mod3 = calculate_op(chip, slot3, phase_mod1); + set_feedback(chip, slot1, phase_mod3); + output3 = phase_mod3; + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + break; + + // --[S2]-----| + // --[S3]-----| + // <--------| | + // +--[S1]--|--+--> + case 6: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + output3 = calculate_op(chip, slot3, OP_INPUT_NONE); + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + break; + + // --[S2]--| + // <--------| +--[S3]--| + // +--[S1]--|--|--------+--> + case 7: + phase_mod1 = calculate_op(chip, slot1, OP_INPUT_FEEDBACK); + set_feedback(chip, slot1, phase_mod1); + output1 = phase_mod1; + output3 = calculate_op(chip, slot3, phase_mod1); + output2 = calculate_op(chip, slot2, OP_INPUT_NONE); + break; + } + + *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch0_level]) + + (output2 * chip->lut_attenuation[chip->slots[slot2].ch0_level]) + + (output3 * chip->lut_attenuation[chip->slots[slot3].ch0_level])) >> 16; + *mixp++ += ((output1 * chip->lut_attenuation[chip->slots[slot1].ch1_level]) + + (output2 * chip->lut_attenuation[chip->slots[slot2].ch1_level]) + + (output3 * chip->lut_attenuation[chip->slots[slot3].ch1_level])) >> 16; + } + } + + mixp = chip->mix_buffer; + update_pcm(chip, j + (3*12), mixp, samples); + break; + } + + // PCM + case 3: + { + update_pcm(chip, j + (0*12), mixp, samples); + update_pcm(chip, j + (1*12), mixp, samples); + update_pcm(chip, j + (2*12), mixp, samples); + update_pcm(chip, j + (3*12), mixp, samples); + break; + } + } + } + + mixp = chip->mix_buffer; + for (i = 0; i < samples; i++) + { + outputs[0][i] = (*mixp++)>>2; + outputs[1][i] = (*mixp++)>>2; + } +} + +static void write_register(YMF271Chip *chip, int slotnum, int reg, UINT8 data) +{ + YMF271Slot *slot = &chip->slots[slotnum]; + + switch (reg) + { + case 0x0: + slot->ext_en = (data & 0x80) ? 1 : 0; + slot->ext_out = (data>>3)&0xf; + + if (data & 1) + { + // key on + slot->step = 0; + slot->stepptr = 0; + + slot->active = 1; + + calculate_step(slot); + init_envelope(chip, slot); + init_lfo(chip, slot); + slot->feedback_modulation0 = 0; + slot->feedback_modulation1 = 0; + } + else + { + if (slot->active) + { + slot->env_state = ENV_RELEASE; + } + } + break; + + case 0x1: + slot->lfoFreq = data; + break; + + case 0x2: + slot->lfowave = data & 3; + slot->pms = (data >> 3) & 0x7; + slot->ams = (data >> 6) & 0x3; + break; + + case 0x3: + slot->multiple = data & 0xf; + slot->detune = (data >> 4) & 0x7; + break; + + case 0x4: + slot->tl = data & 0x7f; + break; + + case 0x5: + slot->ar = data & 0x1f; + slot->keyscale = (data >> 5) & 0x7; + break; + + case 0x6: + slot->decay1rate = data & 0x1f; + break; + + case 0x7: + slot->decay2rate = data & 0x1f; + break; + + case 0x8: + slot->relrate = data & 0xf; + slot->decay1lvl = (data >> 4) & 0xf; + break; + + case 0x9: + // write frequency and block here + slot->fns = (slot->fns_hi << 8 & 0x0f00) | data; + slot->block = slot->fns_hi >> 4 & 0xf; + break; + + case 0xa: + slot->fns_hi = data; + break; + + case 0xb: + slot->waveform = data & 0x7; + slot->feedback = (data >> 4) & 0x7; + slot->accon = (data & 0x80) ? 1 : 0; + break; + + case 0xc: + slot->algorithm = data & 0xf; + break; + + case 0xd: + slot->ch0_level = data >> 4; + slot->ch1_level = data & 0xf; + break; + + case 0xe: + slot->ch2_level = data >> 4; + slot->ch3_level = data & 0xf; + break; + + default: + break; + } +} + +static void ymf271_write_fm(YMF271Chip *chip, int bank, UINT8 address, UINT8 data) +{ + int groupnum = fm_tab[address & 0xf]; + int reg = (address >> 4) & 0xf; + int sync_reg; + int sync_mode; + + if (groupnum == -1) + { + logerror("ymf271_write_fm invalid group %02X %02X\n", address, data); + return; + } + + // check if the register is a synchronized register + sync_reg = 0; + switch (reg) + { + case 0: + case 9: + case 10: + case 12: + case 13: + case 14: + sync_reg = 1; + break; + + default: + break; + } + + // check if the slot is key on slot for synchronizing + sync_mode = 0; + switch (chip->groups[groupnum].sync) + { + // 4 slot mode + case 0: + if (bank == 0) + sync_mode = 1; + break; + + // 2x 2 slot mode + case 1: + if (bank == 0 || bank == 1) + sync_mode = 1; + break; + + // 3 slot + 1 slot mode + case 2: + if (bank == 0) + sync_mode = 1; + break; + + default: + break; + } + + // key-on slot & synced register + if (sync_mode && sync_reg) + { + switch (chip->groups[groupnum].sync) + { + // 4 slot mode + case 0: + write_register(chip, (12 * 0) + groupnum, reg, data); + write_register(chip, (12 * 1) + groupnum, reg, data); + write_register(chip, (12 * 2) + groupnum, reg, data); + write_register(chip, (12 * 3) + groupnum, reg, data); + break; + + // 2x 2 slot mode + case 1: + if (bank == 0) + { + // Slot 1 - Slot 3 + write_register(chip, (12 * 0) + groupnum, reg, data); + write_register(chip, (12 * 2) + groupnum, reg, data); + } + else + { + // Slot 2 - Slot 4 + write_register(chip, (12 * 1) + groupnum, reg, data); + write_register(chip, (12 * 3) + groupnum, reg, data); + } + break; + + // 3 slot + 1 slot mode (1 slot is handled normally) + case 2: + write_register(chip, (12 * 0) + groupnum, reg, data); + write_register(chip, (12 * 1) + groupnum, reg, data); + write_register(chip, (12 * 2) + groupnum, reg, data); + break; + } + } + else + { + // write register normally + write_register(chip, (12 * bank) + groupnum, reg, data); + } +} + +static void ymf271_write_pcm(YMF271Chip *chip, UINT8 address, UINT8 data) +{ + int slotnum = pcm_tab[address & 0xf]; + YMF271Slot *slot; + if (slotnum == -1) + { + logerror("ymf271_write_pcm invalid slot %02X %02X\n", address, data); + return; + } + slot = &chip->slots[slotnum]; + + switch ((address >> 4) & 0xf) + { + case 0x0: + slot->startaddr &= ~0xff; + slot->startaddr |= data; + break; + + case 0x1: + slot->startaddr &= ~0xff00; + slot->startaddr |= data<<8; + break; + + case 0x2: + slot->startaddr &= ~0xff0000; + slot->startaddr |= (data & 0x7f)<<16; + slot->altloop = (data & 0x80) ? 1 : 0; + //if (slot->altloop) + // popmessage("ymf271 A/L, contact MAMEdev"); + break; + + case 0x3: + slot->endaddr &= ~0xff; + slot->endaddr |= data; + break; + + case 0x4: + slot->endaddr &= ~0xff00; + slot->endaddr |= data<<8; + break; + + case 0x5: + slot->endaddr &= ~0xff0000; + slot->endaddr |= (data & 0x7f)<<16; + break; + + case 0x6: + slot->loopaddr &= ~0xff; + slot->loopaddr |= data; + break; + + case 0x7: + slot->loopaddr &= ~0xff00; + slot->loopaddr |= data<<8; + break; + + case 0x8: + slot->loopaddr &= ~0xff0000; + slot->loopaddr |= (data & 0x7f)<<16; + break; + + case 0x9: + slot->fs = data & 0x3; + slot->bits = (data & 0x4) ? 12 : 8; + slot->srcnote = (data >> 3) & 0x3; + slot->srcb = (data >> 5) & 0x7; + break; + + default: + break; + } +} + +/*static TIMER_CALLBACK( ymf271_timer_a_tick ) +{ + YMF271Chip *chip = (YMF271Chip *)ptr; + + chip->status |= 1; + + if (chip->enable & 4) + { + chip->irqstate |= 1; + if (chip->irq_callback) chip->irq_callback(chip->device, 1); + } +} + +static TIMER_CALLBACK( ymf271_timer_b_tick ) +{ + YMF271Chip *chip = (YMF271Chip *)ptr; + + chip->status |= 2; + + if (chip->enable & 8) + { + chip->irqstate |= 2; + if (chip->irq_callback) chip->irq_callback(chip->device, 1); + } +}*/ + +static UINT8 ymf271_read_memory(YMF271Chip *chip, UINT32 offset) +{ + /*if (m_ext_read_handler.isnull()) + { + if (offset < chip->mem_size) + return chip->mem_base[offset]; + + // 8MB chip limit (shouldn't happen) + else if (offset > 0x7fffff) + return chip->mem_base[offset & 0x7fffff]; + + else + return 0; + } + else + return m_ext_read_handler(offset);*/ + + offset &= 0x7FFFFF; + if (offset < chip->mem_size) + return chip->mem_base[offset]; + else + return 0; +} + +static void ymf271_write_timer(YMF271Chip *chip, UINT8 address, UINT8 data) +{ + if ((address & 0xf0) == 0) + { + int groupnum = fm_tab[address & 0xf]; + YMF271Group *group; + if (groupnum == -1) + { + logerror("ymf271_write_timer invalid group %02X %02X\n", address, data); + return; + } + group = &chip->groups[groupnum]; + + group->sync = data & 0x3; + group->pfm = data >> 7; + } + else + { + switch (address) + { + case 0x10: + chip->timerA = data; + break; + + case 0x11: + // According to Yamaha's documentation, this sets timer A upper 2 bits + // (it says timer A is 10 bits). But, PCB audio recordings proves + // otherwise: it doesn't affect timer A frequency. (see ms32.c tetrisp) + // Does this register have another function regarding timer A/B? + break; + + case 0x12: + chip->timerB = data; + break; + + case 0x13: + // timer A load + if (~chip->enable & data & 1) + { + //attotime period = attotime::from_hz(chip->clock) * (384 * 4 * (256 - chip->timerA)); + //chip->timA->adjust((data & 1) ? period : attotime::never, 0); + } + + // timer B load + if (~chip->enable & data & 2) + { + //attotime period = attotime::from_hz(chip->clock) * (384 * 16 * (256 - chip->timerB)); + //chip->timB->adjust((data & 2) ? period : attotime::never, 0); + } + + // timer A reset + if (data & 0x10) + { + chip->irqstate &= ~1; + chip->status &= ~1; + + //if (!chip->irq_handler.isnull() && ~chip->irqstate & 2) + // chip->irq_handler(0); + } + + // timer B reset + if (data & 0x20) + { + chip->irqstate &= ~2; + chip->status &= ~2; + + //if (!chip->irq_handler.isnull() && ~chip->irqstate & 1) + // chip->irq_handler(0); + } + + chip->enable = data; + break; + + case 0x14: + chip->ext_address &= ~0xff; + chip->ext_address |= data; + break; + case 0x15: + chip->ext_address &= ~0xff00; + chip->ext_address |= data << 8; + break; + case 0x16: + chip->ext_address &= ~0xff0000; + chip->ext_address |= (data & 0x7f) << 16; + chip->ext_rw = (data & 0x80) ? 1 : 0; + break; + case 0x17: + chip->ext_address = (chip->ext_address + 1) & 0x7fffff; + //if (!chip->ext_rw && !chip->ext_write_handler.isnull()) + // chip->ext_write_handler(chip->ext_address, data); + break; + } + } +} + +//WRITE8_DEVICE_HANDLER( ymf271_w ) +void ymf271_w(void *_info, offs_t offset, UINT8 data) +{ + //YMF271Chip *chip = get_safe_token(device); + YMF271Chip *chip = (YMF271Chip *)_info; + + chip->regs_main[offset & 0xf] = data; + + switch (offset & 0xf) + { + case 0x0: + case 0x2: + case 0x4: + case 0x6: + case 0x8: + case 0xc: + // address regs + break; + + case 0x1: + ymf271_write_fm(chip, 0, chip->regs_main[0x0], data); + break; + + case 0x3: + ymf271_write_fm(chip, 1, chip->regs_main[0x2], data); + break; + + case 0x5: + ymf271_write_fm(chip, 2, chip->regs_main[0x4], data); + break; + + case 0x7: + ymf271_write_fm(chip, 3, chip->regs_main[0x6], data); + break; + + case 0x9: + ymf271_write_pcm(chip, chip->regs_main[0x8], data); + break; + + case 0xd: + ymf271_write_timer(chip, chip->regs_main[0xc], data); + break; + + default: + break; + } +} + +//READ8_DEVICE_HANDLER( ymf271_r ) +UINT8 ymf271_r(void *_info, offs_t offset) +{ + //YMF271Chip *chip = get_safe_token(device); + YMF271Chip *chip = (YMF271Chip *)_info; + + switch (offset & 0xf) + { + case 0x0: + return chip->status; + + case 0x1: + // statusreg 2 + return 0; + + case 0x2: + { + UINT8 ret; + if (!chip->ext_rw) + return 0xff; + + ret = chip->ext_readlatch; + chip->ext_address = (chip->ext_address + 1) & 0x7fffff; + chip->ext_readlatch = ymf271_read_memory(chip, chip->ext_address); + return ret; + } + + default: + break; + } + + return 0xff; +} + +static void init_tables(YMF271Chip *chip) +{ + int i,j; + double clock_correction; + + for (i = 0; i < 8; i++) + chip->lut_waves[i] = (INT16*)malloc(sizeof(INT16) * SIN_LEN); + + for (i = 0; i < 4*8; i++) + chip->lut_plfo[i>>3][i&7] = (double*)malloc(sizeof(double) * LFO_LENGTH); + + for (i = 0; i < 4; i++) + chip->lut_alfo[i] = (int*)malloc(sizeof(int) * LFO_LENGTH); + + for (i=0; i < SIN_LEN; i++) + { + double m = sin( ((i*2)+1) * M_PI / SIN_LEN ); + double m2 = sin( ((i*4)+1) * M_PI / SIN_LEN ); + + // Waveform 0: sin(wt) (0 <= wt <= 2PI) + chip->lut_waves[0][i] = (INT16)(m * MAXOUT); + + // Waveform 1: sin?(wt) (0 <= wt <= PI) -sin?(wt) (PI <= wt <= 2PI) + chip->lut_waves[1][i] = (i < (SIN_LEN/2)) ? (INT16)((m * m) * MAXOUT) : (INT16)((m * m) * MINOUT); + + // Waveform 2: sin(wt) (0 <= wt <= PI) -sin(wt) (PI <= wt <= 2PI) + chip->lut_waves[2][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : (INT16)(-m * MAXOUT); + + // Waveform 3: sin(wt) (0 <= wt <= PI) 0 + chip->lut_waves[3][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : 0; + + // Waveform 4: sin(2wt) (0 <= wt <= PI) 0 + chip->lut_waves[4][i] = (i < (SIN_LEN/2)) ? (INT16)(m2 * MAXOUT) : 0; + + // Waveform 5: |sin(2wt)| (0 <= wt <= PI) 0 + chip->lut_waves[5][i] = (i < (SIN_LEN/2)) ? (INT16)(fabs(m2) * MAXOUT) : 0; + + // Waveform 6: 1 (0 <= wt <= 2PI) + chip->lut_waves[6][i] = (INT16)(1 * MAXOUT); + + chip->lut_waves[7][i] = 0; + } + + for (i = 0; i < LFO_LENGTH; i++) + { + int tri_wave; + double ftri_wave, fsaw_wave; + double plfo[4]; + + // LFO phase modulation + plfo[0] = 0; + + fsaw_wave = ((i % (LFO_LENGTH/2)) * PLFO_MAX) / (double)((LFO_LENGTH/2)-1); + plfo[1] = (i < (LFO_LENGTH/2)) ? fsaw_wave : fsaw_wave - PLFO_MAX; + + plfo[2] = (i < (LFO_LENGTH/2)) ? PLFO_MAX : PLFO_MIN; + + ftri_wave = ((i % (LFO_LENGTH/4)) * PLFO_MAX) / (double)(LFO_LENGTH/4); + switch (i / (LFO_LENGTH/4)) + { + case 0: plfo[3] = ftri_wave; break; + case 1: plfo[3] = PLFO_MAX - ftri_wave; break; + case 2: plfo[3] = 0 - ftri_wave; break; + case 3: plfo[3] = 0 - (PLFO_MAX - ftri_wave); break; + default: plfo[3] = 0; /*assert(0);*/ break; + } + + for (j = 0; j < 4; j++) + { + chip->lut_plfo[j][0][i] = pow(2.0, 0.0); + chip->lut_plfo[j][1][i] = pow(2.0, (3.378 * plfo[j]) / 1200.0); + chip->lut_plfo[j][2][i] = pow(2.0, (5.0646 * plfo[j]) / 1200.0); + chip->lut_plfo[j][3][i] = pow(2.0, (6.7495 * plfo[j]) / 1200.0); + chip->lut_plfo[j][4][i] = pow(2.0, (10.1143 * plfo[j]) / 1200.0); + chip->lut_plfo[j][5][i] = pow(2.0, (20.1699 * plfo[j]) / 1200.0); + chip->lut_plfo[j][6][i] = pow(2.0, (40.1076 * plfo[j]) / 1200.0); + chip->lut_plfo[j][7][i] = pow(2.0, (79.307 * plfo[j]) / 1200.0); + } + + // LFO amplitude modulation + chip->lut_alfo[0][i] = 0; + + chip->lut_alfo[1][i] = ALFO_MAX - ((i * ALFO_MAX) / LFO_LENGTH); + + chip->lut_alfo[2][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX : ALFO_MIN; + + tri_wave = ((i % (LFO_LENGTH/2)) * ALFO_MAX) / (LFO_LENGTH/2); + chip->lut_alfo[3][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX-tri_wave : tri_wave; + } + + for (i = 0; i < 256; i++) + { + chip->lut_env_volume[i] = (int)(65536.0 / pow(10.0, ((double)i / (256.0 / 96.0)) / 20.0)); + } + + for (i = 0; i < 16; i++) + { + chip->lut_attenuation[i] = (int)(65536.0 / pow(10.0, channel_attenuation_table[i] / 20.0)); + } + for (i = 0; i < 128; i++) + { + double db = 0.75 * (double)i; + chip->lut_total_level[i] = (int)(65536.0 / pow(10.0, db / 20.0)); + } + + // timing may use a non-standard XTAL + clock_correction = (double)(STD_CLOCK) / (double)(chip->clock); + for (i = 0; i < 256; i++) + { + chip->lut_lfo[i] = LFO_frequency_table[i] * clock_correction; + } + + for (i = 0; i < 64; i++) + { + // attack/release rate in number of samples + chip->lut_ar[i] = (ARTime[i] * clock_correction * 44100.0) / 1000.0; + } + for (i = 0; i < 64; i++) + { + // decay rate in number of samples + chip->lut_dc[i] = (DCTime[i] * clock_correction * 44100.0) / 1000.0; + } +} + +/*static void init_state(YMF271Chip *chip, const device_config *device) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH(chip->slots); i++) + { + state_save_register_device_item(device, i, chip->slots[i].ext_out); + state_save_register_device_item(device, i, chip->slots[i].lfoFreq); + state_save_register_device_item(device, i, chip->slots[i].pms); + state_save_register_device_item(device, i, chip->slots[i].ams); + state_save_register_device_item(device, i, chip->slots[i].detune); + state_save_register_device_item(device, i, chip->slots[i].multiple); + state_save_register_device_item(device, i, chip->slots[i].tl); + state_save_register_device_item(device, i, chip->slots[i].keyscale); + state_save_register_device_item(device, i, chip->slots[i].ar); + state_save_register_device_item(device, i, chip->slots[i].decay1rate); + state_save_register_device_item(device, i, chip->slots[i].decay2rate); + state_save_register_device_item(device, i, chip->slots[i].decay1lvl); + state_save_register_device_item(device, i, chip->slots[i].relrate); + state_save_register_device_item(device, i, chip->slots[i].fns); + state_save_register_device_item(device, i, chip->slots[i].block); + state_save_register_device_item(device, i, chip->slots[i].feedback); + state_save_register_device_item(device, i, chip->slots[i].waveform); + state_save_register_device_item(device, i, chip->slots[i].accon); + state_save_register_device_item(device, i, chip->slots[i].algorithm); + state_save_register_device_item(device, i, chip->slots[i].ch0_level); + state_save_register_device_item(device, i, chip->slots[i].ch1_level); + state_save_register_device_item(device, i, chip->slots[i].ch2_level); + state_save_register_device_item(device, i, chip->slots[i].ch3_level); + state_save_register_device_item(device, i, chip->slots[i].startaddr); + state_save_register_device_item(device, i, chip->slots[i].loopaddr); + state_save_register_device_item(device, i, chip->slots[i].endaddr); + state_save_register_device_item(device, i, chip->slots[i].fs); + state_save_register_device_item(device, i, chip->slots[i].srcnote); + state_save_register_device_item(device, i, chip->slots[i].srcb); + state_save_register_device_item(device, i, chip->slots[i].step); + state_save_register_device_item(device, i, chip->slots[i].stepptr); + state_save_register_device_item(device, i, chip->slots[i].active); + state_save_register_device_item(device, i, chip->slots[i].bits); + state_save_register_device_item(device, i, chip->slots[i].volume); + state_save_register_device_item(device, i, chip->slots[i].env_state); + state_save_register_device_item(device, i, chip->slots[i].env_attack_step); + state_save_register_device_item(device, i, chip->slots[i].env_decay1_step); + state_save_register_device_item(device, i, chip->slots[i].env_decay2_step); + state_save_register_device_item(device, i, chip->slots[i].env_release_step); + state_save_register_device_item(device, i, chip->slots[i].feedback_modulation0); + state_save_register_device_item(device, i, chip->slots[i].feedback_modulation1); + state_save_register_device_item(device, i, chip->slots[i].lfo_phase); + state_save_register_device_item(device, i, chip->slots[i].lfo_step); + state_save_register_device_item(device, i, chip->slots[i].lfo_amplitude); + } + + for (i = 0; i < sizeof(chip->groups) / sizeof(chip->groups[0]); i++) + { + state_save_register_device_item(device, i, chip->groups[i].sync); + state_save_register_device_item(device, i, chip->groups[i].pfm); + } + + state_save_register_device_item(device, 0, chip->timerA); + state_save_register_device_item(device, 0, chip->timerB); + state_save_register_device_item(device, 0, chip->timerAVal); + state_save_register_device_item(device, 0, chip->timerBVal); + state_save_register_device_item(device, 0, chip->irqstate); + state_save_register_device_item(device, 0, chip->status); + state_save_register_device_item(device, 0, chip->enable); + state_save_register_device_item(device, 0, chip->reg0); + state_save_register_device_item(device, 0, chip->reg1); + state_save_register_device_item(device, 0, chip->reg2); + state_save_register_device_item(device, 0, chip->reg3); + state_save_register_device_item(device, 0, chip->pcmreg); + state_save_register_device_item(device, 0, chip->timerreg); + state_save_register_device_item(device, 0, chip->ext_address); + state_save_register_device_item(device, 0, chip->ext_read); +}*/ + +//static DEVICE_START( ymf271 ) +int device_start_ymf271(void **_info, int clock) +{ + //static const ymf271_interface defintrf = { DEVCB_NULL }; + //const ymf271_interface *intf; + int i; + //YMF271Chip *chip = get_safe_token(device); + YMF271Chip *chip; + + chip = (YMF271Chip *) calloc(1, sizeof(YMF271Chip)); + *_info = (void *) chip; + + //chip->device = device; + chip->clock = clock; + + //intf = (device->static_config != NULL) ? (const ymf271_interface *)device->static_config : &defintrf; + + chip->mem_size = 0x00; + chip->mem_base = NULL; + + init_tables(chip); + //init_state(chip); + //chip->stream = stream_create(device, 0, 2, device->clock/384, chip, ymf271_update); + + //chip->mix_buffer = auto_alloc_array(machine, INT32, 44100*2); + chip->mix_buffer = (INT32*)malloc(44100*2 * sizeof(INT32)); + + for (i = 0; i < 12; i ++) + chip->groups[i].Muted = 0x00; + + return clock/384; +} + +//static DEVICE_STOP( ymf271 ) +void device_stop_ymf271(void *_info) +{ + int i; + YMF271Chip *chip = (YMF271Chip *)_info; + + free(chip->mem_base); chip->mem_base = NULL; + + for (i=0; i < 8; i++) + { + free(chip->lut_waves[i]); + chip->lut_waves[i] = NULL; + } + for (i = 0; i < 4*8; i++) + { + free(chip->lut_plfo[i>>3][i&7]); + chip->lut_plfo[i>>3][i&7] = NULL; + } + + for (i = 0; i < 4; i++) + { + free(chip->lut_alfo[i]); + chip->lut_alfo[i] = NULL; + } + + free(chip->mix_buffer); + chip->mix_buffer = NULL; + + free(chip); + + return; +} + +//static DEVICE_RESET( ymf271 ) +void device_reset_ymf271(void *_info) +{ + int i; + //YMF271Chip *chip = get_safe_token(device); + YMF271Chip *chip = (YMF271Chip *)_info; + + for (i = 0; i < 48; i++) + { + chip->slots[i].active = 0; + chip->slots[i].volume = 0; + } + + // reset timers and IRQ + //chip->timA->reset(); + //chip->timB->reset(); + + chip->irqstate = 0; + chip->status = 0; + chip->enable = 0; + + //if (!chip->irq_handler.isnull()) + // chip->irq_handler(0); +} + +void ymf271_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + YMF271Chip *chip = (YMF271Chip *)_info; + + if (chip->mem_size != ROMSize) + { + chip->mem_base = (UINT8*)realloc(chip->mem_base, ROMSize); + chip->mem_size = ROMSize; + memset(chip->mem_base, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(chip->mem_base + DataStart, ROMData, DataLength); + + return; +} + +void ymf271_set_mute_mask(void *_info, UINT32 MuteMask) +{ + YMF271Chip *chip = (YMF271Chip *)_info; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 12; CurChn ++) + chip->groups[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + + return; +} + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ymf271 ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(YMF271Chip); break; + + // --- the following bits of info are returned as pointers to data or functions + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymf271 ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ymf271 ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YMF271"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ diff --git a/Frameworks/GME/vgmplay/chips/ymf271.h b/Frameworks/GME/vgmplay/chips/ymf271.h new file mode 100644 index 000000000..6f36a9936 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ymf271.h @@ -0,0 +1,30 @@ +#pragma once + +//#include "devcb.h" + +//typedef struct _ymf271_interface ymf271_interface; +//struct _ymf271_interface +//{ +// //devcb_read8 ext_read; /* external memory read */ +// //devcb_write8 ext_write; /* external memory write */ +// //void (*irq_callback)(const device_config *device, int state); /* irq callback */ +// void (*irq_callback)(int state); /* irq callback */ +//}; + +/*READ8_DEVICE_HANDLER( ymf271_r ); +WRITE8_DEVICE_HANDLER( ymf271_w ); + +DEVICE_GET_INFO( ymf271 ); +#define SOUND_YMF271 DEVICE_GET_INFO_NAME( ymf271 )*/ + +void ymf271_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_ymf271(void **chip, int clock); +void device_stop_ymf271(void *chip); +void device_reset_ymf271(void *chip); + +UINT8 ymf271_r(void *chip, offs_t offset); +void ymf271_w(void *chip, offs_t offset, UINT8 data); +void ymf271_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void ymf271_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/chips/ymf278b.c b/Frameworks/GME/vgmplay/chips/ymf278b.c new file mode 100644 index 000000000..99fb3ffb7 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ymf278b.c @@ -0,0 +1,1220 @@ +/* + + YMF278B FM + Wave table Synthesizer (OPL4) + + Timer and PCM YMF278B. The FM is shared with the ymf262. + + This chip roughly splits the difference between the Sega 315-5560 MultiPCM + (Multi32, Model 1/2) and YMF 292-F SCSP (later Model 2, STV, Saturn, Model 3). + + Features as listed in LSI-4MF2782 data sheet: + FM Synthesis (same as YMF262) + 1. Sound generation mode + Two-operater mode + Generates eighteen voices or fifteen voices plus five rhythm sounds simultaneously + Four-operator mode + Generates six voices in four-operator mode plus six voices in two-operator mode simultaneously, + or generates six voices in four-operator mode plus three voices in two-operator mode plus five + rhythm sounds simultaneously + 2. Eight selectable waveforms + 3. Stereo output + Wave Table Synthesis + 1. Generates twenty-four voices simultaneously + 2. 44.1kHz sampling rate for output sound data + 3. Selectable from 8-bit, 12-bit and 16-bit word lengths for wave data + 4. Stereo output (16-stage panpot for each voice) + Wave Data + 1. Accepts 32M bit external memory at maximum + 2. Up to 512 wave tables + 3. External ROM or SRAM can be connected. With SRAM connected, the CPU can download wave data + 4. Outputs chip select signals for 1Mbit, 4Mbit, 8Mbit or 16Mbit memory + 5. Can be directly connected to the Yamaha YRW801 (Wave data ROM) + Features of YRW801 as listed in LSI 4RW801A2 + Built-in wave data of tones which comply with GM system Level 1 + Melody tone ....... 128 tones + Percussion tone ... 47 tones + 16Mbit capacity (2,097,152word x 8) + + By R. Belmont and O. Galibert. + + Copyright R. Belmont and O. Galibert. + + This software is dual-licensed: it may be used in MAME and properly licensed + MAME derivatives under the terms of the MAME license. For use outside of + MAME and properly licensed derivatives, it is available under the + terms of the GNU Lesser General Public License (LGPL), version 2.1. + You may read the LGPL at http://www.gnu.org/licenses/lgpl.html + + Changelog: + Sep. 8, 2002 - fixed ymf278b_compute_rate when OCT is negative (RB) + Dec. 11, 2002 - added ability to set non-standard clock rates (RB) + fixed envelope target for release (fixes missing + instruments in hotdebut). + Thanks to Team Japump! for MP3s from a real PCB. + fixed crash if MAME is run with no sound. + June 4, 2003 - Changed to dual-license with LGPL for use in OpenMSX. + OpenMSX contributed a bugfix where looped samples were + not being addressed properly, causing pitch fluctuation. + August 15, 2010 - Backport to MAME-style C from OpenMSX +*/ + +#include +#include "mamedef.h" +//#include "sndintrf.h" +//#include "streams.h" +//#include "cpuintrf.h" +#include +#include +#include +#include +#include "ymf262.h" +#include "ymf278b.h" + +#define NULL ((void *)0) + +typedef struct +{ + UINT32 startaddr; + UINT32 loopaddr; + UINT32 endaddr; + UINT32 step; /* fixed-point frequency step */ + UINT32 stepptr; /* fixed-point pointer into the sample */ + UINT32 pos; + INT16 sample1, sample2; + + INT32 env_vol; + + INT32 lfo_cnt; + INT32 lfo_step; + INT32 lfo_max; + + INT16 wave; /* wavetable number */ + INT16 FN; /* f-number */ + INT8 OCT; /* octave */ + INT8 PRVB; /* pseudo-reverb */ + INT8 LD; /* level direct */ + INT8 TL; /* total level */ + INT8 pan; /* panpot */ + INT8 lfo; /* LFO */ + INT8 vib; /* vibrato */ + INT8 AM; /* AM level */ + + INT8 AR; + INT8 D1R; + INT32 DL; + INT8 D2R; + INT8 RC; /* rate correction */ + INT8 RR; + + INT8 bits; /* width of the samples */ + INT8 active; /* slot keyed on */ + + INT8 state; + INT8 lfo_active; + + UINT8 Muted; +} YMF278BSlot; + +typedef struct +{ + YMF278BSlot slots[24]; + + UINT32 eg_cnt; /* Global envelope generator counter. */ + + INT8 wavetblhdr; + INT8 memmode; + INT32 memadr; + + INT32 fm_l, fm_r; + INT32 pcm_l, pcm_r; + + //UINT8 timer_a_count, timer_b_count, enable, current_irq; + //emu_timer *timer_a, *timer_b; + //int irq_line; + + UINT8 port_A, port_B, port_C; + //void (*irq_callback)(const device_config *, int); + void (*irq_callback)(int); + //const device_config *device; + + UINT32 ROMSize; + UINT8 *rom; + UINT32 RAMSize; + UINT8 *ram; + int clock; + + INT32 volume[256*4]; // precalculated attenuation values with some marging for enveloppe and pan levels + + UINT8 regs[256]; + + void *fmchip; + UINT8 FMEnabled; // that saves a whole lot of CPU + //sound_stream * stream; +} YMF278BChip; + +#define EG_SH 16 // 16.16 fixed point (EG timing) +#define EG_TIMER_OVERFLOW (1 << EG_SH) + +// envelope output entries +#define ENV_BITS 10 +#define ENV_LEN (1 << ENV_BITS) +#define ENV_STEP (128.0 / ENV_LEN) +#define MAX_ATT_INDEX ((1 << (ENV_BITS - 1)) - 1) // 511 +#define MIN_ATT_INDEX 0 + +// Envelope Generator phases +#define EG_ATT 4 +#define EG_DEC 3 +#define EG_SUS 2 +#define EG_REL 1 +#define EG_OFF 0 + +#define EG_REV 5 // pseudo reverb +#define EG_DMP 6 // damp + +// Pan values, units are -3dB, i.e. 8. +const INT32 pan_left[16] = { + 0, 8, 16, 24, 32, 40, 48, 256, 256, 0, 0, 0, 0, 0, 0, 0 +}; +const INT32 pan_right[16] = { + 0, 0, 0, 0, 0, 0, 0, 0, 256, 256, 48, 40, 32, 24, 16, 8 +}; + +// Mixing levels, units are -3dB, and add some marging to avoid clipping +const INT32 mix_level[8] = { + 8, 16, 24, 32, 40, 48, 56, 256 +}; + +// decay level table (3dB per step) +// 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB) +#define SC(db) (db * (2.0 / ENV_STEP)) +const UINT32 dl_tab[16] = { + SC( 0), SC( 1), SC( 2), SC(3 ), SC(4 ), SC(5 ), SC(6 ), SC( 7), + SC( 8), SC( 9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31) +}; +#undef SC + +#define RATE_STEPS 8 +const UINT8 eg_inc[15 * RATE_STEPS] = { +//cycle:0 1 2 3 4 5 6 7 + 0, 1, 0, 1, 0, 1, 0, 1, // 0 rates 00..12 0 (increment by 0 or 1) + 0, 1, 0, 1, 1, 1, 0, 1, // 1 rates 00..12 1 + 0, 1, 1, 1, 0, 1, 1, 1, // 2 rates 00..12 2 + 0, 1, 1, 1, 1, 1, 1, 1, // 3 rates 00..12 3 + + 1, 1, 1, 1, 1, 1, 1, 1, // 4 rate 13 0 (increment by 1) + 1, 1, 1, 2, 1, 1, 1, 2, // 5 rate 13 1 + 1, 2, 1, 2, 1, 2, 1, 2, // 6 rate 13 2 + 1, 2, 2, 2, 1, 2, 2, 2, // 7 rate 13 3 + + 2, 2, 2, 2, 2, 2, 2, 2, // 8 rate 14 0 (increment by 2) + 2, 2, 2, 4, 2, 2, 2, 4, // 9 rate 14 1 + 2, 4, 2, 4, 2, 4, 2, 4, // 10 rate 14 2 + 2, 4, 4, 4, 2, 4, 4, 4, // 11 rate 14 3 + + 4, 4, 4, 4, 4, 4, 4, 4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay + 8, 8, 8, 8, 8, 8, 8, 8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) + 0, 0, 0, 0, 0, 0, 0, 0, // 14 infinity rates for attack and decay(s) +}; + +#define O(a) (a * RATE_STEPS) +const UINT8 eg_rate_select[64] = { + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 0),O( 1),O( 2),O( 3), + O( 4),O( 5),O( 6),O( 7), + O( 8),O( 9),O(10),O(11), + O(12),O(12),O(12),O(12), +}; +#undef O + +// rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +// shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 +// mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 +#define O(a) (a) +const UINT8 eg_rate_shift[64] = { + O(12),O(12),O(12),O(12), + O(11),O(11),O(11),O(11), + O(10),O(10),O(10),O(10), + O( 9),O( 9),O( 9),O( 9), + O( 8),O( 8),O( 8),O( 8), + O( 7),O( 7),O( 7),O( 7), + O( 6),O( 6),O( 6),O( 6), + O( 5),O( 5),O( 5),O( 5), + O( 4),O( 4),O( 4),O( 4), + O( 3),O( 3),O( 3),O( 3), + O( 2),O( 2),O( 2),O( 2), + O( 1),O( 1),O( 1),O( 1), + O( 0),O( 0),O( 0),O( 0), + O( 0),O( 0),O( 0),O( 0), + O( 0),O( 0),O( 0),O( 0), + O( 0),O( 0),O( 0),O( 0), +}; +#undef O + + +// number of steps to take in quarter of lfo frequency +// TODO check if frequency matches real chip +#define O(a) ((EG_TIMER_OVERFLOW / a) / 6) +const INT32 lfo_period[8] = { + O(0.168), O(2.019), O(3.196), O(4.206), + O(5.215), O(5.888), O(6.224), O(7.066) +}; +#undef O + + +#define O(a) (a * 65536) +const INT32 vib_depth[8] = { + O(0), O(3.378), O(5.065), O(6.750), + O(10.114), O(20.170), O(40.106), O(79.307) +}; +#undef O + + +#define SC(db) (INT32)(db * (2.0 / ENV_STEP)) +const INT32 am_depth[8] = { + SC(0), SC(1.781), SC(2.906), SC(3.656), + SC(4.406), SC(5.906), SC(7.406), SC(11.91) +}; +#undef SC + +void ymf278b_slot_reset(YMF278BSlot* slot) +{ + slot->wave = slot->FN = slot->OCT = slot->PRVB = slot->LD = slot->TL = slot->pan = + slot->lfo = slot->vib = slot->AM = 0; + slot->AR = slot->D1R = slot->DL = slot->D2R = slot->RC = slot->RR = 0; + slot->step = slot->stepptr = 0; + slot->bits = slot->startaddr = slot->loopaddr = slot->endaddr = 0; + slot->env_vol = MAX_ATT_INDEX; + + slot->lfo_active = 0; + slot->lfo_cnt = slot->lfo_step = 0; + slot->lfo_max = lfo_period[0]; + + slot->state = EG_OFF; + slot->active = 0; + + // not strictly needed, but avoid UMR on savestate + slot->pos = slot->sample1 = slot->sample2 = 0; +} + +INLINE int ymf278b_slot_compute_rate(YMF278BSlot* slot, int val) +{ + int res; + int oct; + + if (val == 0) + return 0; + else if (val == 15) + return 63; + + if (slot->RC != 15) + { + oct = slot->OCT; + + if (oct & 8) + { + oct |= -8; + } + res = (oct + slot->RC) * 2 + (slot->FN & 0x200 ? 1 : 0) + val * 4; + } + else + { + res = val * 4; + } + + if (res < 0) + res = 0; + else if (res > 63) + res = 63; + + return res; +} + +INLINE int ymf278b_slot_compute_vib(YMF278BSlot* slot) +{ + return (((slot->lfo_step << 8) / slot->lfo_max) * vib_depth[slot->vib]) >> 24; +} + + +INLINE int ymf278b_slot_compute_am(YMF278BSlot* slot) +{ + if (slot->lfo_active && slot->AM) + return (((slot->lfo_step << 8) / slot->lfo_max) * am_depth[slot->AM]) >> 12; + else + return 0; +} + +INLINE void ymf278b_slot_set_lfo(YMF278BSlot* slot, int newlfo) +{ + slot->lfo_step = (((slot->lfo_step << 8) / slot->lfo_max) * newlfo) >> 8; + slot->lfo_cnt = (((slot->lfo_cnt << 8) / slot->lfo_max) * newlfo) >> 8; + + slot->lfo = newlfo; + slot->lfo_max = lfo_period[slot->lfo]; +} + +INLINE void ymf278b_advance(YMF278BChip* chip) +{ + YMF278BSlot* op; + int i; + UINT8 rate; + UINT8 shift; + UINT8 select; + + chip->eg_cnt ++; + for (i = 0; i < 24; i ++) + { + op = &chip->slots[i]; + + if (op->lfo_active) + { + op->lfo_cnt ++; + if (op->lfo_cnt < op->lfo_max) + { + op->lfo_step ++; + } + else if (op->lfo_cnt < (op->lfo_max * 3)) + { + op->lfo_step --; + } + else + { + op->lfo_step ++; + if (op->lfo_cnt == (op->lfo_max * 4)) + op->lfo_cnt = 0; + } + } + + // Envelope Generator + switch(op->state) + { + case EG_ATT: // attack phase + rate = ymf278b_slot_compute_rate(op, op->AR); + if (rate < 4) + break; + + shift = eg_rate_shift[rate]; + if (! (chip->eg_cnt & ((1 << shift) - 1))) + { + select = eg_rate_select[rate]; + op->env_vol += (~op->env_vol * eg_inc[select + ((chip->eg_cnt >> shift) & 7)]) >> 3; + if (op->env_vol <= MIN_ATT_INDEX) + { + op->env_vol = MIN_ATT_INDEX; + if (op->DL) + op->state = EG_DEC; + else + op->state = EG_SUS; + } + } + break; + case EG_DEC: // decay phase + rate = ymf278b_slot_compute_rate(op, op->D1R); + if (rate < 4) + break; + + shift = eg_rate_shift[rate]; + if (! (chip->eg_cnt & ((1 << shift) - 1))) + { + select = eg_rate_select[rate]; + op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; + + if ((op->env_vol > dl_tab[6]) && op->PRVB) + op->state = EG_REV; + else + { + if (op->env_vol >= op->DL) + op->state = EG_SUS; + } + } + break; + case EG_SUS: // sustain phase + rate = ymf278b_slot_compute_rate(op, op->D2R); + if (rate < 4) + break; + + shift = eg_rate_shift[rate]; + if (! (chip->eg_cnt & ((1 << shift) - 1))) + { + select = eg_rate_select[rate]; + op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; + + if ((op->env_vol > dl_tab[6]) && op->PRVB) + op->state = EG_REV; + else + { + if (op->env_vol >= MAX_ATT_INDEX) + { + op->env_vol = MAX_ATT_INDEX; + op->active = 0; + } + } + } + break; + case EG_REL: // release phase + rate = ymf278b_slot_compute_rate(op, op->RR); + if (rate < 4) + break; + + shift = eg_rate_shift[rate]; + if (! (chip->eg_cnt & ((1 << shift) - 1))) + { + select = eg_rate_select[rate]; + op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; + + if ((op->env_vol > dl_tab[6]) && op->PRVB) + op->state = EG_REV; + else + { + if (op->env_vol >= MAX_ATT_INDEX) + { + op->env_vol = MAX_ATT_INDEX; + op->active = 0; + } + } + } + break; + case EG_REV: // pseudo reverb + // TODO improve env_vol update + rate = ymf278b_slot_compute_rate(op, 5); + //if (rate < 4) + // break; + + shift = eg_rate_shift[rate]; + if (! (chip->eg_cnt & ((1 << shift) - 1))) + { + select = eg_rate_select[rate]; + op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; + + if (op->env_vol >= MAX_ATT_INDEX) + { + op->env_vol = MAX_ATT_INDEX; + op->active = 0; + } + } + break; + case EG_DMP: // damping + // TODO improve env_vol update, damp is just fastest decay now + rate = 56; + shift = eg_rate_shift[rate]; + if (! (chip->eg_cnt & ((1 << shift) - 1))) + { + select = eg_rate_select[rate]; + op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)]; + + if (op->env_vol >= MAX_ATT_INDEX) + { + op->env_vol = MAX_ATT_INDEX; + op->active = 0; + } + } + break; + case EG_OFF: + // nothing + break; + + default: +#ifdef _DEBUG + //logerror(...); +#endif + break; + } + } +} + +INLINE UINT8 ymf278b_readMem(YMF278BChip* chip, offs_t address) +{ + if (address < chip->ROMSize) + return chip->rom[address]; + else if (address < chip->ROMSize + chip->RAMSize) + return chip->ram[address - chip->ROMSize]; + else + return 255; // TODO check +} + +INLINE UINT8* ymf278b_readMemAddr(YMF278BChip* chip, offs_t address) +{ + if (address < chip->ROMSize) + return &chip->rom[address]; + else if (address < chip->ROMSize + chip->RAMSize) + return &chip->ram[address - chip->ROMSize]; + else + return NULL; // TODO check +} + +INLINE void ymf278b_writeMem(YMF278BChip* chip, offs_t address, UINT8 value) +{ + if (address < chip->ROMSize) + return; // can't write to ROM + else if (address < chip->ROMSize + chip->RAMSize) + chip->ram[address - chip->ROMSize] = value; + else + return; // can't write to unmapped memory + + return; +} + +INLINE INT16 ymf278b_getSample(YMF278BChip* chip, YMF278BSlot* op) +{ + INT16 sample; + UINT32 addr; + UINT8* addrp; + + switch (op->bits) + { + case 0: + // 8 bit + sample = ymf278b_readMem(chip, op->startaddr + op->pos) << 8; + break; + case 1: + // 12 bit + addr = op->startaddr + ((op->pos / 2) * 3); + addrp = ymf278b_readMemAddr(chip, addr); + if (op->pos & 1) + sample = (addrp[2] << 8) | ((addrp[1] << 4) & 0xF0); + else + sample = (addrp[0] << 8) | (addrp[1] & 0xF0); + break; + case 2: + // 16 bit + addr = op->startaddr + (op->pos * 2); + addrp = ymf278b_readMemAddr(chip, addr); + sample = (addrp[0] << 8) | addrp[1]; + break; + default: + // TODO unspecified + sample = 0; + break; + } + return sample; +} + +int ymf278b_anyActive(YMF278BChip* chip) +{ + int i; + + for (i = 0; i < 24; i ++) + { + if (chip->slots[i].active) + return 1; + } + return 0; +} + +void ymf278b_pcm_update(void *_info, stream_sample_t** outputs, int samples) +{ + YMF278BChip* chip = (YMF278BChip *)_info; + int i; + unsigned int j; + INT32 vl; + INT32 vr; + + if (chip->FMEnabled) + { + /* memset is done by ymf262_update */ + ymf262_update_one(chip->fmchip, outputs, samples); + } + else + { + memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); + memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); + } + + if (! ymf278b_anyActive(chip)) + { + // TODO update internal state, even if muted + // TODO also mute individual channels + return; + } + + vl = mix_level[chip->pcm_l]; + vr = mix_level[chip->pcm_r]; + for (j = 0; j < samples; j ++) + { + for (i = 0; i < 24; i ++) + { + YMF278BSlot* sl; + INT16 sample; + int vol; + int volLeft; + int volRight; + + sl = &chip->slots[i]; + if (! sl->active || sl->Muted) + { + //outputs[0][j] += 0; + //outputs[1][j] += 0; + continue; + } + + sample = (sl->sample1 * (0x10000 - sl->stepptr) + + sl->sample2 * sl->stepptr) >> 16; + vol = sl->TL + (sl->env_vol >> 2) + ymf278b_slot_compute_am(sl); + + volLeft = vol + pan_left [sl->pan] + vl; + volRight = vol + pan_right[sl->pan] + vr; + // TODO prob doesn't happen in real chip + //volLeft = std::max(0, volLeft); + //volRight = std::max(0, volRight); + volLeft &= 0x3FF; // catch negative Volume values in a hardware-like way + volRight &= 0x3FF; // (anything beyond 0x100 results in *0) + + outputs[0][j] += (sample * chip->volume[volLeft] ) >> 17; + outputs[1][j] += (sample * chip->volume[volRight]) >> 17; + + if (sl->lfo_active && sl->vib) + { + int oct; + unsigned int step; + + oct = sl->OCT; + if (oct & 8) + oct |= -8; + oct += 5; + step = (sl->FN | 1024) + ymf278b_slot_compute_vib(sl); + if (oct >= 0) + step <<= oct; + else + step >>= -oct; + sl->stepptr += step; + } + else + sl->stepptr += sl->step; + + while (sl->stepptr >= 0x10000) + { + sl->stepptr -= 0x10000; + sl->sample1 = sl->sample2; + sl->pos ++; + if (sl->pos >= sl->endaddr) + sl->pos = sl->loopaddr; + sl->sample2 = ymf278b_getSample(chip, sl); + } + } + ymf278b_advance(chip); + } +} + +INLINE void ymf278b_keyOnHelper(YMF278BChip* chip, YMF278BSlot* slot) +{ + int oct; + unsigned int step; + + slot->active = 1; + + oct = slot->OCT; + if (oct & 8) + oct |= -8; + oct += 5; + step = slot->FN | 1024; + if (oct >= 0) + step <<= oct; + else + step >>= -oct; + slot->step = step; + slot->state = EG_ATT; + slot->stepptr = 0; + slot->pos = 0; + slot->sample1 = ymf278b_getSample(chip, slot); + slot->pos = 1; + slot->sample2 = ymf278b_getSample(chip, slot); +} + +static void ymf278b_A_w(YMF278BChip *chip, UINT8 reg, UINT8 data) +{ + switch(reg) + { + case 0x02: + //chip->timer_a_count = data; + //ymf278b_timer_a_reset(chip); + break; + case 0x03: + //chip->timer_b_count = data; + //ymf278b_timer_b_reset(chip); + break; + case 0x04: + /*if(data & 0x80) + chip->current_irq = 0; + else + { + UINT8 old_enable = chip->enable; + chip->enable = data; + chip->current_irq &= ~data; + if((old_enable ^ data) & 1) + ymf278b_timer_a_reset(chip); + if((old_enable ^ data) & 2) + ymf278b_timer_b_reset(chip); + } + ymf278b_irq_check(chip);*/ + break; + default: +//#ifdef _DEBUG +// logerror("YMF278B: Port A write %02x, %02x\n", reg, data); +//#endif + ymf262_write(chip->fmchip, 1, data); + if ((reg & 0xF0) == 0xB0 && (data & 0x20)) // Key On set + chip->FMEnabled = 0x01; + else if (reg == 0xBD && (data & 0x1F)) // one of the Rhythm bits set + chip->FMEnabled = 0x01; + break; + } +} + +static void ymf278b_B_w(YMF278BChip *chip, UINT8 reg, UINT8 data) +{ + switch(reg) + { + case 0x05: // OPL3/OPL4 Enable + // actually Bit 1 enables OPL4 WaveTable Synth + ymf262_write(chip->fmchip, 3, data & ~0x02); + break; + default: + ymf262_write(chip->fmchip, 3, data); + if ((reg & 0xF0) == 0xB0 && (data & 0x20)) + chip->FMEnabled = 0x01; + break; + } +//#ifdef _DEBUG +// logerror("YMF278B: Port B write %02x, %02x\n", reg, data); +//#endif +} + +void ymf278b_C_w(YMF278BChip* chip, UINT8 reg, UINT8 data) +{ + // Handle slot registers specifically + if (reg >= 0x08 && reg <= 0xF7) + { + int snum = (reg - 8) % 24; + YMF278BSlot* slot = &chip->slots[snum]; + int base; + UINT8* buf; + int oct; + unsigned int step; + + switch((reg - 8) / 24) + { + case 0: + //loadTime = time + LOAD_DELAY; + + slot->wave = (slot->wave & 0x100) | data; + base = (slot->wave < 384 || ! chip->wavetblhdr) ? + (slot->wave * 12) : + (chip->wavetblhdr * 0x80000 + ((slot->wave - 384) * 12)); + buf = ymf278b_readMemAddr(chip, base); + + slot->bits = (buf[0] & 0xC0) >> 6; + ymf278b_slot_set_lfo(slot, (buf[7] >> 3) & 7); + slot->vib = buf[7] & 7; + slot->AR = buf[8] >> 4; + slot->D1R = buf[8] & 0xF; + slot->DL = dl_tab[buf[9] >> 4]; + slot->D2R = buf[9] & 0xF; + slot->RC = buf[10] >> 4; + slot->RR = buf[10] & 0xF; + slot->AM = buf[11] & 7; + slot->startaddr = buf[2] | (buf[1] << 8) | + ((buf[0] & 0x3F) << 16); + slot->loopaddr = buf[4] + (buf[3] << 8); + slot->endaddr = (((buf[6] + (buf[5] << 8)) ^ 0xFFFF) + 1); + + if (chip->regs[reg + 4] & 0x080) + ymf278b_keyOnHelper(chip, slot); + break; + case 1: + slot->wave = (slot->wave & 0xFF) | ((data & 0x1) << 8); + slot->FN = (slot->FN & 0x380) | (data >> 1); + + oct = slot->OCT; + if (oct & 8) + oct |= -8; + oct += 5; + step = slot->FN | 1024; + if (oct >= 0) + step <<= oct; + else + step >>= -oct; + slot->step = step; + break; + case 2: + slot->FN = (slot->FN & 0x07F) | ((data & 0x07) << 7); + slot->PRVB = ((data & 0x08) >> 3); + slot->OCT = ((data & 0xF0) >> 4); + + oct = slot->OCT; + if (oct & 8) + oct |= -8; + oct += 5; + step = slot->FN | 1024; + if (oct >= 0) + step <<= oct; + else + step >>= -oct; + slot->step = step; + break; + case 3: + slot->TL = data >> 1; + slot->LD = data & 0x1; + + // TODO + if (slot->LD) { + // directly change volume + } else { + // interpolate volume + } + break; + case 4: + if (data & 0x10) + { + // output to DO1 pin: + // this pin is not used in moonsound + // we emulate this by muting the sound + slot->pan = 8; // both left/right -inf dB + } + else + slot->pan = data & 0x0F; + + if (data & 0x020) + { + // LFO reset + slot->lfo_active = 0; + slot->lfo_cnt = 0; + slot->lfo_max = lfo_period[slot->vib]; + slot->lfo_step = 0; + } + else + { + // LFO activate + slot->lfo_active = 1; + } + + switch (data >> 6) + { + case 0: // tone off, no damp + if (slot->active && (slot->state != EG_REV)) + slot->state = EG_REL; + break; + case 2: // tone on, no damp + if (! (chip->regs[reg] & 0x080)) + ymf278b_keyOnHelper(chip, slot); + break; + case 1: // tone off, damp + case 3: // tone on, damp + slot->state = EG_DMP; + break; + } + break; + case 5: + slot->vib = data & 0x7; + ymf278b_slot_set_lfo(slot, (data >> 3) & 0x7); + break; + case 6: + slot->AR = data >> 4; + slot->D1R = data & 0xF; + break; + case 7: + slot->DL = dl_tab[data >> 4]; + slot->D2R = data & 0xF; + break; + case 8: + slot->RC = data >> 4; + slot->RR = data & 0xF; + break; + case 9: + slot->AM = data & 0x7; + break; + } + } + else + { + // All non-slot registers + switch (reg) + { + case 0x00: // TEST + case 0x01: + break; + + case 0x02: + chip->wavetblhdr = (data >> 2) & 0x7; + chip->memmode = data & 1; + break; + + case 0x03: + chip->memadr = (chip->memadr & 0x00FFFF) | (data << 16); + break; + + case 0x04: + chip->memadr = (chip->memadr & 0xFF00FF) | (data << 8); + break; + + case 0x05: + chip->memadr = (chip->memadr & 0xFFFF00) | data; + break; + + case 0x06: // memory data + //busyTime = time + MEM_WRITE_DELAY; + ymf278b_writeMem(chip, chip->memadr, data); + chip->memadr = (chip->memadr + 1) & 0xFFFFFF; + break; + + case 0xF8: + // TODO use these + chip->fm_l = data & 0x7; + chip->fm_r = (data >> 3) & 0x7; + break; + + case 0xF9: + chip->pcm_l = data & 0x7; + chip->pcm_r = (data >> 3) & 0x7; + break; + } + } + + chip->regs[reg] = data; +} + +UINT8 ymf278b_readReg(YMF278BChip* chip, UINT8 reg) +{ + // no need to call updateStream(time) + UINT8 result; + switch(reg) + { + case 2: // 3 upper bits are device ID + result = (chip->regs[2] & 0x1F) | 0x20; + break; + + case 6: // Memory Data Register + //busyTime = time + MEM_READ_DELAY; + result = ymf278b_readMem(chip, chip->memadr); + chip->memadr = (chip->memadr + 1) & 0xFFFFFF; + break; + + default: + result = chip->regs[reg]; + break; + } + + return result; +} + +UINT8 ymf278b_peekReg(YMF278BChip* chip, UINT8 reg) +{ + UINT8 result; + + switch(reg) + { + case 2: // 3 upper bits are device ID + result = (chip->regs[2] & 0x1F) | 0x20; + break; + + case 6: // Memory Data Register + result = ymf278b_readMem(chip, chip->memadr); + break; + + default: + result = chip->regs[reg]; + break; + } + return result; +} + +UINT8 ymf278b_readStatus(YMF278BChip* chip) +{ + UINT8 result = 0; + //if (time < busyTime) + // result |= 0x01; + //if (time < loadTime) + // result |= 0x02; + return result; +} + +//WRITE8_DEVICE_HANDLER( ymf278b_w ) +void ymf278b_w(void *_info, offs_t offset, UINT8 data) +{ + //YMF278BChip *chip = get_safe_token(device); + YMF278BChip *chip = (YMF278BChip *)_info; + + switch (offset) + { + case 0: + chip->port_A = data; + ymf262_write(chip->fmchip, offset, data); + break; + + case 1: + ymf278b_A_w(chip, chip->port_A, data); + break; + + case 2: + chip->port_B = data; + ymf262_write(chip->fmchip, offset, data); + break; + + case 3: + ymf278b_B_w(chip, chip->port_B, data); + break; + + case 4: + chip->port_C = data; + break; + + case 5: + ymf278b_C_w(chip, chip->port_C, data); + break; + + default: +#ifdef _DEBUG + logerror("YMF278B: unexpected write at offset %X to ymf278b = %02X\n", offset, data); +#endif + break; + } +} + +void ymf278b_clearRam(YMF278BChip* chip) +{ + memset(chip->ram, 0, chip->RAMSize); +} + +static void ymf278b_load_rom(YMF278BChip *chip) +{ + chip->ROMSize = 0x00200000; + chip->rom = (UINT8*)malloc(chip->ROMSize); + memset(chip->rom, 0xFF, chip->ROMSize); + + return; +} + +static int ymf278b_init(YMF278BChip *chip, int clock, void (*cb)(int)) +{ + int rate; + + rate = clock / 768; + //if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) || + // CHIP_SAMPLING_MODE == 0x02) + // rate = CHIP_SAMPLE_RATE; + chip->fmchip = ymf262_init(clock * 8 / 19, rate); + chip->FMEnabled = 0x00; + + chip->rom = NULL; + chip->irq_callback = cb; + //chip->timer_a = timer_alloc(device->machine, ymf278b_timer_a_tick, chip); + //chip->timer_b = timer_alloc(device->machine, ymf278b_timer_b_tick, chip); + chip->clock = clock; + + ymf278b_load_rom(chip); + chip->RAMSize = 0x00080000; + chip->ram = (UINT8*)malloc(chip->RAMSize); + ymf278b_clearRam(chip); + + return rate; +} + +//static DEVICE_START( ymf278b ) +int device_start_ymf278b(void **_info, int clock) +{ + static const ymf278b_interface defintrf = { 0 }; + const ymf278b_interface *intf; + int i; + YMF278BChip *chip; + int rate; + + chip = (YMF278BChip *) calloc(1, sizeof(YMF278BChip)); + *_info = (void *) chip; + + //chip->device = device; + //intf = (device->static_config != NULL) ? (const ymf278b_interface *)device->static_config : &defintrf; + intf = &defintrf; + + rate = ymf278b_init(chip, clock, intf->irq_callback); + //chip->stream = stream_create(device, 0, 2, device->clock/768, chip, ymf278b_pcm_update); + + chip->memadr = 0; // avoid UMR + + // Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB + for (i = 0; i < 256; i ++) + chip->volume[i] = 32768 * pow(2.0, (-0.375 / 6) * i); + for (i = 256; i < 256 * 4; i ++) + chip->volume[i] = 0; + for (i = 0; i < 24; i ++) + chip->slots[i].Muted = 0x00;; + + return rate; +} + +//static DEVICE_STOP( ymf278 ) +void device_stop_ymf278b(void *_info) +{ + YMF278BChip* chip = (YMF278BChip *)_info; + + ymf262_shutdown(chip->fmchip); + free(chip->rom); chip->rom = NULL; + + free(chip); + + return; +} + +void device_reset_ymf278b(void *_info) +{ + YMF278BChip* chip = (YMF278BChip *)_info; + int i; + + ymf262_reset_chip(chip->fmchip); + chip->FMEnabled = 0x00; + + chip->eg_cnt = 0; + + for (i = 0; i < 24; i ++) + ymf278b_slot_reset(&chip->slots[i]); + for (i = 255; i >= 0; i --) // reverse order to avoid UMR + ymf278b_C_w(chip, i, 0); + + chip->wavetblhdr = chip->memmode = chip->memadr = 0; + chip->fm_l = chip->fm_r = chip->pcm_l = chip->pcm_r = 0; + //busyTime = time; + //loadTime = time; +} + +void ymf278b_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData) +{ + YMF278BChip *chip = (YMF278BChip *)_info; + + if (chip->ROMSize != ROMSize) + { + chip->rom = (UINT8*)realloc(chip->rom, ROMSize); + chip->ROMSize = ROMSize; + memset(chip->rom, 0xFF, ROMSize); + } + if (DataStart > ROMSize) + return; + if (DataStart + DataLength > ROMSize) + DataLength = ROMSize - DataStart; + + memcpy(chip->rom + DataStart, ROMData, DataLength); + + return; +} + + +void ymf278b_set_mute_mask(void *_info, UINT32 MuteMaskFM, UINT32 MuteMaskWT) +{ + YMF278BChip *chip = (YMF278BChip *)_info; + UINT8 CurChn; + + ymf262_set_mutemask(chip->fmchip, MuteMaskFM); + for (CurChn = 0; CurChn < 24; CurChn ++) + chip->slots[CurChn].Muted = (MuteMaskWT >> CurChn) & 0x01; + + return; +} diff --git a/Frameworks/GME/vgmplay/chips/ymf278b.h b/Frameworks/GME/vgmplay/chips/ymf278b.h new file mode 100644 index 000000000..8cbff7187 --- /dev/null +++ b/Frameworks/GME/vgmplay/chips/ymf278b.h @@ -0,0 +1,30 @@ +#pragma once + +#define YMF278B_STD_CLOCK (33868800) /* standard clock for OPL4 */ + + +typedef struct _ymf278b_interface ymf278b_interface; +struct _ymf278b_interface +{ + //void (*irq_callback)(const device_config *device, int state); /* irq callback */ + void (*irq_callback)(int state); /* irq callback */ +}; + +/*READ8_DEVICE_HANDLER( ymf278b_r ); +WRITE8_DEVICE_HANDLER( ymf278b_w ); + +DEVICE_GET_INFO( ymf278b ); +#define SOUND_YMF278B DEVICE_GET_INFO_NAME( ymf278b )*/ + +void ymf278b_pcm_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_ymf278b(void **chip, int clock); +void device_stop_ymf278b(void *chip); +void device_reset_ymf278b(void *chip); + +UINT8 ymf278b_r(void *chip, offs_t offset); +void ymf278b_w(void *chip, offs_t offset, UINT8 data); +void ymf278b_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, + const UINT8* ROMData); + +void ymf278b_set_mute_mask(void *chip, UINT32 MuteMaskFM, UINT32 MuteMaskWT); + diff --git a/Frameworks/GME/gme/ymz280b.c b/Frameworks/GME/vgmplay/chips/ymz280b.c similarity index 88% rename from Frameworks/GME/gme/ymz280b.c rename to Frameworks/GME/vgmplay/chips/ymz280b.c index c331f4ff8..8e2a91e8b 100644 --- a/Frameworks/GME/gme/ymz280b.c +++ b/Frameworks/GME/vgmplay/chips/ymz280b.c @@ -18,9 +18,16 @@ 256 steps total level and 16 steps panpot can be set Voice signal is output in stereo 16-bit 2's complement MSB-first format + TODO: + - Is memory handling 100% correct? At the moment, Konami firebeat.c is the only + hardware currently emulated that uses external handlers. + It also happens to be the only one using 16-bit PCM. + + Some other drivers (eg. bishi.c, bfm_sc4/5.c) also use ROM readback. + */ -#define _USE_MATH_DEFINES + #include #include "mamedef.h" @@ -33,6 +40,8 @@ #include #include "ymz280b.h" +#define NULL ((void *)0) + static void update_irq_state_timer_common(void *param, int voicenum); //unsigned char DISABLE_YMZ_FIX = 0x00; @@ -106,12 +115,17 @@ struct _ymz280b_state UINT8 irq_mask; /* current IRQ mask */ UINT8 irq_enable; /* current IRQ enable */ UINT8 keyon_enable; /* key on enable */ + UINT8 ext_mem_enable; /* external memory enable */ + UINT8 ext_readlatch; /* external memory prefetched data */ + UINT32 ext_mem_address_hi; + UINT32 ext_mem_address_mid; + UINT32 ext_mem_address; /* where the CPU can read the ROM */ + double master_clock; /* master clock frequency */ double rate; //void (*irq_callback)(const device_config *, int); /* IRQ callback */ void (*irq_callback)(int); /* IRQ callback */ struct YMZ280BVoice voice[8]; /* the 8 voices */ - UINT32 rom_readback_addr; /* where the CPU can read the ROM */ //devcb_resolved_read8 ext_ram_read; /* external RAM read handler */ //devcb_resolved_write8 ext_ram_write; /* external RAM write handler */ @@ -228,6 +242,22 @@ INLINE void update_volumes(struct YMZ280BVoice *voice) } +//static STATE_POSTLOAD( YMZ280B_state_save_update_step ) +/*void YMZ280B_state_save_update_step(void *param) +{ + ymz280b_state *chip = (ymz280b_state *)param; + int j; + for (j = 0; j < 8; j++) + { + struct YMZ280BVoice *voice = &chip->voice[j]; + update_step(chip, voice); + if(voice->irq_schedule) + // timer_set(machine, attotime_zero, chip, 0, update_irq_state_cb[j]); + update_irq_state_timer_common(param, j); + } +}*/ + + INLINE UINT8 ymz280b_read_memory(UINT8 *base, UINT32 size, UINT32 offset) { offset &= 0xFFFFFF; @@ -299,7 +329,7 @@ static void compute_tables(void) static int generate_adpcm(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { - UINT32 position = voice->position; + int position = voice->position; int signal = voice->signal; int step = voice->step; int val; @@ -426,8 +456,8 @@ static int generate_adpcm(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, static int generate_pcm8(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { + int position = voice->position; int val; - UINT32 position = voice->position; /*if (! DISABLE_YMZ_FIX) { @@ -511,8 +541,8 @@ static int generate_pcm8(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, I static int generate_pcm16(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { + int position = voice->position; int val; - UINT32 position = voice->position; /*if (! DISABLE_YMZ_FIX) { @@ -535,6 +565,7 @@ static int generate_pcm16(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, /* fetch the current value */ //val = (INT16)((base[position / 2 + 1] << 8) + base[position / 2 + 0]); val = (INT16)((ymz280b_read_memory(base, size, position / 2 + 0) << 8) + ymz280b_read_memory(base, size, position / 2 + 1)); + // Note: Last MAME updates say it's: ((position / 2 + 1) << 8) + (position / 2 + 0); /* output to the buffer, scaling by the volume */ *buffer++ = val; @@ -598,10 +629,9 @@ static int generate_pcm16(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, ***********************************************************************************************/ //static STREAM_UPDATE( ymz280b_update ) -void ymz280b_update(void *_chip, stream_sample_t **outputs, int samples) +void ymz280b_update(void *param, stream_sample_t **outputs, int samples) { - //ymz280b_state *chip = (ymz280b_state *)param; - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)param; stream_sample_t *lacc = outputs[0]; stream_sample_t *racc = outputs[1]; int v; @@ -685,8 +715,7 @@ void ymz280b_update(void *_chip, stream_sample_t **outputs, int samples) { /* note: samples_left bit 16 is set if the voice was finished at the same time the function ended */ int base; - UINT32 i; - int t; + int i, t; samples_left &= 0xffff; base = new_samples - samples_left; @@ -770,7 +799,7 @@ void ymz280b_update(void *_chip, stream_sample_t **outputs, int samples) ***********************************************************************************************/ //static DEVICE_START( ymz280b ) -void * device_start_ymz280b(int clock) +int device_start_ymz280b(void **_info, int clock) { static const ymz280b_interface defintrf = { 0 }; //const ymz280b_interface *intf = (device->static_config != NULL) ? (const ymz280b_interface *)device->static_config : &defintrf; @@ -780,6 +809,8 @@ void * device_start_ymz280b(int clock) int chn; chip = (ymz280b_state *) calloc(1, sizeof(ymz280b_state)); + *_info = (void *) chip; + //chip->device = device; //devcb_resolve_read8(&chip->ext_ram_read, &intf->ext_read, device); //devcb_resolve_write8(&chip->ext_ram_write, &intf->ext_write, device); @@ -792,7 +823,7 @@ void * device_start_ymz280b(int clock) chip->rate = chip->master_clock * 2.0; // disabled until the frequency calculation gets fixed - /*if ((CHIP_SAMPLING_MODE == 0x01 && chip->rate < CHIP_SAMPLE_RATE) || + /*if (((CHIP_SAMPLING_MODE & 0x01) && chip->rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) chip->rate = (double)CHIP_SAMPLE_RATE;*/ @@ -850,6 +881,8 @@ void * device_start_ymz280b(int clock) for (chn = 0; chn < 8; chn ++) chip->voice[chn].Muted = 0x00; + //state_save_register_postload(device->machine, YMZ280B_state_save_update_step, chip); + #if MAKE_WAVS chip->wavresample = wav_open("resamp.wav", INTERNAL_SAMPLE_RATE, 2); #endif @@ -870,15 +903,15 @@ void * device_start_ymz280b(int clock) } } #endif - - return chip; //return (int)INTERNAL_SAMPLE_RATE; + + return (int)INTERNAL_SAMPLE_RATE; } //static DEVICE_STOP( ymz280b ) -void device_stop_ymz280b(void *_chip) +void device_stop_ymz280b(void *_info) { //ymz280b_state *chip = get_safe_token(device); - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)_info; free(chip->region_base); chip->region_base = NULL; free(chip->scratch); @@ -892,14 +925,16 @@ void device_stop_ymz280b(void *_chip) } } #endif - + free(chip); + + return; } //static DEVICE_RESET( ymz280b ) -void device_reset_ymz280b(void *_chip) +void device_reset_ymz280b(void *_info) { - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)_info; /*struct YMZ280BVoice *voice; unsigned char curvoc; @@ -909,7 +944,7 @@ void device_reset_ymz280b(void *_chip) chip->irq_mask = 0x00; chip->irq_enable = 0x00; chip->keyon_enable = 0x00; - chip->rom_readback_addr = 0x000000; + chip->ext_mem_address = 0x000000; for (curvoc = 0; curvoc < 8; curvoc ++) { voice = &chip->voice[curvoc]; @@ -955,6 +990,8 @@ void device_reset_ymz280b(void *_chip) voice->output_pos = FRAC_ONE; voice->playing = 0; } + + return; } @@ -1120,30 +1157,32 @@ static void write_to_register(ymz280b_state *chip, int data) case 0x80: // d0-2: DSP Rch, d3: enable Rch (0: yes, 1: no), d4-6: DSP Lch, d7: enable Lch (0: yes, 1: no) case 0x81: // d0: enable control of $82 (0: yes, 1: no) case 0x82: // DSP data - /*logerror("YMZ280B: DSP register write %02X = %02X\n", chip->current_register, data);*/ + logerror("YMZ280B: DSP register write %02X = %02X\n", chip->current_register, data); break; case 0x84: /* ROM readback / RAM write (high) */ - chip->rom_readback_addr &= 0xffff; - chip->rom_readback_addr |= (data<<16); + chip->ext_mem_address_hi = data << 16; break; - case 0x85: /* ROM readback / RAM write (med) */ - chip->rom_readback_addr &= 0xff00ff; - chip->rom_readback_addr |= (data<<8); + case 0x85: /* ROM readback / RAM write (middle) */ + chip->ext_mem_address_mid = data << 8; break; - case 0x86: /* ROM readback / RAM write (low) */ - chip->rom_readback_addr &= 0xffff00; - chip->rom_readback_addr |= data; + case 0x86: /* ROM readback / RAM write (low) -> update latch */ + chip->ext_mem_address = chip->ext_mem_address_hi | chip->ext_mem_address_mid | data; + if (chip->ext_mem_enable) + chip->ext_readlatch = ymz280b_read_memory(chip->region_base, chip->region_size, chip->ext_mem_address); break; case 0x87: /* RAM write */ - /*if (!chip->ext_ram_write.isnull()) - chip->ext_ram_write(chip->rom_readback_addr, data); - else - logerror("YMZ280B attempted RAM write to %X\n", chip->rom_readback_addr);*/ - chip->rom_readback_addr = (chip->rom_readback_addr + 1) & 0xffffff; + if (chip->ext_mem_enable) + { + /*if (!chip->ext_ram_write.isnull()) + chip->ext_ram_write(chip->ext_mem_address, data); + else + logerror("YMZ280B attempted RAM write to %X\n", chip->ext_mem_address);*/ + chip->ext_mem_address = (chip->ext_mem_address + 1) & 0xffffff; + } break; case 0xfe: /* IRQ mask */ @@ -1152,6 +1191,7 @@ static void write_to_register(ymz280b_state *chip, int data) break; case 0xff: /* IRQ enable, test, etc */ + chip->ext_mem_enable = (data & 0x40) >> 6; chip->irq_enable = (data & 0x10) >> 4; update_irq_state(chip); @@ -1197,15 +1237,6 @@ static int compute_status(ymz280b_state *chip) { UINT8 result; - /* ROM/RAM readback? */ - if (chip->current_register == 0x86) - { - //result = chip->region_base[chip->rom_readback_addr]; - result = ymz280b_read_memory(chip->region_base, chip->region_size, chip->rom_readback_addr); - chip->rom_readback_addr = (chip->rom_readback_addr + 1) & 0xffffff; - return result; - } - /* force an update */ //stream_update(chip->stream); @@ -1227,23 +1258,23 @@ static int compute_status(ymz280b_state *chip) ***********************************************************************************************/ //READ8_DEVICE_HANDLER( ymz280b_r ) -UINT8 ymz280b_r(void *_chip, offs_t offset) +UINT8 ymz280b_r(void *_info, offs_t offset) { //ymz280b_state *chip = get_safe_token(device); - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)_info; if ((offset & 1) == 0) { - /* read from external memory */ - UINT8 result; - /*if (chip->ext_ram_read.isnull()) - result = chip->ext_ram_read(chip->rom_readback_addr); - else - result = ymz280b_read_memory(chip->region_base, chip->region_size, chip->rom_readback_addr);*/ - result = 0x00; + UINT8 ret; + + if (! chip->ext_mem_enable) + return 0xff; - chip->rom_readback_addr = (chip->rom_readback_addr + 1) & 0xffffff; - return result; + /* read from external memory */ + ret = chip->ext_readlatch; + ret = ymz280b_read_memory(chip->region_base, chip->region_size, chip->ext_mem_address); + chip->ext_mem_address = (chip->ext_mem_address + 1) & 0xffffff; + return ret; } else { @@ -1253,10 +1284,10 @@ UINT8 ymz280b_r(void *_chip, offs_t offset) //WRITE8_DEVICE_HANDLER( ymz280b_w ) -void ymz280b_w(void *_chip, offs_t offset, UINT8 data) +void ymz280b_w(void *_info, offs_t offset, UINT8 data) { //ymz280b_state *chip = get_safe_token(device); - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)_info; if ((offset & 1) == 0) chip->current_register = data; @@ -1269,10 +1300,10 @@ void ymz280b_w(void *_chip, offs_t offset, UINT8 data) } } -void ymz280b_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +void ymz280b_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)_info; if (chip->region_size != ROMSize) { @@ -1291,9 +1322,9 @@ void ymz280b_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t Dat } -void ymz280b_set_mute_mask(void *_chip, UINT32 MuteMask) +void ymz280b_set_mute_mask(void *_info, UINT32 MuteMask) { - ymz280b_state *chip = (ymz280b_state *) _chip; + ymz280b_state *chip = (ymz280b_state *)_info; UINT8 CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) @@ -1301,3 +1332,31 @@ void ymz280b_set_mute_mask(void *_chip, UINT32 MuteMask) return; } + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +/*DEVICE_GET_INFO( ymz280b ) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ymz280b_state); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymz280b ); break; + case DEVINFO_FCT_STOP: // Nothing break; + case DEVINFO_FCT_RESET: info->start = DEVICE_RESET_NAME( ymz280b ); break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case DEVINFO_STR_NAME: strcpy(info->s, "YMZ280B"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha Wavetable"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +}*/ + diff --git a/Frameworks/GME/gme/ymz280b.h b/Frameworks/GME/vgmplay/chips/ymz280b.h similarity index 61% rename from Frameworks/GME/gme/ymz280b.h rename to Frameworks/GME/vgmplay/chips/ymz280b.h index d649dae2f..2c28afd20 100644 --- a/Frameworks/GME/gme/ymz280b.h +++ b/Frameworks/GME/vgmplay/chips/ymz280b.h @@ -7,14 +7,8 @@ #pragma once -#include "mamedef.h" - //#include "devcb.h" -#ifdef __cplusplus -extern "C" { -#endif - typedef struct _ymz280b_interface ymz280b_interface; struct _ymz280b_interface { @@ -30,18 +24,14 @@ WRITE8_DEVICE_HANDLER( ymz280b_w ); DEVICE_GET_INFO( ymz280b ); #define SOUND_YMZ280B DEVICE_GET_INFO_NAME( ymz280b )*/ -void ymz280b_update(void *, stream_sample_t **outputs, int samples); -void * device_start_ymz280b(int clock); -void device_stop_ymz280b(void *); -void device_reset_ymz280b(void *); +void ymz280b_update(void *chip, stream_sample_t **outputs, int samples); +int device_start_ymz280b(void **chip, int clock); +void device_stop_ymz280b(void *chip); +void device_reset_ymz280b(void *chip); -UINT8 ymz280b_r(void *, offs_t offset); -void ymz280b_w(void *, offs_t offset, UINT8 data); -void ymz280b_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength, +UINT8 ymz280b_r(void *chip, offs_t offset); +void ymz280b_w(void *chip, offs_t offset, UINT8 data); +void ymz280b_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); -void ymz280b_set_mute_mask(void *, UINT32 MuteMask); - -#ifdef __cplusplus -} -#endif +void ymz280b_set_mute_mask(void *chip, UINT32 MuteMask); diff --git a/Frameworks/GME/vgmplay/licenses/GPL.txt b/Frameworks/GME/vgmplay/licenses/GPL.txt new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/Frameworks/GME/vgmplay/licenses/GPL.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Frameworks/GME/vgmplay/licenses/List.txt b/Frameworks/GME/vgmplay/licenses/List.txt new file mode 100644 index 000000000..ad2d7cb46 --- /dev/null +++ b/Frameworks/GME/vgmplay/licenses/List.txt @@ -0,0 +1,11 @@ +MAME - mame_license.txt +DosBox - GPL.txt +openMSX - GPL.txt +Gens/GS - GPL.txt +NSFPlay - [Google Code lists Apache License 2.0] +Ootake - ? +in_vgm - ? +MEKA - ? +zlib - see zlib.h +in_wsr - ? +vbjin/mednafen - GNU GPLv2 diff --git a/Frameworks/GME/vgmplay/licenses/mame_license.txt b/Frameworks/GME/vgmplay/licenses/mame_license.txt new file mode 100644 index 000000000..33267a54c --- /dev/null +++ b/Frameworks/GME/vgmplay/licenses/mame_license.txt @@ -0,0 +1,35 @@ +Unless otherwise explicitly stated, all code in MAME is released under the +following license: + +Copyright Nicola Salmoria and the MAME team +All rights reserved. + +Redistribution and use of this code or any derivative works are permitted +provided that the following conditions are met: + +* Redistributions may not be sold, nor may they be used in a commercial +product or activity. + +* Redistributions that are modified from the original source must include the +complete source code, including the source code for all components used by a +binary built from the modified sources. However, as a special exception, the +source code distributed need not include anything that is normally distributed +(in either source or binary form) with the major components (compiler, kernel, +and so on) of the operating system on which the executable runs, unless that +component itself accompanies the executable. + +* Redistributions must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Frameworks/GME/vgmplay/pt_ioctl.c b/Frameworks/GME/vgmplay/pt_ioctl.c new file mode 100644 index 000000000..7d04d6486 --- /dev/null +++ b/Frameworks/GME/vgmplay/pt_ioctl.c @@ -0,0 +1,251 @@ +/******************************************************************************/ +/* */ +/* IoExample for PortTalk V2.1 */ +/* Version 2.1, 12th January 2002 */ +/* http://www.beyondlogic.org */ +/* */ +/* Copyright © 2002 Craig Peacock. Craig.Peacock@beyondlogic.org */ +/* Any publication or distribution of this code in source form is prohibited */ +/* without prior written permission of the copyright holder. This source code */ +/* is provided "as is", without any guarantee made as to its suitability or */ +/* fitness for any particular use. Permission is herby granted to modify or */ +/* enhance this sample code to produce a derivative program which may only be */ +/* distributed in compiled object form only. */ +/******************************************************************************/ + +#include +#include +#include +#include "PortTalk_IOCTL.h" + +unsigned char OpenPortTalk(void); +void ClosePortTalk(void); + +void outportb(unsigned short PortAddress, unsigned char byte); +unsigned char inportb(unsigned short PortAddress); + +void InstallPortTalkDriver(void); +unsigned char StartPortTalkDriver(void); + +#define inp(PortAddress) inportb(PortAddress) +#define outp(PortAddress, Value) outportb(PortAddress, Value) + +HANDLE PortTalk_Handle; /* Handle for PortTalk Driver */ + +void outportb(unsigned short PortAddress, unsigned char byte) +{ + unsigned int error; + DWORD BytesReturned; + unsigned char Buffer[3]; + unsigned short * pBuffer; + pBuffer = (unsigned short *)&Buffer[0]; + *pBuffer = PortAddress; + Buffer[2] = byte; + + error = DeviceIoControl(PortTalk_Handle, + IOCTL_WRITE_PORT_UCHAR, + &Buffer, + 3, + NULL, + 0, + &BytesReturned, + NULL); + + if (!error) printf("Error occured during outportb while talking to PortTalk driver %d\n",GetLastError()); +} + +unsigned char inportb(unsigned short PortAddress) +{ + unsigned int error; + DWORD BytesReturned; + unsigned char Buffer[3]; + unsigned short * pBuffer; + pBuffer = (unsigned short *)&Buffer; + *pBuffer = PortAddress; + + error = DeviceIoControl(PortTalk_Handle, + IOCTL_READ_PORT_UCHAR, + &Buffer, + 2, + &Buffer, + 1, + &BytesReturned, + NULL); + + if (!error) printf("Error occured during inportb while talking to PortTalk driver %d\n",GetLastError()); + return(Buffer[0]); +} + +unsigned char OpenPortTalk(void) +{ + /* Open PortTalk Driver. If we cannot open it, try installing and starting it */ + PortTalk_Handle = CreateFile("\\\\.\\PortTalk", + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(PortTalk_Handle == INVALID_HANDLE_VALUE) { + /* Start or Install PortTalk Driver */ + StartPortTalkDriver(); + /* Then try to open once more, before failing */ + PortTalk_Handle = CreateFile("\\\\.\\PortTalk", + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(PortTalk_Handle == INVALID_HANDLE_VALUE) { + printf("PortTalk: Couldn't access PortTalk Driver, Please ensure driver is loaded.\n\n"); + return -1; + } + } + return 0; +} + +void ClosePortTalk(void) +{ + CloseHandle(PortTalk_Handle); +} + +unsigned char StartPortTalkDriver(void) +{ + SC_HANDLE SchSCManager; + SC_HANDLE schService; + BOOL ret; + DWORD err; + + /* Open Handle to Service Control Manager */ + SchSCManager = OpenSCManager (NULL, /* machine (NULL == local) */ + NULL, /* database (NULL == default) */ + SC_MANAGER_ALL_ACCESS); /* access required */ + + if (SchSCManager == NULL) + if (GetLastError() == ERROR_ACCESS_DENIED) { + /* We do not have enough rights to open the SCM, therefore we must */ + /* be a poor user with only user rights. */ + printf("PortTalk: You do not have rights to access the Service Control Manager and\n"); + printf("PortTalk: the PortTalk driver is not installed or started. Please ask \n"); + printf("PortTalk: your administrator to install the driver on your behalf.\n"); + return(0); + } + + do { + /* Open a Handle to the PortTalk Service Database */ + schService = OpenService(SchSCManager, /* handle to service control manager database */ + "PortTalk", /* pointer to name of service to start */ + SERVICE_ALL_ACCESS); /* type of access to service */ + + if (schService == NULL) + switch (GetLastError()){ + case ERROR_ACCESS_DENIED: + printf("PortTalk: You do not have rights to the PortTalk service database\n"); + return(0); + case ERROR_INVALID_NAME: + printf("PortTalk: The specified service name is invalid.\n"); + return(0); + case ERROR_SERVICE_DOES_NOT_EXIST: + printf("PortTalk: The PortTalk driver does not exist. Installing driver.\n"); + printf("PortTalk: This can take up to 30 seconds on some machines . .\n"); + InstallPortTalkDriver(); + break; + } + } while (schService == NULL); + + /* Start the PortTalk Driver. Errors will occur here if PortTalk.SYS file doesn't exist */ + + ret = StartService (schService, /* service identifier */ + 0, /* number of arguments */ + NULL); /* pointer to arguments */ + + if (ret) printf("PortTalk: The PortTalk driver has been successfully started.\n"); + else { + err = GetLastError(); + if (err == ERROR_SERVICE_ALREADY_RUNNING) + printf("PortTalk: The PortTalk driver is already running.\n"); + else { + printf("PortTalk: Unknown error while starting PortTalk driver service.\n"); + printf("PortTalk: Does PortTalk.SYS exist in your \\System32\\Drivers Directory?\n"); + return(0); + } + } + + /* Close handle to Service Control Manager */ + CloseServiceHandle (schService); + return(TRUE); +} + +void InstallPortTalkDriver(void) +{ + SC_HANDLE SchSCManager; + SC_HANDLE schService; + DWORD err; + CHAR DriverFileName[80]; + + /* Get Current Directory. Assumes PortTalk.SYS driver is in this directory. */ + /* Doesn't detect if file exists, nor if file is on removable media - if this */ + /* is the case then when windows next boots, the driver will fail to load and */ + /* a error entry is made in the event viewer to reflect this */ + + /* Get System Directory. This should be something like c:\windows\system32 or */ + /* c:\winnt\system32 with a Maximum Character lenght of 20. As we have a */ + /* buffer of 80 bytes and a string of 24 bytes to append, we can go for a max */ + /* of 55 bytes */ + + if (!GetSystemDirectory(DriverFileName, 55)) + { + printf("PortTalk: Failed to get System Directory. Is System Directory Path > 55 Characters?\n"); + printf("PortTalk: Please manually copy driver to your system32/driver directory.\n"); + } + + /* Append our Driver Name */ + lstrcat(DriverFileName,"\\Drivers\\PortTalk.sys"); + printf("PortTalk: Copying driver to %s\n",DriverFileName); + + /* Copy Driver to System32/drivers directory. This fails if the file doesn't exist. */ + + if (!CopyFile("PortTalk.sys", DriverFileName, FALSE)) + { + printf("PortTalk: Failed to copy driver to %s\n",DriverFileName); + printf("PortTalk: Please manually copy driver to your system32/driver directory.\n"); + } + + /* Open Handle to Service Control Manager */ + SchSCManager = OpenSCManager (NULL, /* machine (NULL == local) */ + NULL, /* database (NULL == default) */ + SC_MANAGER_ALL_ACCESS); /* access required */ + + /* Create Service/Driver - This adds the appropriate registry keys in */ + /* HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services - It doesn't */ + /* care if the driver exists, or if the path is correct. */ + + schService = CreateService (SchSCManager, /* SCManager database */ + "PortTalk", /* name of service */ + "PortTalk", /* name to display */ + SERVICE_ALL_ACCESS, /* desired access */ + SERVICE_KERNEL_DRIVER, /* service type */ + SERVICE_DEMAND_START, /* start type */ + SERVICE_ERROR_NORMAL, /* error control type */ + "System32\\Drivers\\PortTalk.sys", /* service's binary */ + NULL, /* no load ordering group */ + NULL, /* no tag identifier */ + NULL, /* no dependencies */ + NULL, /* LocalSystem account */ + NULL /* no password */ + ); + + if (schService == NULL) { + err = GetLastError(); + if (err == ERROR_SERVICE_EXISTS) + printf("PortTalk: Driver already exists. No action taken.\n"); + else printf("PortTalk: Unknown error while creating Service.\n"); + } + else printf("PortTalk: Driver successfully installed.\n"); + + /* Close Handle to Service Control Manager */ + CloseServiceHandle (schService); +} diff --git a/Frameworks/GME/vgmplay/resampler.c b/Frameworks/GME/vgmplay/resampler.c new file mode 100644 index 000000000..168e51764 --- /dev/null +++ b/Frameworks/GME/vgmplay/resampler.c @@ -0,0 +1,355 @@ +#include "resampler.h" + +#include +#include +#include + +/* Copyright (C) 2004-2008 Shay Green. + Copyright (C) 2015 Christopher Snowhill. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +enum { imp_scale = 0x7FFF }; +typedef int16_t imp_t; +typedef int32_t imp_off_t; /* for max_res of 512 and impulse width of 32, end offsets must be 32 bits */ + +#if RESAMPLER_BITS == 16 +typedef int32_t intermediate_t; +#elif RESAMPLER_BITS == 32 +typedef int64_t intermediate_t; +#endif + +static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, + int count, imp_t* out ) +{ + double const maxh = 256; + double const step = PI / maxh * spacing; + double const to_w = maxh * 2 / width; + double const pow_a_n = pow( rolloff, maxh ); + scale /= maxh * 2; + double angle = (count / 2 - 1 + offset) * -step; + + while ( count-- ) + { + *out++ = 0; + double w = angle * to_w; + if ( fabs( w ) < PI ) + { + double rolloff_cos_a = rolloff * cos( angle ); + double num = 1 - rolloff_cos_a - + pow_a_n * cos( maxh * angle ) + + pow_a_n * rolloff * cos( (maxh - 1) * angle ); + double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + double sinc = scale * num / den - scale; + + out [-1] = (imp_t) (cos( w ) * sinc + sinc); + } + angle += step; + } +} + +enum { width = 32 }; +enum { stereo = 2 }; +enum { max_res = 512 }; +enum { min_width = (width < 4 ? 4 : width) }; +enum { adj_width = min_width / 4 * 4 + 2 }; +enum { write_offset = adj_width * stereo }; + +enum { buffer_size = 128 }; + +typedef struct _resampler +{ + int width_; + int rate_; + int inptr; + int infilled; + int outptr; + int outfilled; + + int latency; + + imp_t const* imp; + imp_t impulses [max_res * (adj_width + 2 * (sizeof(imp_off_t) / sizeof(imp_t)))]; + sample_t buffer_in[buffer_size * stereo * 2]; + sample_t buffer_out[buffer_size * stereo]; +} resampler; + +void * resampler_create() +{ + resampler *r = (resampler *) malloc(sizeof(resampler)); + if (r) resampler_clear(r); + return r; +} + +void * resampler_dup(void *_r) +{ + resampler *r = (resampler *)_r; + resampler *t = (resampler *) malloc(sizeof(resampler)); + if (r && t) + { + memcpy(t, r, sizeof(resampler)); + t->imp = t->impulses + (r->imp - r->impulses); + } + else if (t) + { + resampler_clear(t); + } + return t; +} + +void resampler_destroy(void *r) +{ + free(r); +} + +void resampler_clear(void *_r) +{ + resampler * r = (resampler *)_r; + r->width_ = adj_width; + r->inptr = 0; + r->infilled = 0; + r->outptr = 0; + r->outfilled = 0; + r->latency = 0; + r->imp = r->impulses; + + resampler_set_rate(r, 1.0); +} + +void resampler_set_rate( void *_r, double new_factor ) +{ + resampler *rs = (resampler *)_r; + + double const rolloff = 0.999; + double const gain = 1.0; + + /* determine number of sub-phases that yield lowest error */ + double ratio_ = 0.0; + int res = -1; + { + double least_error = 2; + double pos = 0; + for ( int r = 1; r <= max_res; r++ ) + { + pos += new_factor; + double nearest = floor( pos + 0.5 ); + double error = fabs( pos - nearest ); + if ( error < least_error ) + { + res = r; + ratio_ = nearest / res; + least_error = error; + } + } + } + rs->rate_ = ratio_; + + /* how much of input is used for each output sample */ + int const step = stereo * (int) floor( ratio_ ); + double fraction = fmod( ratio_, 1.0 ); + + double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + /*int input_per_cycle = 0;*/ + imp_t* out = rs->impulses; + for ( int n = res; --n >= 0; ) + { + gen_sinc( rolloff, (int) (rs->width_ * filter + 1) & ~1, pos, filter, + (double)(imp_scale * gain * filter), (int) rs->width_, out ); + out += rs->width_; + + int cur_step = step; + pos += fraction; + if ( pos >= 0.9999999 ) + { + pos -= 1.0; + cur_step += stereo; + } + + ((imp_off_t*)out)[0] = (cur_step - rs->width_ * 2 + 4) * sizeof (sample_t); + ((imp_off_t*)out)[1] = 2 * sizeof (imp_t) + 2 * sizeof (imp_off_t); + out += 2 * (sizeof(imp_off_t) / sizeof(imp_t)); + /*input_per_cycle += cur_step;*/ + } + /* last offset moves back to beginning of impulses*/ + ((imp_off_t*)out) [-1] -= (char*) out - (char*) rs->impulses; + + rs->imp = rs->impulses; +} + +int resampler_get_free(void *_r) +{ + resampler *r = (resampler *)_r; + return buffer_size * stereo - r->infilled; +} + +int resampler_get_min_fill(void *_r) +{ + resampler *r = (resampler *)_r; + const int min_needed = write_offset + stereo; + const int latency = r->latency ? 0 : adj_width; + int min_free = min_needed - r->infilled - latency; + return min_free < 0 ? 0 : min_free; +} + +void resampler_write_pair(void *_r, sample_t ls, sample_t rs) +{ + resampler *r = (resampler *)_r; + + if (!r->latency) + { + for (int i = 0; i < adj_width / 2; ++i) + { + r->buffer_in[r->inptr + 0] = 0; + r->buffer_in[r->inptr + 1] = 0; + r->buffer_in[buffer_size * stereo + r->inptr + 0] = 0; + r->buffer_in[buffer_size * stereo + r->inptr + 1] = 0; + r->inptr = (r->inptr + stereo) % (buffer_size * stereo); + r->infilled += stereo; + } + r->latency = 1; + } + + if (r->infilled < buffer_size * stereo) + { + r->buffer_in[r->inptr + 0] = ls; + r->buffer_in[r->inptr + 1] = rs; + r->buffer_in[buffer_size * stereo + r->inptr + 0] = ls; + r->buffer_in[buffer_size * stereo + r->inptr + 1] = rs; + r->inptr = (r->inptr + stereo) % (buffer_size * stereo); + r->infilled += stereo; + } +} + +#ifdef _MSC_VER +#define restrict __restrict +#endif + +static const sample_t * resampler_inner_loop( resampler *r, sample_t** out_, + sample_t const* out_end, sample_t const in [], int in_size ) +{ + in_size -= write_offset; + if ( in_size > 0 ) + { + sample_t* restrict out = *out_; + sample_t const* const in_end = in + in_size; + imp_t const* imp = r->imp; + + do + { + /* accumulate in extended precision*/ + int pt = imp [0]; + intermediate_t l = (intermediate_t)pt * (intermediate_t)(in [0]); + intermediate_t r = (intermediate_t)pt * (intermediate_t)(in [1]); + if ( out >= out_end ) + break; + for ( int n = (adj_width - 2) / 2; n; --n ) + { + pt = imp [1]; + l += (intermediate_t)pt * (intermediate_t)(in [2]); + r += (intermediate_t)pt * (intermediate_t)(in [3]); + + /* pre-increment more efficient on some RISC processors*/ + imp += 2; + pt = imp [0]; + r += (intermediate_t)pt * (intermediate_t)(in [5]); + in += 4; + l += (intermediate_t)pt * (intermediate_t)(in [0]); + } + pt = imp [1]; + l += (intermediate_t)pt * (intermediate_t)(in [2]); + r += (intermediate_t)pt * (intermediate_t)(in [3]); + + /* these two "samples" after the end of the impulse give the + * proper offsets to the next input sample and next impulse */ + in = (sample_t const*) ((char const*) in + ((imp_off_t*)(&imp [2]))[0]); /* some negative value */ + imp = (imp_t const*) ((char const*) imp + ((imp_off_t*)(&imp [2]))[1]); /* small positive or large negative */ + + out [0] = (sample_t) (l >> 15); + out [1] = (sample_t) (r >> 15); + out += 2; + } + while ( in < in_end ); + + r->imp = imp; + *out_ = out; + } + return in; +} + +#undef restrict + +static int resampler_wrapper( resampler *r, sample_t out [], int* out_size, + sample_t const in [], int in_size ) +{ + sample_t* out_ = out; + int result = resampler_inner_loop( r, &out_, out + *out_size, in, in_size ) - in; + + *out_size = out_ - out; + return result; +} + +static void resampler_fill( resampler *r ) +{ + while (!r->outfilled && r->infilled) + { + int writepos = ( r->outptr + r->outfilled ) % (buffer_size * stereo); + int writesize = (buffer_size * stereo) - writepos; + if ( writesize > ( buffer_size * stereo - r->outfilled ) ) + writesize = buffer_size * stereo - r->outfilled; + int inread = resampler_wrapper(r, &r->buffer_out[writepos], &writesize, &r->buffer_in[buffer_size * stereo + r->inptr - r->infilled], r->infilled); + r->infilled -= inread; + r->outfilled += writesize; + if (!inread) + break; + } +} + +int resampler_get_avail(void *_r) +{ + resampler *r = (resampler *)_r; + if (r->outfilled < stereo && r->infilled >= r->width_) + resampler_fill( r ); + return r->outfilled; +} + +static void resampler_read_pair_internal( resampler *r, sample_t *ls, sample_t *rs, int advance ) +{ + if (r->outfilled < stereo) + resampler_fill( r ); + if (r->outfilled < stereo) + { + *ls = 0; + *rs = 0; + return; + } + *ls = r->buffer_out[r->outptr + 0]; + *rs = r->buffer_out[r->outptr + 1]; + if (advance) + { + r->outptr = (r->outptr + 2) % (buffer_size * stereo); + r->outfilled -= stereo; + } +} + +void resampler_read_pair( void *_r, sample_t *ls, sample_t *rs ) +{ + resampler *r = (resampler *)_r; + resampler_read_pair_internal(r, ls, rs, 1); +} + +void resampler_peek_pair( void *_r, sample_t *ls, sample_t *rs ) +{ + resampler *r = (resampler *)_r; + resampler_read_pair_internal(r, ls, rs, 0); +} diff --git a/Frameworks/GME/vgmplay/resampler.h b/Frameworks/GME/vgmplay/resampler.h new file mode 100644 index 000000000..d0fad30a2 --- /dev/null +++ b/Frameworks/GME/vgmplay/resampler.h @@ -0,0 +1,73 @@ +#ifndef _RESAMPLER_H_ +#define _RESAMPLER_H_ + +/* Copyright (C) 2004-2008 Shay Green. + Copyright (C) 2015 Christopher Snowhill. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define RESAMPLER_BITS 32 +#define RESAMPLER_DECORATE vgmplay + +#ifdef RESAMPLER_DECORATE +#undef PASTE +#undef EVALUATE +#define PASTE(a,b) a ## b +#define EVALUATE(a,b) PASTE(a,b) +#define resampler_create EVALUATE(RESAMPLER_DECORATE,_resampler_create) +#define resampler_dup EVALUATE(RESAMPLER_DECORATE,_resampler_dup) +#define resampler_destroy EVALUATE(RESAMPLER_DECORATE,_resampler_destroy) +#define resampler_clear EVALUATE(RESAMPLER_DECORATE,_resampler_clear) +#define resampler_set_rate EVALUATE(RESAMPLER_DECORATE,_resampler_set_rate) +#define resampler_get_free EVALUATE(RESAMPLER_DECORATE,_resampler_get_free) +#define resampler_get_min_fill EVALUATE(RESAMPLER_DECORATE,_resampler_get_min_fill) +#define resampler_write_pair EVALUATE(RESAMPLER_DECORATE,_resampler_write_pair) +#define resampler_get_avail EVALUATE(RESAMPLER_DECORATE,_resampler_get_avail) +#define resampler_read_pair EVALUATE(RESAMPLER_DECORATE,_resampler_read_pair) +#define resampler_peek_pair EVALUATE(RESAMPLER_DECORATE,_resampler_peek_pair) +#endif + +#include + +#if RESAMPLER_BITS == 16 +typedef int16_t sample_t; +#elif RESAMPLER_BITS == 32 +typedef int32_t sample_t; +#else +#error Choose a bit depth! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void * resampler_create(); +void * resampler_dup(void *); +void resampler_destroy(void *); + +void resampler_clear(void *); + +void resampler_set_rate( void *, double new_factor ); + +int resampler_get_free(void *); +int resampler_get_min_fill(void *); + +void resampler_write_pair(void *, sample_t ls, sample_t rs); + +int resampler_get_avail(void *); + +void resampler_read_pair( void *, sample_t *ls, sample_t *rs ); +void resampler_peek_pair( void *, sample_t *ls, sample_t *rs ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Frameworks/GME/vgmplay/stdbool.h b/Frameworks/GME/vgmplay/stdbool.h new file mode 100644 index 000000000..b13d090c9 --- /dev/null +++ b/Frameworks/GME/vgmplay/stdbool.h @@ -0,0 +1,16 @@ +// custom stdbool.h to for 1-byte bool types + +#ifndef _CSTM_STDBOOL_H_ +#define _CSTM_STDBOOL_H_ + +#ifndef __cplusplus // C++ already has the bool-type + +#define false 0x00 +#define true 0x01 + +// the MS VC++ 6 compiler uses a one-byte-type (unsigned char, to be exact), so I'll reproduce this here +typedef unsigned char bool; + +#endif // !__cplusplus + +#endif // !_CSTM_STDBOOL_H_ diff --git a/Frameworks/GME/vgmplay/vgm-player b/Frameworks/GME/vgmplay/vgm-player new file mode 100644 index 000000000..9f51754e8 --- /dev/null +++ b/Frameworks/GME/vgmplay/vgm-player @@ -0,0 +1,366 @@ +#!/bin/sh + +# Launcher script to work around limitations in vgmplay: +# OSS only - detects oss wrapper and allows explicit selection +# Config must be in working dir - automatically cd/mktemps as needed +# Can't open release packs - plays from temporary directories +# +# Original script by ZekeSulastin +# 7z support by Valley Bell + +trap '[ -n "$_tmpdir" ] && rm -rf "$_tmpdir"; [ -w "$_log_path/vgmplay.ini" ] && rm "$_log_path/vgmplay.ini"' EXIT +trap '[ -n "$_tmpdir" ] && rm -rf "$_tmpdir"; [ -w "$_log_path/vgmplay.ini" ] && rm "$_log_path/vgmplay.ini"; reset' HUP INT QUIT TERM SEGV + +_set_output= +_output= +_pulse_server= +_log_path= +_binary= +_config= +_unpacker= +_pwd="$(pwd)" +_host="$(hostname)" +_ext= +_unpacker= +_tmpdir= +_music_file= +_m3u_stub= +XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" +XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS:-/etc/xdg}" +_padsp="$(command -v padsp 2>/dev/null)" +_aoss="$(command -v aoss 2>/dev/null)" + +_help () { +cat <, + --pulse-server= + set alternate PulseAudio server (implies --padsp) + -l, --log outputs to wav files in current directory + --log-path= outputs to wav files in (implies --log) + + WARNING: If you plan to log using this script, use --log or --log-path! + Do NOT set logging on in a custom config, else the script logic will + remove your logs! + + --binary= use alternate vgmplay binary + --config= use alternate configuration file + +Keys (In-Player): + Left/Right seek 5 seconds backward/forward + Ctrl + Left/Right seek 60 seconds backward/forward + Space pause + Escape (twice) or Q quit program + F fade out current track + R restart current track + PageUp or B previous track in playlist + PageDown or N next track in playlist +EOF +exit 0 +} + +_to_stderr () { + echo "$0: $2" 1>&2 + exit $1 +} + +_die () { + case $1 in + too_many_outputs ) + _to_stderr 64 "can only force one output type" ;; + no_pulse_server ) + _to_stderr 64 "--pulse-server requires server parameter" ;; + no_log_path ) + _to_stderr 64 "--log-to requires path parameter" ;; + unknown_option ) + _to_stderr 64 "unrecognized option '$2'\nTry 'vgm-player --help' for more information." ;; + no_aoss ) + _to_stderr 69 "alsa output forced but aoss not found" ;; + no_oss ) + _to_stderr 74 "oss output forced but /dev/dsp unwritable or not present" ;; + no_pulse ) + _to_stderr 69 "pulse output forced but padsp not found" ;; + too_many_files ) + _to_stderr 64 "only one music file should be specified" ;; + file_unreadable ) + _to_stderr 66 "unable to read specified input - does it exist?" ;; + invalid_format ) + _to_stderr 66 "format unsupported" ;; + invalid_binary ) + _to_stderr 69 "invalid binary specified" ;; + no_binary ) + _to_stderr 64 "--binary requires binary parameter" ;; + no_config ) + _to_stderr 64 "--config requires config parameter" ;; + noread_config ) + _to_stderr 69 "specified config unreadable - does it exist?" ;; + invalid_config ) + _to_stderr 64 "invalid config specified" ;; + vgmplay_not_found ) + _to_stderr 69 "vgmplay binary not found" ;; + no_unpacker ) + _to_stderr 69 "no suitable unpacker found" ;; + tmpdir_failed ) + _to_stderr 70 "unable to create/use temporary working directory" ;; + no_output ) + _to_stderr 69 "no usable otuput detected" ;; + log_path_nowrite ) + _to_stderr 73 "log path unwriteable" ;; + esac +} + +_set_output () { + if [ -n "$_set_output" ] && [ "$1" != "$_set_output" ]; then + _die too_many_outputs + fi + _set_output=$1 + + case "$_set_output" in + alsa ) + [ -x "$_aoss" ] || _die no_aoss ;; + oss ) + [ -w /dev/dsp ] || _die no_oss ;; + pulse ) + [ -n "$_padsp" ] || _die no_pulse ;; + * ) + ;; + esac +} + +# Set terminal title for xterm/rxvt +case $TERM in + xterm* | rxvt* ) + printf '%b\n' '\033]2;VGM Player\007' ;; + * ) + ;; +esac + +# Parse command line options +# All single-letter options are exclusive +while :; do + case $1 in + --help ) + _help ;; + --version ) + echo "VGMPlay 0.40.3 supporting VGM 1.70" + exit 0 ;; + -a | --alsa ) + _set_output alsa + shift ;; + -o | --oss ) + _set_output oss + shift ;; + -p | --pulse ) + _set_output pulse + shift ;; + --pulse-server=* ) + _set_output pulse + _pulse_server="${1#*=}" + [ -n "$_pulse_server" ] || _die no_pulse_server + shift ;; + -s* ) + _set_output pulse + _pulse_server="${1#-s}" + if [ -z "$_pulse_server" ]; then + _pulse_server="$2" + shift + fi + [ -n "$_pulse_server" ] || _die no_pulse_server + shift ;; + -l | --log ) + _set_output log + _log_path="$_pwd" + shift ;; + --log-path=* ) + _set_output log + _log_path="${1#*=}" + [ -d "$_log_path" ] || _die no_log_path + shift ;; + --config=* ) + _config="${1#*=}" + [ -n "$_config" ] || _die no_binary + [ -r "$_config" ] || _die noread_config + file "$_config" | grep -q "ASCII" || _die invalid_config + shift ;; + --binary=* ) + _binary="${1#*=}" + [ -n "$_binary"] || _die no_binary + if [ ! -x "$_binary" ] || [ ! "${binary##*/}" = "vgmplay" ]; then + die _invalid_binary + fi + shift ;; + -* ) + _die unknown_option "$1" ;; + -- ) + shift ;; + "" ) + _die no_music_file ;; + * ) + [ -z "$2" ] || _die too_many_files + _ext="$(echo ${1##*.} | tr [:upper:] [:lower:])" + case $_ext in + vgm | vgz | cmf | dro | m3u | zip | 7z | vgm7z ) + [ -r "$1" ] || _die file_unreadable + _music_file="$1";; + * ) + _die invalid_format + esac + case $_music_file in + ~* | /* ) + break ;; + * ) + _music_file="$_pwd/$_music_file" ;; + esac + break ;; + esac +done + +# Does `vgmplay` exist if --binary not specified? +# Prefers vgmplay in working dir over any others +if [ -n "$_binary" ]; then + break +elif [ -x "$_pwd/vgmplay" ]; then + _binary="$_pwd/vgmplay" +else + _binary="$(command -v vgmplay 2>/dev/null)" +fi +[ -n "$_binary" ] || _die vgmplay_not_found + +# If a compressed pack, does a suitable unpacker exist? +case $_ext in + zip ) + command -v unzip 2>/dev/null || _die no_unpacker ;; + 7z | vgm7z ) + command -v 7z 2>/dev/null || _die no_unpacker ;; + vgm | vgz | cmf | dro | m3u ) + break ;; + * ) + _die invalid_format ;; +esac + +# Select config +if [ -r "$_config" ]; then + break +else + for i in "$_pwd" "$XDG_CONFIG_HOME/vgmplay" "$HOME/.config/vgmplay" "$XDG_CONFIG_DIRS/vgmplay" "/etc/xdg/vgmplay" "/usr/local/share/vgmplay" "/usr/share/vgmplay"; do + _config="$i/vgmplay.ini" + [ -r "$_config" ] && break + done +fi +# vgmplay will function without a config, but will warn the user thus from inside the program + +# Make temporary working directory for vgmplay +# Logdir availability checked here ... +# Uses $XDG_RUNTIME_DIR, $TMPDIR, /tmp +# Uses mktemp program for my sanity's sake +# Assumes user isn't going to try to log into a directory w/ a preexisting vgmplay.ini +# Seriously, if you're going to do that why would you use this helper to start with +if [ -d "$_log_path" ]; then + command sed "s/^LogSound.*/LogSound = 1/" < "$_config" > "$_log_path/vgmplay.ini" || _die log_path_nowrite +elif [ -n "$XDG_RUNTIME_DIR" ]; then + _tmpdir="$(command mktemp -d --tmpdir="$XDG_RUNTIME_DIR")" || _die tmpdir_fail + command cp "$_config" "$_tmpdir/vgmplay.ini" || _die tmpdir_fail +else #mktemp defaults to tmpdir then /tmp! + _tmpdir="$(command mktemp -d)" || _die tmpdir_fail + command cp "$_config" "$_tmpdir/vgmplay.ini" || _die tmpdir_fail +fi + +# Determine output to use if not forced +# Also sets pulse-server if detected +# PULSE_SERVER, oss, padsp, aoss, log set, error +if [ -n $_set_output ]; then + if [ -n "$PULSE_SERVER" ] && [ -x "$_padsp" ]; then + _set_output="pulse-server" + elif [ -w /dev/dsp ]; then + _set_output="oss" + elif [ -x "$_padsp" ] && command pulseaudio --check ; then + _set_output="pulse" + elif [ -x "$_aoss" ]; then + _set_output="alsa" + elif [ -n "$_log_path" ]; then + _set_output="log" + else + _die no_output + fi +fi +[ -n "$_pulse_server" ] && _set_output=pulse-server + +# Extract packs to tmpdir. +if [ $_ext = "zip" ]; then + if [ "$_set_output" = log ]; then + ( [ -d "$_log_path" ] && cd "$_log_path" || _die log_path_nowrite + command unzip "$_music_file" >"$_log_path/extractlog" + ) + _m3u_stub="$(command grep < "$_log_path/extractlog" m3u | command sed 's/ inflating: \| extracting: //g')" + _music_file="$_log_path/${_m3u_stub%% }" + command rm "$_log_path/extractlog" + else + ( [ -d "$_tmpdir" ] && cd "$_tmpdir" || _die tmpdir_failed + command unzip "$_music_file" >"$_tmpdir/extractlog" + ) + _m3u_stub="$(command grep < "$_tmpdir/extractlog" m3u | command sed 's/ inflating: \| extracting: //g')" + _music_file="$_tmpdir/${_m3u_stub%% }" + fi +elif [ $_ext = "7z" ] || [ $_ext = "vgm7z" ]; then + if [ "$_set_output" = log ]; then + ( [ -d "$_log_path" ] && cd "$_log_path" || _die log_path_nowrite + command 7z x "$_music_file" >"$_log_path/extractlog" + ) + _m3u_stub="$(command grep < "$_log_path/extractlog" m3u | command sed 's/Extracting //g')" + _music_file="$_log_path/${_m3u_stub%% }" + command rm "$_log_path/extractlog" + else + ( [ -d "$_tmpdir" ] && cd "$_tmpdir" || _die tmpdir_failed + command 7z x "$_music_file" >"$_tmpdir/extractlog" + ) + _m3u_stub="$(command grep < "$_tmpdir/extractlog" m3u | command sed 's/Extracting //g')" + _music_file="${_m3u_stub%% }" + #echo "Test:$_music_file|" + if [ "$_music_file" == "" ]; then + _music_file="List.m3u" + #echo "Test:$_music_file|" + command grep < "$_tmpdir/extractlog" vgm | command sed 's/Extracting //g' >"$_tmpdir/$_music_file" + #exec cat "$_music_file" + fi + _music_file="$_tmpdir/$_music_file" + fi +fi +_music_file=`echo "$_music_file" | sed -e "s/\\s*$//g"` + +# Plays back song with specified everything +case $_set_output in + log ) + ( cd "$_log_path" + "$_binary" "$_music_file" + ) + command mv "${_music_file%/*}/"*.wav "${_log_path}" + [ -n "$_music_stub" ] && rm -rf "$_log_path/$_music_stub" ;; + pulse-server ) + _pulse_server="${_pulse_server:-$PULSE_SERVER}" + ( cd "$_tmpdir" + exec "$_padsp" -s "$_pulse_server" -n "VGM Player" -m "OSS Emulation (from $_host)" "$_binary" "$_music_file" + ) ;; + pulse ) + ( cd "$_tmpdir" + exec "$_padsp" -n "VGM Player" -m "OSS Emulation" "$_binary" "$_music_file" + ) ;; + alsa ) + ( cd "$_tmpdir" + exec "$_aoss" "$_binary" "$_music_file" + ) ;; + oss ) + ( cd "$_tmpdir" + exec "$_binary" "$_music_file" + ) ;; +esac +#read waitvar +# vim:ts=4:sw=4:et diff --git a/Frameworks/GME/vgmplay/vgm2pcm.c b/Frameworks/GME/vgmplay/vgm2pcm.c new file mode 100644 index 000000000..5e608bb9a --- /dev/null +++ b/Frameworks/GME/vgmplay/vgm2pcm.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +#include "chips/mamedef.h" +#include "stdbool.h" +#include "VGMPlay.h" + +#define SAMPLESIZE sizeof(WAVE_16BS) + +INLINE int fputBE16(UINT16 Value, FILE* hFile) +{ + int RetVal; + int ResVal; + + RetVal = fputc((Value & 0xFF00) >> 8, hFile); + RetVal = fputc((Value & 0x00FF) >> 0, hFile); + ResVal = (RetVal != EOF) ? 0x02 : 0x00; + return ResVal; +} + +int main(int argc, char *argv[]) { + UINT8 result; + WAVE_16BS *sampleBuffer; + UINT32 bufferedLength; + FILE *outputFile; + void *vgmp; + VGM_PLAYER *p; + int EndPlay; + + if (argc < 3) { + fputs("usage: vgm2pcm vgm_file pcm_file\n", stderr); + return 1; + } + + vgmp = VGMPlay_Init(); + VGMPlay_Init2(vgmp); + + if (!OpenVGMFile(vgmp, argv[1])) { + fprintf(stderr, "vgm2pcm: error: failed to open vgm_file (%s)\n", argv[1]); + return 1; + } + + outputFile = fopen(argv[2], "wb"); + if (outputFile == NULL) { + fprintf(stderr, "vgm2pcm: error: failed to open pcm_file (%s)\n", argv[2]); + return 1; + } + + PlayVGM(vgmp); + + p = (VGM_PLAYER *) vgmp; + + sampleBuffer = (WAVE_16BS*)malloc(SAMPLESIZE * p->SampleRate); + if (sampleBuffer == NULL) { + fprintf(stderr, "vgm2pcm: error: failed to allocate %u bytes of memory\n", SAMPLESIZE * p->SampleRate); + return 1; + } + + EndPlay = 0; + while (!EndPlay) { + UINT32 bufferSize = p->SampleRate; + bufferedLength = FillBuffer(vgmp, sampleBuffer, bufferSize); + if (bufferedLength) { + UINT32 numberOfSamples; + UINT32 currentSample; + const UINT16* sampleData; + + sampleData = (UINT16*)sampleBuffer; + numberOfSamples = SAMPLESIZE * bufferedLength / 0x02; + for (currentSample = 0x00; currentSample < numberOfSamples; currentSample++) { + fputBE16(sampleData[currentSample], outputFile); + } + } + if (bufferedLength < bufferSize) + EndPlay = 1; + } + + StopVGM(vgmp); + + CloseVGMFile(vgmp); + + VGMPlay_Deinit(vgmp); + + return 0; +} diff --git a/Frameworks/GME/vgmplay/vgmplay.1 b/Frameworks/GME/vgmplay/vgmplay.1 new file mode 100644 index 000000000..ba4e8b053 --- /dev/null +++ b/Frameworks/GME/vgmplay/vgmplay.1 @@ -0,0 +1,148 @@ +.TH vgmplay "1" "November 10" "Valley Bell" "User Commands" +.nh +.SH NAME +vgmplay \- the official and always up-to-date player for all VGM files +.SH SYNOPSIS +\fBvgmplay\fP file +.SH KEYS +Cursor Left/Right - Seek 5 seconds backward/forward +.PP +Ctrl + Cursor Left/Right - Seek 1 minute backward/forward +.PP +Space - Pause +.PP +ESC/Q - Quit the program +.PP +F - Fade out +.PP +R - Restart current Track +.PP +PageUp/B - Previous Track +.PP +PageDown/N - Next Track +.SH SUPPORTED FILETYPES +Video Game Music Files (.vgm, .vgz) +.PP +Creative Music Files (.cmf) +.PP +DosBox RAW OPL Log Files (.dro) +.PP +Playlist files (.m3u) +.SH SUPPORTED CHIPS +.PP +SN76496 (2) (Sega PSG) and T6W28 (2) (NeoGeo Pocket custom) +.PP +YM2413 (1) (OPLL) +.PP +YM2612 (OPN2) +.PP +YM2151 (OPM) +.PP +SegaPCM +.PP +RF5C68 +.PP +YM2203 (OPN) +.PP +YM2608 (OPNA) +.PP +YM2610/B (OPNB) +.PP +YM3812 (1) (OPL2) +.PP +YM3526 (1) (OPL) +.PP +Y8950 (1) (MSX AUDIO) +.PP +YMF262 (1) (OP3) +.PP +YMF278B (3) (OPL4) +.PP +YMF271 (OPLX) +.PP +YMZ280B +.PP +RF5C164 (Sega MegaCD PCM) +.PP +PWM (from Sega 32x) +.PP +AY8910 (MSX PSG) +.PP +GameBoy DMG +.PP +NES APU (incl. FDS) +.PP +MultiPCM +.PP +UPD7759 +.PP +OKI6258 (Sharp X68000 ADPCM) +.PP +OKI6295 +.PP +K051649 +.PP +K054539 +.PP +HuC6280 (PC Engine) +.PP +C140 +.PP +K053260 +.PP +Pokey (Atari) +.PP +QSound +.PP +SCSP (Saturn Custom Sound Processor, YMF292-F) +.PP +(1) This chip can be emulated via OPL Hardware (like Soundblaster sound cards). +.PP +(2) OPL hardware emulation is available, but software emulation is prefered. Hardware emulation is used if another chip activates HW emulation or FMForce is True. +(3) You need a sample ROM, called yrw801.rom, to make playback work. Place it in the directory where vgmplay lies or in /usr/local/share/vgmplay/. +.PP +OPL hardware emulation can be enabled by setting the "FMPort"-entry in the ini-file. +Under Linux the program must be run as root to use hardware FM. +.PP +It's possible to write Wave files by editing the "LogSound"-line in the ini-file. +Batch conversions are possible by opening a playlist. +FM hardware cannot be logged to Wave files. +.SH CONFIGURATION +vgmplay is configured in the file VGMPlay.ini, which should be located in $XDG_CONFIG_HOME/vgmplay (thus, by default ~/.config/vgmplay/VGMPlay.ini). A sample configuration /usr/share/vgmplay/VGMPlay.ini is available for copying and general reference. +.SH BUGS +PauseEmulation is disabled under Linux if no FM Hardware is used. +.PP +You have to double-tap ESC to quit the program. +.PP +Sometimes MAME's sound cores tend to sound strange. +.SH COMMENTS +The T6W28 doesn't use MAME's T6W28 core. Instead the SN76496 core is modified to emulate the T6W28 with 2 SN76496 chips. +The SN76496 OPL emulation is okay, but it's impossible to get the noise sound right. +.PP +EMU2413 Emulator was added, because sometimes the one of MAME sounds strange. +The Gens YM2612 core was added for the same reason before MAME's YM2612 core was fixed. +.PP +.SH AUTHORS +This program was written by Valley Bell. +.PP +Almost all software emulators are from MAME (http://mamedev.org) +.PP +EMU2413 and Gens YM2612 were ported from Maxim's in_vgm +.PP +The YMF278B core was ported from openMSX +.PP +zlib compression by Jean-loup Gailly and Mark Adler is used +.PP +All custom OPL Mappers were written using MAME software emulators and the OPL2/3 programming guides by Jeffrey S. Lee and Vladimir Arnost +.PP +one YM2413 OPL Mapper was ported from MEKA. +.PP +The RF5C164 and PWM cores were ported from Gens/GS +.PP +The MAME YM2612 core was fixed with the help of Blargg's MAME YM2612 fix and Genesis Plus GX' YM2612 core +.PP +AdLibEmu (OPL2 and OPL3 core) was ported from DOSBox +.PP +The default HuC6280 core is from Ootake. +.PP +EMU2149, the alternative NES APU core and the NES FDS core were ported from rainwarrior's NSFPlay. diff --git a/Frameworks/GME/vgmplay/vgmspec171.txt b/Frameworks/GME/vgmplay/vgmspec171.txt new file mode 100644 index 000000000..fb3dedfb7 --- /dev/null +++ b/Frameworks/GME/vgmplay/vgmspec171.txt @@ -0,0 +1,847 @@ +VGM Spec v1.71 beta +============== +VGM (Video Game Music) is a sample-accurate sound logging format for the Sega +Master System, the Sega Game Gear and possibly many other machines (e.g. Sega +Genesis). + +The normal file extension is .vgm but files can also be GZip compressed into +.vgz files. However, a VGM player should attempt to support compressed and +uncompressed files with either extension. (ZLib's GZIO library makes this +trivial to implement.) + +The format starts with a 256 byte header: + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F +0x00 ["Vgm " ident ][EoF offset ][Version ][SN76489 clock ] +0x10 [YM2413 clock ][GD3 offset ][Total # samples][Loop offset ] +0x20 [Loop # samples ][Rate ][SN FB ][SNW][SF][YM2612 clock ] +0x30 [YM2151 clock ][VGM data offset][Sega PCM clock ][SPCM Interface ] +0x40 [RF5C68 clock ][YM2203 clock ][YM2608 clock ][YM2610/B clock ] +0x50 [YM3812 clock ][YM3526 clock ][Y8950 clock ][YMF262 clock ] +0x60 [YMF278B clock ][YMF271 clock ][YMZ280B clock ][RF5C164 clock ] +0x70 [PWM clock ][AY8910 clock ][AYT][AY Flags ][VM] *** [LB][LM] +0x80 [GB DMG clock ][NES APU clock ][MultiPCM clock ][uPD7759 clock ] +0x90 [OKIM6258 clock ][OF][KF][CF] *** [OKIM6295 clock ][K051649 clock ] +0xA0 [K054539 clock ][HuC6280 clock ][C140 clock ][K053260 clock ] +0xB0 [Pokey clock ][QSound clock ][SCSP clock ][Extra Hdr ofs ] +0xC0 [WSwan clock ][VSU clock ][SAA1099 clock ][ES5503 clock ] +0xD0 [ES5506 clock ][EC][EC][CD] *** [X1-010 clock ][C352 clock ] +0xE0 [GA20 clock ] *** *** *** *** *** *** *** *** *** *** *** *** +0xF0 *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** + +- Unused space (marked with *) is reserved for future expansion, and must be + zero. +- All integer values are *unsigned* and written in "Intel" byte order (Little + Endian), so for example 0x12345678 is written as 0x78 0x56 0x34 0x12. +- All pointer offsets are written as relative to the current position in the + file, so for example the GD3 offset at 0x14 in the header is the file + position of the GD3 tag minus 0x14. +- All header sizes are valid for all versions from 1.50 on, as long as header + has at least 64 bytes. If the VGM data starts at an offset that is lower + than 0x100, all overlapping header bytes have to be handled as they were + zero. +- VGMs run with a rate of 44100 samples per second. All sample values use this + unit. + +0x00: "Vgm " (0x56 0x67 0x6d 0x20) file identification (32 bits) +0x04: Eof offset (32 bits) + Relative offset to end of file (i.e. file length - 4). + This is mainly used to find the next track when concatenating + player stubs and multiple files. +0x08: Version number (32 bits) + Version number in BCD-Code. e.g. Version 1.71 is stored as 0x00000171. + This is used for backwards compatibility in players, and defines which + header values are valid. +0x0C: SN76489 clock (32 bits) + Input clock rate in Hz for the SN76489 PSG chip. A typical value is + 3579545. It should be 0 if there is no PSG chip used. + Note: Bit 31 (0x80000000) is used on combination with the dual-chip-bit + to indicate that this is a T6W28. (PSG variant used in NeoGeo Pocket) +0x10: YM2413 clock (32 bits) + Input clock rate in Hz for the YM2413 chip. A typical value is 3579545. + It should be 0 if there is no YM2413 chip used. +0x14: GD3 offset (32 bits) + Relative offset to GD3 tag. 0 if no GD3 tag. + GD3 tags are descriptive tags similar in use to ID3 tags in MP3 files. + See the GD3 specification for more details. The GD3 tag is usually + stored immediately after the VGM data. +0x18: Total # samples (32 bits) + Total of all wait values in the file. +0x1C: Loop offset (32 bits) + Relative offset to loop point, or 0 if no loop. + For example, if the data for the one-off intro to a song was in bytes + 0x0040-0x3FFF of the file, but the main looping section started at + 0x4000, this would contain the value 0x4000-0x1C = 0x00003FE4. +0x20: Loop # samples (32 bits) + Number of samples in one loop, or 0 if there is no loop. + Total of all wait values between the loop point and the end of + the file. +[VGM 1.01 additions:] +0x24: Rate (32 bits) + "Rate" of recording in Hz, used for rate scaling on playback. It is + typically 50 for PAL systems and 60 for NTSC systems. It should be set + to zero if rate scaling is not appropriate - for example, if the game + adjusts its music engine for the system's speed. + VGM 1.00 files will have a value of 0. +[VGM 1.10 additions:] +0x28: SN76489 feedback (16 bits) + The white noise feedback pattern for the SN76489 PSG. Known values are: + 0x0009 Sega Master System 2/Game Gear/Mega Drive + (SN76489/SN76496 integrated into Sega VDP chip) + 0x0003 Sega Computer 3000H, BBC Micro + (SN76489AN) + 0x0006 SN76494, SN76496 + For version 1.01 and earlier files, the feedback pattern should be + assumed to be 0x0009. If the PSG is not used then this may be omitted + (left at zero). +0x2A: SN76489 shift register width (8 bits) + The noise feedback shift register width, in bits. Known values are: + 16 Sega Master System 2/Game Gear/Mega Drive + (SN76489/SN76496 integrated into Sega VDP chip) + 15 Sega Computer 3000H, BBC Micro + (SN76489AN) + For version 1.01 and earlier files, the shift register width should be + assumed to be 16. If the PSG is not used then this may be omitted (left + at zero). +[VGM 1.51 additions:] +0x2B: SN76489 Flags (8 bits) + Misc flags for the SN76489. Most of them don't make audible changes and + can be ignored, if the SN76489 emulator lacks the features. + bit 0 frequency 0 is 0x400 + bit 1 output negate flag + bit 2 stereo on/off (on when bit clear) + bit 3 /8 Clock Divider on/off (on when bit clear) + bit 4-7 reserved (must be zero) + For version 1.51 and earlier files, all the flags should not be set. + If the PSG is not used then this may be omitted (left at zero). +[VGM 1.10 additions:] +0x2C: YM2612 clock (32 bits) + Input clock rate in Hz for the YM2612 chip. A typical value is 7670454. + It should be 0 if there us no YM2612 chip used. + For version 1.01 and earlier files, the YM2413 clock rate should be + used for the clock rate of the YM2612. +0x30: YM2151 clock (32 bits) + Input clock rate in Hz for the YM2151 chip. A typical value is 3579545. + It should be 0 if there us no YM2151 chip used. + For version 1.01 and earlier files, the YM2413 clock rate should be + used for the clock rate of the YM2151. +[VGM 1.50 additions:] +0x34: VGM data offset (32 bits) + Relative offset to VGM data stream. + If the VGM data starts at absolute offset 0x40, this will contain + value 0x0000000C. For versions prior to 1.50, it should be 0 and the + VGM data must start at offset 0x40. +[VGM 1.51 additions:] +0x38: Sega PCM clock (32 bits) + Input clock rate in Hz for the Sega PCM chip. A typical value is + 4000000. It should be 0 if there is no Sega PCM chip used. +0x3C: Sega PCM interface register (32 bits) + The interface register for the Sega PCM chip. It should be 0 if there + is no Sega PCM chip used. +0x40: RF5C68 clock (32 bits) + Input clock rate in Hz for the RF5C68 PCM chip. A typical value is + 12500000. It should be 0 if there is no RF5C68 chip used. +0x44: YM2203 clock (32 bits) + Input clock rate in Hz for the YM2203 chip. A typical value is 3000000. + It should be 0 if there is no YM2203 chip used. +0x48: YM2608 clock (32 bits) + Input clock rate in Hz for the YM2608 chip. A typical value is 8000000. + It should be 0 if there is no YM2608 chip used. +0x4C: YM2610/YM2610B clock (32 bits) + Input clock rate in Hz for the YM2610/B chip. A typical value is + 8000000. It should be 0 if there is no YM2610/B chip used. + Note: Bit 31 is used to set whether it is a YM2610 or a YM2610B chip. + If bit 31 is set it is an YM2610B, if bit 31 is clear it is an YM2610. +0x50: YM3812 clock (32 bits) + Input clock rate in Hz for the YM3812 chip. A typical value is 3579545. + It should be 0 if there is no YM3812 chip used. +0x54: YM3526 clock (32 bits) + Input clock rate in Hz for the YM3526 chip. A typical value is 3579545. + It should be 0 if there is no YM3526 chip used. +0x58: Y8950 clock (32 bits) + Input clock rate in Hz for the Y8950 chip. A typical value is 3579545. + It should be 0 if there is no Y8950 chip used. +0x5C: YMF262 clock (32 bits) + Input clock rate in Hz for the YMF262 chip. A typical value is + 14318180. It should be 0 if there is no YMF262 chip used. +0x60: YMF278B clock (32 bits) + Input clock rate in Hz for the YMF278B chip. A typical value is + 33868800. It should be 0 if there is no YMF278B chip used. +0x64: YMF271 clock (32 bits) + Input clock rate in Hz for the YMF271 chip. A typical value is + 16934400. It should be 0 if there is no YMF271 chip used. +0x68: YMZ280B clock (32 bits) + Input clock rate in Hz for the YMZ280B chip. A typical value is + 16934400. It should be 0 if there is no YMZ280B chip used. +0x6C: RF5C164 clock (32 bits) + Input clock rate in Hz for the RF5C164 PCM chip. A typical value is + 12500000. It should be 0 if there is no RF5C164 chip used. +0x70: PWM clock (32 bits) + Input clock rate in Hz for the PWM chip. A typical value is + 23011361. It should be 0 if there is no PWM chip used. +0x74: AY8910 clock (32 bits) + Input clock rate in Hz for the AY8910 chip. A typical value is 1789750. + It should be 0 if there is no AY8910 chip used. +0x78: AY8910 Chip Type (8 bits) + Defines the exact type of AY8910. The values are: + 0x00 - AY8910 + 0x01 - AY8912 + 0x02 - AY8913 + 0x03 - AY8930 + 0x10 - YM2149 + 0x11 - YM3439 + 0x12 - YMZ284 + 0x13 - YMZ294 + If the AY8910 is not used then this may be omitted (left at zero). +0x79: AY8910 Flags (8 bits) + Misc flags for the AY8910. Default is 0x01. + For additional description see ay8910.h in MAME source code. + bit 0 Legacy Output + bit 1 Single Output + bit 2 Discrete Output + bit 3 RAW Output + bit 4 YM2149 Pin 26 (additional /2 clock divider) + bit 5-7 reserved (must be zero) + If the AY8910 is not used then this may be omitted (left at zero). +0x7A: YM2203/AY8910 Flags (8 bits) + Misc flags for the AY8910. This one is specific for the AY8910 that's + connected with/part of the YM2203. +0x7B: YM2608/AY8910 Flags (8 bits) + Misc flags for the AY8910. This one is specific for the AY8910 that's + connected with/part of the YM2608. +[VGM 1.60 additions:] +0x7C: Volume Modifier (8 bits) + Volume = 2 ^ (VolumeModifier / 0x20) where VolumeModifier is a number + from -63 to 192 (-63 = 0xC1, 0 = 0x00, 192 = 0xC0). Also the value -63 + gets replaced with -64 in order to make factor of 0.25 possible. + Therefore the volume can reach levels between 0.25 and 64. + Default is 0, which is equal to a factor of 1 or 100%. + Note: Players should support the Volume Modifier in v1.50 files and + higher. This way MegaDrive VGMs can use the Volume Modifier without + breaking compatibility with old players. +0x7D: reserved + Reserved byte for future use. It must be 0. +0x7E: Loop Base (8 bits) + Modifies the number of loops that are played before the playback ends. + Set this value to eg. 1 to reduce the number of played loops by one. + This is useful, if the song is looped twice in the vgm, because there + are minor differences between the first and second loop and the song + repeats just the second loop. + The resulting number of loops that are played is calculated as + following: NumLoops = NumLoopsModified - LoopBase + Default is 0. Negative numbers are possible (80h...FFh = -128...-1) +[VGM 1.51 additions:] +0x7F: Loop Modifier (8 bits) + Modifies the number of loops that are played before the playback ends. + You may want to use this, e.g. if a tune has a very short, but non- + repetive loop (then set it to 0x20 double the loop number). + The resulting number of loops that are played is calculated as + following: NumLoops = ProgramNumLoops * LoopModifier / 0x10 + Default is 0, which is equal to 0x10. +[VGM 1.61 additions:] +0x80: GameBoy DMG clock (32 bits) + Input clock rate in Hz for the GameBoy DMG chip, LR35902. A typical + value is 4194304. It should be 0 if there is no GB DMG chip used. +0x84: NES APU clock (32 bits) + Input clock rate in Hz for the NES APU chip, N2A03. A typical value is + 1789772. It should be 0 if there is no NES APU chip used. + Note: Bit 31 is used to enable the FDS sound addon. Set to enable, + clear to disable. +0x88: MultiPCM clock (32 bits) + Input clock rate in Hz for the MultiPCM chip. A typical value is + 8053975. It should be 0 if there is no MultiPCM chip used. +0x8C: uPD7759 clock (32 bits) + Input clock rate in Hz for the uPD7759 chip. A typical value is 640000. + It should be 0 if there is no uPD7759 chip used. +0x90: OKIM6258 clock (32 bits) + Input clock rate in Hz for the OKIM6258 chip. A typical value is + 4000000. It should be 0 if there is no OKIM6258 chip used. +0x94: OKIM6258 Flags (8 bits) + Misc flags for the OKIM6258. Default is 0x00. + bit 0-1 Clock Divider (clock dividers are 1024, 768, 512, 512) + bit 2 3/4-bit ADPCM select (default is 4-bit, doesn't work currently) + bit 3 10/12-bit Output (default is 10-bit) + bit 4-7 reserved (must be zero) + If the OKIM6258 is not used then this may be omitted (left at zero). +0x95: K054539 Flags (8 bits) + Misc flags for the K054539. Default is 0x01. + See also k054539.h in MAME source code. + bit 0 Reverse Stereo + bit 1 Disable Reverb + bit 2 Update at KeyOn + bit 3-7 reserved (must be zero) + If the K054539 is not used then this may be omitted (left at zero). +0x96: C140 Chip Type (8 bits) + Defines the exact type of C140 and its banking method. The values are: + 0x00 - C140, Namco System 2 + 0x01 - C140, Namco System 21 + 0x02 - 219 ASIC, Namco NA-1/2 + If the C140 is not used then this may be omitted (left at zero). +0x97: reserved + Reserved byte for future use. It must be 0. +0x98: OKIM6295 clock (32 bits) + Input clock rate in Hz for the OKIM6295 chip. A typical value is + 8000000. It should be 0 if there is no OKIM6295 chip used. +0x9C: K051649 clock (32 bits) + Input clock rate in Hz for the K051649 chip. A typical value is + 1500000. It should be 0 if there is no K051649 chip used. +0xA0: K054539 clock (32 bits) + Input clock rate in Hz for the K054539 chip. A typical value is + 18432000. It should be 0 if there is no K054539 chip used. +0xA4: HuC6280 clock (32 bits) + Input clock rate in Hz for the HuC6280 chip. A typical value is + 3579545. It should be 0 if there is no HuC6280 chip used. +0xA8: C140 clock (32 bits) + Input clock rate in Hz for the C140 chip. A typical value is 8000000. + It should be 0 if there is no C140 chip used. +0xAC: K053260 clock (32 bits) + Input clock rate in Hz for the K053260 chip. A typical value is + 3579545. It should be 0 if there is no K053260 chip used. +0xB0: Pokey clock (32 bits) + Input clock rate in Hz for the Pokey chip. A typical value is 1789772. + It should be 0 if there is no Pokey chip used. +0xB4: QSound clock (32 bits) + Input clock rate in Hz for the QSound chip. A typical value is 4000000. + It should be 0 if there is no QSound chip used. +[VGM 1.71 additions:] +0xB8: SCSP clock (32 bits) + Input clock rate in Hz for the SCSP chip. A typical value is 22579200. + It should be 0 if there is no SCSP chip used. +[VGM 1.70 additions:] +0xBC: Extra Header Offset (32 bits) + Relative offset to the extra header or 0 if no extra header is present. +[VGM 1.71 additions:] +0xC0: WonderSwan clock (32 bits) + Input clock rate in Hz for the WonderSwan chip. A typical value is + 3072000. It should be 0 if there is no WonderSwan chip used. +0xC4: Virtual Boy VSU clock (32 bits) + Input clock rate in Hz for the VSU chip. A typical value is 5000000. + It should be 0 if there is no VSU chip used. +0xC8: SAA1099 clock (32 bits) + Input clock rate in Hz for the SAA1099 chip. A typical value is + 8000000. It should be 0 if there is no SAA1099 chip used. +0xCC: ES5503 clock (32 bits) + Input clock rate in Hz for the ES5503 chip. A typical value is 7159090. + It should be 0 if there is no ES5503 chip used. +0xD0: ES5505/ES5506 clock (32 bits) + Input clock rate in Hz for the ES5506 chip. A typical value is + 16000000. It should be 0 if there is no ES5506 chip used. + Note: Bit 31 is used to set whether it is an ES5505 or an ES5506 chip. + If bit 31 is set it is an ES5506, if bit 31 is clear it is an ES5505. +0xD4: ES5503 Channels (8 bits) + Defines the internal number of output channels for the ES5503. + Possible values are 1 to 8. A typical value is 2. + If the ES5503 is not used then this may be omitted (left at zero). +0xD5: ES5505/6 Channels (8 bits) + Defines the internal number of output channels for the ES5506. + Possible values are 1 to 4 for the ES5505 and 1 to 8 for the ES5506. + A typical value is 1. + If the ES5506 is not used then this may be omitted (left at zero). +0xD6: C352 Clock Divider (8 bits) + Defines the clock divider for the C352 chip, divided by 4 in order + to achieve a divider range of 0 to 1020. A typical value is 288. + If the C352 is not used then this may be omitted (left at zero). +0xD7: reserved + Reserved byte for future use. It must be 0. +0xD8: Seta X1-010 clock (32 bits) + Input clock rate in Hz for the X1-010 chip. A typical value is + 16000000. It should be 0 if there is no X1-010 chip used. +0xDC: Namco C352 clock (32 bits) + Input clock rate in Hz for the C352 chip. A typical value is 24192000. + It should be 0 if there is no C352 chip used. +0xE0: Irem GA20 clock (32 bits) + Input clock rate in Hz for the GA20 chip. A typical value is 3579545. + It should be 0 if there is no GA20 chip used. +0xE4: reserved + Reserved bytes for future use. They must be 0. + + +Starting at the location specified by the VGM data offset (or, offset 0x40 for +file versions below 1.50) is found a sequence of commands containing data +written to the chips or timing information. A command is one of: + + 0x4F dd : Game Gear PSG stereo, write dd to port 0x06 + 0x50 dd : PSG (SN76489/SN76496) write value dd + 0x51 aa dd : YM2413, write value dd to register aa + 0x52 aa dd : YM2612 port 0, write value dd to register aa + 0x53 aa dd : YM2612 port 1, write value dd to register aa + 0x54 aa dd : YM2151, write value dd to register aa + 0x55 aa dd : YM2203, write value dd to register aa + 0x56 aa dd : YM2608 port 0, write value dd to register aa + 0x57 aa dd : YM2608 port 1, write value dd to register aa + 0x58 aa dd : YM2610 port 0, write value dd to register aa + 0x59 aa dd : YM2610 port 1, write value dd to register aa + 0x5A aa dd : YM3812, write value dd to register aa + 0x5B aa dd : YM3526, write value dd to register aa + 0x5C aa dd : Y8950, write value dd to register aa + 0x5D aa dd : YMZ280B, write value dd to register aa + 0x5E aa dd : YMF262 port 0, write value dd to register aa + 0x5F aa dd : YMF262 port 1, write value dd to register aa + 0x61 nn nn : Wait n samples, n can range from 0 to 65535 (approx 1.49 + seconds). Longer pauses than this are represented by multiple + wait commands. + 0x62 : wait 735 samples (60th of a second), a shortcut for + 0x61 0xdf 0x02 + 0x63 : wait 882 samples (50th of a second), a shortcut for + 0x61 0x72 0x03 + 0x64 cc nn nn : override length of 0x62/0x63 + cc - command (0x62/0x63) + nn - delay in samples + [Note: Not yet implemented. Am I really sure about this?] + 0x66 : end of sound data + 0x67 ... : data block: see below + 0x68 ... : PCM RAM write: see below + 0x7n : wait n+1 samples, n can range from 0 to 15. + 0x8n : YM2612 port 0 address 2A write from the data bank, then wait + n samples; n can range from 0 to 15. Note that the wait is n, + NOT n+1. (Note: Written to first chip instance only.) + 0x90-0x95 : DAC Stream Control Write: see below + 0xA0 aa dd : AY8910, write value dd to register aa + 0xB0 aa dd : RF5C68, write value dd to register aa + 0xB1 aa dd : RF5C164, write value dd to register aa + 0xB2 ad dd : PWM, write value ddd to register a (d is MSB, dd is LSB) + 0xB3 aa dd : GameBoy DMG, write value dd to register aa + Note: Register 00 equals GameBoy address FF10. + 0xB4 aa dd : NES APU, write value dd to register aa + Note: Registers 00-1F equal NES address 4000-401F, + registers 20-3E equal NES address 4080-409E, + register 3F equals NES address 4023, + registers 40-7F equal NES address 4040-407F. + 0xB5 aa dd : MultiPCM, write value dd to register aa + 0xB6 aa dd : uPD7759, write value dd to register aa + 0xB7 aa dd : OKIM6258, write value dd to register aa + 0xB8 aa dd : OKIM6295, write value dd to register aa + 0xB9 aa dd : HuC6280, write value dd to register aa + 0xBA aa dd : K053260, write value dd to register aa + 0xBB aa dd : Pokey, write value dd to register aa + 0xBC aa dd : WonderSwan, write value dd to register aa + 0xBD aa dd : SAA1099, write value dd to register aa + 0xBE aa dd : ES5506, write 8-bit value dd to register aa + 0xBF aa dd : GA20, write value dd to register aa + 0xC0 bbaa dd : Sega PCM, write value dd to memory offset aabb + 0xC1 bbaa dd : RF5C68, write value dd to memory offset aabb + 0xC2 bbaa dd : RF5C164, write value dd to memory offset aabb + 0xC3 cc bbaa : MultiPCM, write set bank offset aabb to channel cc + 0xC4 mmll rr : QSound, write value mmll to register rr + (mm - data MSB, ll - data LSB) + 0xC5 mmll dd : SCSP, write value dd to memory offset mmll + (mm - offset MSB, ll - offset LSB) + 0xC6 mmll dd : WonderSwan, write value dd to memory offset mmll + (mm - offset MSB, ll - offset LSB) + 0xC7 mmll dd : VSU, write value dd to register mmll + (mm - MSB, ll - LSB) + 0xC8 mmll dd : X1-010, write value dd to memory offset mmll + (mm - offset MSB, ll - offset LSB) + 0xD0 pp aa dd : YMF278B port pp, write value dd to register aa + 0xD1 pp aa dd : YMF271 port pp, write value dd to register aa + 0xD2 pp aa dd : SCC1 port pp, write value dd to register aa + 0xD3 pp aa dd : K054539 write value dd to register ppaa + 0xD4 pp aa dd : C140 write value dd to register ppaa + 0xD5 pp aa dd : ES5503 write value dd to register ppaa + 0xD6 aa ddee : ES5506 write 16-bit value ddee to register aa + 0xE0 dddddddd : seek to offset dddddddd (Intel byte order) in PCM data bank + 0xE1 aabb ddee: C352 write 16-bit value ddee to register aabb + +Some ranges are reserved for future use, with different numbers of operands: + + 0x30..0x3F dd : one operand, reserved for future use + Note: used for dual-chip support (see below) + 0x40..0x4E dd dd : two operands, reserved for future use + Note: was one operand only til v1.60 + 0xA1..0xAF dd dd : two operands, reserved for future use + Note: used for dual-chip support (see below) + 0xC9..0xCF dd dd dd : three operands, reserved for future use + 0xD7..0xDF dd dd dd : three operands, reserved for future use + 0xE2..0xFF dd dd dd dd : four operands, reserved for future use + +On encountering these, the correct number of bytes should be skipped. + + +Data blocks +----------- +VGM command 0x67 specifies a data block. These are used to store large amounts +of data, which can be used in parallel with the normal VGM data stream. The +data block format is: + + 0x67 0x66 tt ss ss ss ss (data) + +where: + 0x67 = VGM command + 0x66 = compatibility command to make older players stop parsing the stream + tt = data type + ss ss ss ss (32 bits) = size of data, in bytes + (data) = data, of size previously specified + +Data blocks of recorded streams, if present, should be at the very start of the +VGM data. Multiple data blocks expand the data bank. (The start offset and +length of the block in the data bank should be saved for command 0x95.) +Because data blocks can happen anywhere in the stream, players must be able to +parse data blocks anywhere in the stream. + +The data block type specifies what type of data it contains. Currently defined +types are: + +00..3F : data of recorded streams (uncompressed) +40..7E : data of recorded streams (compressed) + data block format for compressed streams: + tt (8 bits) = compression type + 00 - n-Bit-Compression + 01 - DPCM-Compression + ss ss ss ss (32 bits) = size of uncompressed data (for memory allocation) + It is assumed that each decompressed value uses + ceil(bd/8) bytes. + (attr) = attribute bytes used by the decompression-algorithm + n-Bit-Compression: + bd (8 bits) = Bits decompressed + bc (8 bits) = Bits compressed + st (8 bits) = compression sub-type + 00 - copy (high bits aren't used) + 01 - shift left (low bits aren't used) + 02 - use table (data = index into decompression table, + see data block 7F) + aa aa (16 bits) = value that is added (ignored if table is used) + The data block is treated as a bitstream with bc bits per value. The + top bits in each byte are read first. The extracted bits of each value + are transformed into a value with at least bd bits using method st. + Finally, aaaa is added to get the resulting value. + DPCM-Compression: (uses a decompression table) + bd (8 bits) = Bits decompressed + bc (8 bits) = Bits compressed + st (8 bits) = [reserved for future use, must be 00] + aa aa (16 bits) = start value + The data is read as a bitstream (see n-Bit). The read value is used as + index into a delta-table (defined by data block 7F). The delta value + is added to the "state" value, which is also the result value. + The "state" value is initialized with aaaa at the beginning. + (data) = compressed data, of size (block size - 0x0A - attr size) +7F : Decompression Table + tt (8 bits) = compression type (see data block 40..7E) + st (8 bits) = compression sub-type (see data block 40..7E) + bd (8 bits) = Bits decompressed + bc (8 bits) = Bits compressed (only used for verifying against + block 40..7E) + cc cc (16 bits) = number of following values (with each of size + ceil(bd / 8)) + (data) = table data, cccc values with a total size of (block size - 0x06) + Note: Multiple decompression tables are valid. The player should keep a + list of one table per tt and st combination. If there are multiple + tables of the same tt/st type, the new one overrides the old one and + all following compressed data blocks will use the new table. +80..BF : ROM/RAM Image dumps (contain usually samples) + data block format for ROM dumps: + rr rr rr rr (32 bits) = size of the entire ROM + ss ss ss ss (32 bits) = start address of data + (data) = ROM data, of size (block size - 0x08) + The size of the VGM can be decreased a lot by saving only the used parts + of the ROM. This is done by saving multiple small ROM data blocks. + The start address is the ROM offset where the data will be written, the + ROM size is used to allocate space for the ROM (and some chips rely on it). +C0..DF : RAM writes (for RAM with up to 64 KB) + data block format for direct RAM writes: + ss ss (16 bits) = start address of data (affected by a chip's banking + registers) + (data) = RAM data, of size (block size - 0x02) +E0..FF : RAM writes (for RAM with more than 64 KB) + data block format for direct RAM writes: + ss ss ss ss (32 bits) = start address of data (affected by a chip's banking + registers) + (data) = RAM data, of size (block size - 0x04) + +00 = YM2612 PCM data for use with associated commands +01 = RF5C68 PCM data for use with associated commands +02 = RF5C164 PCM data for use with associated commands +03 = PWM PCM data for use with associated commands +04 = OKIM6258 ADPCM data for use with associated commands +05 = HuC6280 PCM data for use with associated commands +06 = SCSP PCM data for use with associated commands +07 = NES APU DPCM data for use with associated commands +40..7E = same as 00..3E, but compressed +80 = Sega PCM ROM data +81 = YM2608 DELTA-T ROM data +82 = YM2610 ADPCM ROM data +83 = YM2610 DELTA-T ROM data +84 = YMF278B ROM data +85 = YMF271 ROM data +86 = YMZ280B ROM data +87 = YMF278B RAM data +88 = Y8950 DELTA-T ROM data +89 = MultiPCM ROM data +8A = uPD7759 ROM data +8B = OKIM6295 ROM data +8C = K054539 ROM data +8D = C140 ROM data +8E = K053260 ROM data +8F = Q-Sound ROM data +90 = ES5506 ROM data +91 = X1-010 ROM data +92 = C352 ROM data +93 = GA20 ROM data +C0 = RF5C68 RAM write +C1 = RF5C164 RAM write +C2 = NES APU RAM write +E0 = SCSP RAM write +E1 = ES5503 RAM write + +All unknown types must be skipped by the player. + + +PCM RAM writes +-------------- +VGM command 0x68 specifies a PCM RAM write. These are used to write data from +data blocks to the RAM of a PCM chip. The data block format is: + + 0x68 0x66 cc oo oo oo dd dd dd ss ss ss + +where: + 0x68 = VGM command + 0x66 = compatibility command to make older players stop parsing the stream + cc = chip type (see data block types 00..3F) + oo oo oo (24 bits) = read offset in data block + dd dd dd (24 bits) = write offset in chip's ram (affected by chip's + registers) + ss ss ss (24 bits) = size of data, in bytes + Since size can't be zero, a size of 0 bytes means 0x0100 0000 bytes. + +All unknown types must be skipped by the player. + + +DAC Stream Control Write +------------------------ +VGM commands 0x90 to 0x95 specify writes to the DAC Stream Control Driver. +These are used to stream data from data blocks to the chips via chip writes. +To use it you must: +1. Setup the Stream (set chip type and command) - this activates the stream +2. Set the Stream Data Bank +3. Set the Stream Frequency +4. Now you can start the stream, change its frequency, start it again, stop it, + etc ... + +There are the following commands: + +Note: Stream ID 0xFF is reserved and ignored unless noted otherwise. + +Setup Stream Control: + 0x90 ss tt pp cc + ss = Stream ID + tt = Chip Type (see clock-order in header, e.g. YM2612 = 0x02) + bit 7 is used to select the 2nd chip + pp cc = write command/register cc at port pp + Note: For chips that use Channel Select Registers (like the RF5C-family + and the HuC6280), the format is pp cd where pp is the channel + number, c is the channel register and d is the data register. + If you set pp to FF, the channel select write is skipped. + +Set Stream Data: + 0x91 ss dd ll bb + ss = Stream ID + dd = Data Bank ID (see data block types 0x00..0x3f) + ll = Step Size (how many data is skipped after every write, usually 1) + Set to 2, if you're using an interleaved stream (e.g. for + left/right channel). + bb = Step Base (data offset added to the Start Offset when starting + stream playback, usually 0) + If you're using an interleaved stream, set it to 0 in one stream + and to 1 in the other one. + Note: Step Size/Step Step are given in command-data-size + (i.e. 1 for YM2612, 2 for PWM), not bytes + +Set Stream Frequency: + 0x92 ss ff ff ff ff + ss = Stream ID + ff = Frequency (or Sample Rate, in Hz) at which the writes are done + +Start Stream: + 0x93 ss aa aa aa aa mm ll ll ll ll + ss = Stream ID + aa = Data Start offset in data bank (byte offset in data bank) + Note: if set to -1, the Data Start offset is ignored + mm = Length Mode (how the Data Length is calculated) + 00 - ignore (just change current data position) + 01 - length = number of commands + 02 - length in msec + 03 - play until end of data + 1? - (bit 4) Reverse Mode + 8? - (bit 7) Loop (automatically restarts when finished) + ll = Data Length + +Stop Stream: + 0x94 ss + ss = Stream ID + Note: 0xFF stops all streams + +Start Stream (fast call): + 0x95 ss bb bb ff + ss = Stream ID + bb = Block ID (number of the data block that is part of the data bank set + with command 0x91) + ff = Flags + bit 0 - Loop (see command 0x93, mm bit 7) + bit 4 - Reverse Mode (see command 0x93) + +General Note to the DAC Stream Control: +Although it may be quite hard to press already streamed data into these +commands, it makes it very easy to write vgm-creation tools that need to stream +something. (like YM2612 DAC drums/voices/etc.) +The DAC Stream Control can use with almost all chips and is NOT limited to +chips such as YM2612 and PWM. + + +Dual Chip Support +----------------- +These chips support two instances of a chip in one vgm: +PSG, YM2413, YM2612, YM2151, YM2203, YM2608, YM2610, YM3812, YM3526, Y8950, +YMZ280B, YMF262, YMF278B, YMF271, AY8910, GameBoy DMG, NES APU, MultiPCM, +uPD7759, OKIM6258, OKIM6295, K051649, K054539, HuC6280, C140, K053260, Pokey, +SCSP + +Dual chip support is activated by setting bit 30 (0x40000000) in the chip's +clock value. (Note: The PSG needs this bit set for T6W28 mode.) + +Dual Chip Support #1: +The second chip instance is controlled via separate commands. + +The second SN76489 PSG uses 0x30 (0x3F for GG Stereo). +All chips of the YM-family that use command 0x5n use 0xAn for the second chip. +n is the last digit of the main command. +e.g. 0x52 (1st chip) -> 0xA2 (2nd chip) + +Dual Chip Support #2: +All other chips use bit 7 (0x80) of the first parameter byte to distinguish +between the 1st and 2nd chip. (0x00-7F = Chip 1, 0x80-0xFF = chip 2) + +Note: The SegaPCM chip has the 2nd-chip-bit in the high byte of the address +parameter. This is the second parameter byte. + + +Extra Header +------------ +With VGM v1.70, there was an extra header added. This one has to be placed +between the usual header and the actual VGM data. + +This is the format of the extra header: + + 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F +0x00 [Header Size ][ChpClock Offset][ChpVol Offset ] + +Header Size is the size of the extra header. It has to be 4 or larger, +depending in the needed offsets. + +Then there are two offsets that point to extra header data for: +- additional Chip Clocks for second chips +- user-defined chip volumes + + +Chip Clock Header +----------------- +1 byte - Entry Count (chips with extra clocks) +[5 bytes - List Entry 1] +[5 bytes - List Entry 2] +... + +Each list entry has the format: +1 byte - Chip ID (chip order follows the header) +4 bytes - clock for second chip of the type above + + +Chip Volume Header +------------------ +1 byte - Entry Count (chips with user-defined volumes) +[4 bytes - List Entry 1] +[4 bytes - List Entry 2] +... + +Each list entry has the format: +1 byte - Chip ID (chip order follows the header) + Note: If bit 7 is set, it's the volume for a paired chip. + (e.g. the AY-part of the YM2203) +1 byte - Flags + Note: If bit 0 is set, it's the volume for the second chip. +2 bytes - volume for the chip + Note: If Bit 15 is 0, this is an absolute volume setting. + If Bit 15 is 1, it's relative and the chip volume gets + multiplied by ((Value & 0x7FFF) / 0x0100). + + +History +------- +[1.00] +Initial public release by Dave + +[1.01] +Rate value added by Maxim; 1.00 files are fully compatible + +[1.10] +PSG white noise feedback and shift register width parameters added by Maxim, +with note on how to handle earlier version files. +Additional wait command added by Maxim with thanks to Steve Snake for the +suggestion. +1.01 files are fully compatible but 1.01 players might have problems +with 1.10 files, hence the 0.1 version change. + +[1.50] +VGM data offset added to header by Maxim. +Data block support added by blargg, to allow for better handling of YM2612 PCM +data. +Both of these changes have the potential to cause problems, but are really good +changes, so the version number has been increased all the way to 1.50. + +[1.51] +Sega PCM, RF5C68, YM2203, YM2608, YM2610/B, YM3812, YM3526, Y8950, YMF262, +YMF278B, YMF271, YMZ280B, RF5C164, PWM and AY8910 chips and commands added. + +Additional data block types RF5C68 RAM write, RF5C164 RAM write, Sega PCM ROM, +YM2608 DELTA-T ROM, YM2610 ADPCM ROM, YM2610 DELTA-T ROM, YMF278B ROM, +YMF271 ROM, YMF271 RAM, YMZ280B ROM and Y8950 DELTA-T ROM Data added. + +Data Block Types splitted into 4 categories. (PCM Stream, compressed PCM +Stream, ROM/RAM Dump, RAM write) + +SN76489 Flags and Loop Modifier added. + +It is the first time the header size exceeds 0x40 bytes. +1.51 files are fully compatible to 1.50 players, but there may be problems with +the new commands. + +Note: Dual chip support was added too, but as a "cheat"-feature. The dual-chip +-bits in the clock values are not compatible to 1.50, but the rest is. + +All changes done by Valley Bell. + +[1.60] +RF5C68, RF5C164 and PWM PCM blocks and compressed data blocks added. + +A whole bunch of new commands (PCM RAM write and DAC Stream Control) added. + +Volume Modifier and Loop Base added. + +The new commands (especially 0x9?) may cause problems with older players. + +All changes done by Valley Bell. + +[1.61] +GameBoy DMG, NES APU, MultiPCM, uPD7759, OKIM6258, OKIM6295, K051649, K051649, +HuC6280, C140, K053260, Pokey and Q-Sound chips added. +(including necessary data blocks) + +Changed number of operands from 1 to 2 for reserved commands 0x40-0x4E. +Although they're still unused, old players might handle future vgm versions +wrongly. + +All changes done by Valley Bell. + +[1.70] +Added extra header with seperate chip clocks for the second one of dual chips +and chip volume adjustments. + +All changes done by Valley Bell. + +[1.71] +SCSP, WonderSwan, Virtual Boy VSU, SAA1099, ES5503, ES5506, Seta X1-010, +Namco C352, Irem GA20 added. +(including necessary ROM data blocks) + +Data blocks (type 0x) for OKIM6258, HuC6280, SCSP and NES added. +VGM v1.61 players should support the data block of their respective chips +despite their late addition. + +All changes done by Valley Bell.