From dc0b9121ff16d57f92f4ab2b16bf78518d3a8b9b Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Sun, 11 Apr 2010 14:48:51 +0200 Subject: changed gme to dynamic plugin --- Makefile.am | 9 +- cgme.c | 248 --- configure.ac | 13 +- gme/Game_Music_Emu-0.5.2/Makefile.am | 3 - gme/Game_Music_Emu-0.5.2/changes.txt | 218 --- gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp | 182 --- gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h | 73 - gme/Game_Music_Emu-0.5.2/demo/basics.c | 57 - gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp | 67 - gme/Game_Music_Emu-0.5.2/demo/features.c | 149 -- gme/Game_Music_Emu-0.5.2/design.txt | 194 --- gme/Game_Music_Emu-0.5.2/gme.txt | 464 ------ gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp | 395 ----- gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h | 107 -- gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp | 1665 ------------------- gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h | 92 -- gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp | 404 ----- gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h | 70 - gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp | 446 ----- gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h | 485 ------ gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp | 184 --- gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h | 127 -- gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp | 315 ---- gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h | 151 -- gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp | 131 -- gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h | 50 - gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp | 529 ------ gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h | 86 - gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp | 199 --- gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h | 171 -- gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp | 306 ---- gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h | 90 -- gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp | 1056 ------------ gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h | 93 -- gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp | 336 ---- gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h | 83 - gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp | 288 ---- gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h | 88 - gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp | 216 --- gme/Game_Music_Emu-0.5.2/gme/Gme_File.h | 145 -- gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp | 379 ----- gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h | 82 - gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp | 315 ---- gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h | 66 - gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp | 1303 --------------- gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h | 125 -- gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp | 529 ------ gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h | 94 -- gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp | 1706 -------------------- gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h | 124 -- gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp | 414 ----- gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h | 96 -- gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp | 97 -- gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h | 106 -- gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp | 426 ----- gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h | 67 - gme/Game_Music_Emu-0.5.2/gme/Makefile.am | 64 - gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp | 232 --- gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h | 156 -- gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp | 410 ----- gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h | 211 --- gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp | 391 ----- gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h | 179 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp | 1084 ------------- gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h | 114 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp | 121 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h | 131 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp | 145 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h | 102 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp | 551 ------- gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h | 147 -- gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp | 215 --- gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h | 95 -- gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp | 557 ------- gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h | 106 -- gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp | 330 ---- gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h | 68 - gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp | 334 ---- gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h | 77 - gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp | 1011 ------------ gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h | 83 - gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp | 442 ----- gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h | 69 - gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp | 330 ---- gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h | 75 - gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h | 49 - gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp | 489 ------ gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h | 121 -- gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp | 1062 ------------ gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h | 57 - gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp | 666 -------- gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h | 152 -- gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp | 326 ---- gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h | 77 - gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp | 412 ----- gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h | 84 - gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp | 314 ---- gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h | 71 - gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp | 21 - gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h | 33 - gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp | 1319 --------------- gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h | 38 - gme/Game_Music_Emu-0.5.2/gme/blargg_common.h | 179 -- gme/Game_Music_Emu-0.5.2/gme/blargg_config.h | 30 - gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h | 158 -- gme/Game_Music_Emu-0.5.2/gme/blargg_source.h | 78 - gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h | 72 - gme/Game_Music_Emu-0.5.2/gme/gme.cpp | 256 --- gme/Game_Music_Emu-0.5.2/gme/gme.h | 222 --- gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h | 101 -- gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h | 83 - gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h | 26 - gme/Game_Music_Emu-0.5.2/license.txt | 504 ------ gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp | 198 --- gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h | 36 - gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp | 231 --- gme/Game_Music_Emu-0.5.2/player/Music_Player.h | 69 - gme/Game_Music_Emu-0.5.2/player/player.cpp | 213 --- gme/Game_Music_Emu-0.5.2/readme.txt | 205 --- gme/Game_Music_Emu-0.5.2/test.m3u | 2 - gme/Game_Music_Emu-0.5.2/test.nsf | Bin 749 -> 0 bytes moduleconf.h | 1 - plugins/gme/Game_Music_Emu-0.5.2/Makefile.am | 3 + plugins/gme/Game_Music_Emu-0.5.2/changes.txt | 218 +++ .../gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp | 182 +++ .../gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h | 73 + plugins/gme/Game_Music_Emu-0.5.2/demo/basics.c | 57 + .../gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp | 67 + plugins/gme/Game_Music_Emu-0.5.2/demo/features.c | 149 ++ plugins/gme/Game_Music_Emu-0.5.2/design.txt | 194 +++ plugins/gme/Game_Music_Emu-0.5.2/gme.txt | 464 ++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp | 395 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h | 107 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp | 1665 +++++++++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h | 92 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp | 404 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h | 70 + .../gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp | 446 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h | 485 ++++++ .../gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp | 184 +++ plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h | 127 ++ .../gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp | 315 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h | 151 ++ .../Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp | 131 ++ .../gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h | 50 + .../Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp | 529 ++++++ .../gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h | 86 + .../gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp | 199 +++ .../gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h | 171 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp | 306 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h | 90 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp | 1056 ++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h | 93 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp | 336 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h | 83 + plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp | 288 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h | 88 + plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp | 216 +++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h | 145 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp | 379 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h | 82 + plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp | 315 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h | 66 + plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp | 1303 +++++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h | 125 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp | 529 ++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h | 94 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp | 1706 ++++++++++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h | 124 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp | 414 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h | 96 ++ .../gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp | 97 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h | 106 ++ .../gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp | 426 +++++ .../gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h | 67 + plugins/gme/Game_Music_Emu-0.5.2/gme/Makefile.am | 66 + .../gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp | 232 +++ .../gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h | 156 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp | 410 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h | 211 +++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp | 391 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h | 179 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp | 1084 +++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h | 114 ++ .../gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp | 121 ++ .../gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h | 131 ++ .../gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp | 145 ++ .../gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h | 102 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp | 551 +++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h | 147 ++ .../gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp | 215 +++ .../gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h | 95 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp | 557 +++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h | 106 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp | 330 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h | 68 + plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp | 334 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h | 77 + plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp | 1011 ++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h | 83 + plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp | 442 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h | 69 + plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp | 330 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h | 75 + plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h | 49 + plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp | 489 ++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h | 121 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp | 1062 ++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h | 57 + plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp | 666 ++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h | 152 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp | 326 ++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h | 77 + plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp | 412 +++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h | 84 + .../gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp | 314 ++++ .../gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h | 71 + .../gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp | 21 + plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h | 33 + .../gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp | 1319 +++++++++++++++ plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h | 38 + .../gme/Game_Music_Emu-0.5.2/gme/blargg_common.h | 179 ++ .../gme/Game_Music_Emu-0.5.2/gme/blargg_config.h | 30 + .../gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h | 158 ++ .../gme/Game_Music_Emu-0.5.2/gme/blargg_source.h | 78 + plugins/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h | 72 + plugins/gme/Game_Music_Emu-0.5.2/gme/gme.cpp | 256 +++ plugins/gme/Game_Music_Emu-0.5.2/gme/gme.h | 222 +++ plugins/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h | 101 ++ plugins/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h | 83 + plugins/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h | 26 + plugins/gme/Game_Music_Emu-0.5.2/license.txt | 504 ++++++ .../Game_Music_Emu-0.5.2/player/Audio_Scope.cpp | 198 +++ .../gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h | 36 + .../Game_Music_Emu-0.5.2/player/Music_Player.cpp | 231 +++ .../gme/Game_Music_Emu-0.5.2/player/Music_Player.h | 69 + plugins/gme/Game_Music_Emu-0.5.2/player/player.cpp | 213 +++ plugins/gme/Game_Music_Emu-0.5.2/readme.txt | 205 +++ plugins/gme/Game_Music_Emu-0.5.2/test.m3u | 2 + plugins/gme/Game_Music_Emu-0.5.2/test.nsf | Bin 0 -> 749 bytes plugins/gme/Makefile.am | 12 + plugins/gme/cgme.c | 248 +++ 242 files changed, 31703 insertions(+), 31686 deletions(-) delete mode 100644 cgme.c delete mode 100644 gme/Game_Music_Emu-0.5.2/Makefile.am delete mode 100644 gme/Game_Music_Emu-0.5.2/changes.txt delete mode 100644 gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h delete mode 100644 gme/Game_Music_Emu-0.5.2/demo/basics.c delete mode 100644 gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/demo/features.c delete mode 100644 gme/Game_Music_Emu-0.5.2/design.txt delete mode 100644 gme/Game_Music_Emu-0.5.2/gme.txt delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gme_File.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Makefile.am delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/blargg_common.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/blargg_config.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/blargg_source.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/gme.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/gme.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h delete mode 100644 gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h delete mode 100644 gme/Game_Music_Emu-0.5.2/license.txt delete mode 100644 gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h delete mode 100644 gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/player/Music_Player.h delete mode 100644 gme/Game_Music_Emu-0.5.2/player/player.cpp delete mode 100644 gme/Game_Music_Emu-0.5.2/readme.txt delete mode 100644 gme/Game_Music_Emu-0.5.2/test.m3u delete mode 100644 gme/Game_Music_Emu-0.5.2/test.nsf create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/Makefile.am create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/changes.txt create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/demo/basics.c create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/demo/features.c create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/design.txt create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme.txt create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Makefile.am create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/gme.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/gme.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/license.txt create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.h create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/player/player.cpp create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/readme.txt create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/test.m3u create mode 100644 plugins/gme/Game_Music_Emu-0.5.2/test.nsf create mode 100644 plugins/gme/Makefile.am create mode 100644 plugins/gme/cgme.c diff --git a/Makefile.am b/Makefile.am index 621282a9..4038e09b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,10 @@ SUBDIRS = ${MPGMAD_DIR}\ - gme/Game_Music_Emu-0.5.2\ - gme/Game_Music_Emu-0.5.2/gme\ dumb\ pixmaps\ icons\ ${PLUGINS_DIRS} dumbpath=@top_srcdir@/dumb -gmepath=@top_srcdir@/gme/Game_Music_Emu-0.5.2 bin_PROGRAMS = deadbeef @@ -26,16 +23,16 @@ deadbeef_SOURCES =\ optmath.h\ vfs.c vfs.h vfs_stdio.c\ timeline.c timeline.h\ - cgme.c cdumb.c\ + cdumb.c\ md5/md5.c md5/md5.h\ metacache.c sdkdir = $(pkgincludedir) sdk_HEADERS = deadbeef.h -deadbeef_LDADD = $(LDADD) $(DEPS_LIBS) $(ICONV_LIB) gme/Game_Music_Emu-0.5.2/gme/libgme.a dumb/libdumb.a -lstdc++ -lm +deadbeef_LDADD = $(LDADD) $(DEPS_LIBS) $(ICONV_LIB) dumb/libdumb.a -lstdc++ -lm -AM_CFLAGS = $(DEPS_CFLAGS) -I$(gmepath) -std=c99 +AM_CFLAGS = $(DEPS_CFLAGS) -std=c99 AM_CPPFLAGS = $(DEPS_CFLAGS) docsdir = $(docdir) diff --git a/cgme.c b/cgme.c deleted file mode 100644 index 2bed740c..00000000 --- a/cgme.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko - - 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, see . -*/ -#include -#include -#include -#include "gme/gme.h" -#include "deadbeef.h" - -static DB_decoder_t plugin; -static DB_functions_t *deadbeef; - -typedef struct { - DB_fileinfo_t info; - Music_Emu *emu; - int reallength; - uint32_t cgme_voicemask; - float duration; // of current song -} gme_info_t; - -static DB_fileinfo_t * -cgme_init (DB_playItem_t *it) { - DB_fileinfo_t *_info = malloc (sizeof (gme_info_t)); - gme_info_t *info = (gme_info_t*)_info; - memset (_info, 0, sizeof (gme_info_t)); - int samplerate = deadbeef->conf_get_int ("synth.samplerate", 48000); - if (gme_open_file (it->fname, &info->emu, samplerate)) { - plugin.free (_info); - return NULL; - } - gme_mute_voices (info->emu, info->cgme_voicemask); - gme_start_track (info->emu, it->tracknum); - - track_info_t inf; - - gme_track_info (info->emu, &inf, it->tracknum); - - _info->plugin = &plugin; - _info->bps = 16; - _info->channels = 2; - _info->samplerate = samplerate; - info->duration = deadbeef->pl_get_item_duration (it); - info->reallength = inf.length; - _info->readpos = 0; - return _info; -} - -static void -cgme_free (DB_fileinfo_t *_info) { - gme_info_t *info = (gme_info_t*)_info; - if (info->emu) { - gme_delete (info->emu); - } - free (info); -} - -static int -cgme_read (DB_fileinfo_t *_info, char *bytes, int size) { - gme_info_t *info = (gme_info_t*)_info; - float t = (size/4) / (float)_info->samplerate; - if (_info->readpos + t >= info->duration) { - t = info->duration - _info->readpos; - if (t <= 0) { - return 0; - } - // DON'T ajust size, buffer must always be po2 - //size = t * (float)info->samplerate * 4; - } - if (gme_play (info->emu, size/2, (short*)bytes)) { - return 0; - } - _info->readpos += t; - if (info->reallength == -1) { - if (gme_track_ended (info->emu)) { - return 0; - } - } - return size; -} - -static int -cgme_seek (DB_fileinfo_t *_info, float time) { - gme_info_t *info = (gme_info_t*)_info; - if (gme_seek (info->emu, (long)(time * 1000))) { - return -1; - } - _info->readpos = time; - return 0; -} - -static DB_playItem_t * -cgme_insert (DB_playItem_t *after, const char *fname) { - Music_Emu *emu; - if (!gme_open_file (fname, &emu, gme_info_only)) { - int cnt = gme_track_count (emu); - for (int i = 0; i < cnt; i++) { - track_info_t inf; - const char *ret = gme_track_info (emu, &inf, i); - if (!ret) { - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - char str[1024]; - if (inf.song[0]) { - snprintf (str, 1024, "%d %s - %s", i, inf.game, inf.song); - } - else { - snprintf (str, 1024, "%d %s - ?", i, inf.game); - } - it->tracknum = i; - - // add metadata - deadbeef->pl_add_meta (it, "system", inf.system); - deadbeef->pl_add_meta (it, "album", inf.game); - int tl = sizeof (inf.song); - int n; - for (n = 0; i < tl && inf.song[n] && inf.song[n] == ' '; n++); - if (n == tl || !inf.song[n]) { - deadbeef->pl_add_meta (it, "title", NULL); - } - else { - deadbeef->pl_add_meta (it, "title", inf.song); - } - deadbeef->pl_add_meta (it, "artist", inf.author); - deadbeef->pl_add_meta (it, "copyright", inf.copyright); - deadbeef->pl_add_meta (it, "comment", inf.comment); - deadbeef->pl_add_meta (it, "dumper", inf.dumper); - char trk[10]; - snprintf (trk, 10, "%d", i+1); - deadbeef->pl_add_meta (it, "track", trk); - if (inf.length == -1) { - float songlength = deadbeef->conf_get_float ("gme.songlength", 3); - deadbeef->pl_set_item_duration (it, songlength * 60.f); - } - else { - deadbeef->pl_set_item_duration (it, (float)inf.length/1000.f); - } - const char *ext = fname + strlen (fname) - 1; - while (ext >= fname && *ext != '.') { - ext--; - } - it->filetype = NULL; - if (*ext == '.') { - ext++; - for (int i = 0; plugin.exts[i]; i++) { - if (!strcasecmp (ext, plugin.exts[i])) { - it->filetype = plugin.exts[i]; - } - } - } - after = deadbeef->pl_insert_item (after, it); - deadbeef->pl_item_unref (it); - } - else { - printf ("gme error: %s\n", ret); - } - } - if (emu) { - gme_delete (emu); - } - } - else { - printf ("error adding %s\n", fname); - } - return after; -} - -static const char * exts[]= -{ - "ay","gbs","gym","hes","kss","nsf","nsfe","sap","spc","vgm","vgz",NULL -}; - -#if 0 -static int -cgme_numvoices (void) { - if (!emu) { - return 0; - } - return gme_voice_count (emu); -} - -static void -cgme_mutevoice (int voice, int mute) { - cgme_voicemask &= ~ (1< -#include - -/* Copyright (C) 2003-2006 by Shay Green. Permission is hereby granted, free -of charge, to any person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and -to permit persons to whom the Software is furnished to do so, subject to the -following conditions: The above copyright notice and this permission notice -shall be included in all copies or substantial portions of the Software. THE -SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -const int header_size = 0x2C; - -static void exit_with_error( const char* str ) -{ - printf( "Error: %s\n", str ); getchar(); - exit( EXIT_FAILURE ); -} - -Wave_Writer::Wave_Writer( long sample_rate, const char* filename ) -{ - sample_count_ = 0; - rate = sample_rate; - buf_pos = header_size; - chan_count = 1; - - buf = (unsigned char*) malloc( buf_size * sizeof *buf ); - if ( !buf ) - exit_with_error( "Out of memory" ); - - file = fopen( filename, "wb" ); - if ( !file ) - exit_with_error( "Couldn't open WAVE file for writing" ); - - setvbuf( file, 0, _IOFBF, 32 * 1024L ); -} - -void Wave_Writer::flush() -{ - if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) ) - exit_with_error( "Couldn't write WAVE data" ); - buf_pos = 0; -} - -void Wave_Writer::write( const sample_t* in, long remain, int skip ) -{ - sample_count_ += remain; - while ( remain ) - { - if ( buf_pos >= buf_size ) - flush(); - - long n = (buf_size - buf_pos) / sizeof (sample_t); - if ( n > remain ) - n = remain; - remain -= n; - - // convert to lsb first format - unsigned char* p = &buf [buf_pos]; - while ( n-- ) - { - int s = *in; - in += skip; - *p++ = (unsigned char) s; - *p++ = (unsigned char) (s >> 8); - } - - buf_pos = p - buf; - assert( buf_pos <= buf_size ); - } -} - - -void Wave_Writer::write( const float* in, long remain, int skip ) -{ - sample_count_ += remain; - while ( remain ) - { - if ( buf_pos >= buf_size ) - flush(); - - long n = (buf_size - buf_pos) / sizeof (sample_t); - if ( n > remain ) - n = remain; - remain -= n; - - // convert to lsb first format - unsigned char* p = &buf [buf_pos]; - while ( n-- ) - { - long s = (long) (*in * 0x7FFF); - in += skip; - if ( (short) s != s ) - s = 0x7FFF - (s >> 24); // clamp to 16 bits - *p++ = (unsigned char) s; - *p++ = (unsigned char) (s >> 8); - } - - buf_pos = p - buf; - assert( buf_pos <= buf_size ); - } -} - -void Wave_Writer::close() -{ - if ( file ) - { - flush(); - - // generate header - long ds = sample_count_ * sizeof (sample_t); - long rs = header_size - 8 + ds; - int frame_size = chan_count * sizeof (sample_t); - long bps = rate * frame_size; - unsigned char header [header_size] = - { - 'R','I','F','F', - rs,rs>>8, // length of rest of file - rs>>16,rs>>24, - 'W','A','V','E', - 'f','m','t',' ', - 0x10,0,0,0, // size of fmt chunk - 1,0, // uncompressed format - chan_count,0, // channel count - rate,rate >> 8, // sample rate - rate>>16,rate>>24, - bps,bps>>8, // bytes per second - bps>>16,bps>>24, - frame_size,0, // bytes per sample frame - 16,0, // bits per sample - 'd','a','t','a', - ds,ds>>8,ds>>16,ds>>24// size of sample data - // ... // sample data - }; - - // write header - fseek( file, 0, SEEK_SET ); - fwrite( header, sizeof header, 1, file ); - - fclose( file ); - file = 0; - free( buf ); - } -} - -Wave_Writer::~Wave_Writer() -{ - close(); -} - -// C interface - -static Wave_Writer* ww; - -void wave_open( long sample_rate, const char* filename ) -{ - ww = new Wave_Writer( sample_rate, filename ); - assert( ww ); -} - -void wave_enable_stereo() { ww->enable_stereo(); } - -long wave_sample_count() { return ww->sample_count(); } - -void wave_write( const short* buf, long count ) { ww->write( buf, count ); } - -void wave_close() -{ - delete ww; - ww = 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h b/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h deleted file mode 100644 index da08cc2a..00000000 --- a/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h +++ /dev/null @@ -1,73 +0,0 @@ -/* WAVE sound file writer for recording 16-bit output during program development */ - -#ifndef WAVE_WRITER_H -#define WAVE_WRITER_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* C interface */ -void wave_open( long sample_rate, const char* filename ); -void wave_enable_stereo( void ); -void wave_write( const short* buf, long count ); -long wave_sample_count( void ); -void wave_close( void ); - -#ifdef __cplusplus - } -#endif - -#ifdef __cplusplus -#include -#include - -/* C++ interface */ -class Wave_Writer { -public: - typedef short sample_t; - - // Create sound file with given sample rate (in Hz) and filename. - // Exits program if there's an error. - Wave_Writer( long sample_rate, char const* filename = "out.wav" ); - - // Enable stereo output - void enable_stereo(); - - // Append 'count' samples to file. Use every 'skip'th source sample; allows - // one channel of stereo sample pairs to be written by specifying a skip of 2. - void write( const sample_t*, long count, int skip = 1 ); - - // Append 'count' floating-point samples to file. Use every 'skip'th source sample; - // allows one channel of stereo sample pairs to be written by specifying a skip of 2. - void write( const float*, long count, int skip = 1 ); - - // Number of samples written so far - long sample_count() const; - - // Finish writing sound file and close it - void close(); - - ~Wave_Writer(); -public: - // Deprecated - void stereo( bool b ) { chan_count = b ? 2 : 1; } -private: - enum { buf_size = 32768 * 2 }; - unsigned char* buf; - FILE* file; - long sample_count_; - long rate; - long buf_pos; - int chan_count; - - void flush(); -}; - -inline void Wave_Writer::enable_stereo() { chan_count = 2; } - -inline long Wave_Writer::sample_count() const { return sample_count_; } - -#endif - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/demo/basics.c b/gme/Game_Music_Emu-0.5.2/demo/basics.c deleted file mode 100644 index 55178251..00000000 --- a/gme/Game_Music_Emu-0.5.2/demo/basics.c +++ /dev/null @@ -1,57 +0,0 @@ -/* C example that opens a game music file and records 10 seconds to "out.wav" */ - -static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ - -#include "gme/gme.h" - -#include "Wave_Writer.h" /* wave_ functions for writing sound file */ -#include -#include - -void handle_error( const char* str ); - -int main() -{ - long sample_rate = 44100; /* number of samples per second */ - int track = 0; /* index of track to play (0 = first) */ - - /* Open music file in new emulator */ - Music_Emu* emu; - handle_error( gme_open_file( filename, &emu, sample_rate ) ); - - /* Start track */ - handle_error( gme_start_track( emu, track ) ); - - /* Begin writing to wave file */ - wave_open( sample_rate, "out.wav" ); - wave_enable_stereo(); - - /* Record 10 seconds of track */ - while ( gme_tell( emu ) < 10 * 1000L ) - { - /* Sample buffer */ - #define buf_size 1024 /* can be any multiple of 2 */ - short buf [buf_size]; - - /* Fill sample buffer */ - handle_error( gme_play( emu, buf_size, buf ) ); - - /* Write samples to wave file */ - wave_write( buf, buf_size ); - } - - /* Cleanup */ - gme_delete( emu ); - wave_close(); - - return 0; -} - -void handle_error( const char* str ) -{ - if ( str ) - { - printf( "Error: %s\n", str ); getchar(); - exit( EXIT_FAILURE ); - } -} diff --git a/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp b/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp deleted file mode 100644 index 53fab418..00000000 --- a/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// C++ example that opens a game music file and records 10 seconds to "out.wav" - -static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ - -#include "gme/Music_Emu.h" - -#include "Wave_Writer.h" -#include -#include - -void handle_error( const char* str ); - -int main() -{ - long sample_rate = 44100; // number of samples per second - int track = 0; // index of track to play (0 = first) - - // Determine file type - gme_type_t file_type; - handle_error( gme_identify_file( filename, &file_type ) ); - if ( !file_type ) - handle_error( "Unsupported music type" ); - - // Create emulator and set sample rate - Music_Emu* emu = file_type->new_emu(); - if ( !emu ) - handle_error( "Out of memory" ); - handle_error( emu->set_sample_rate( sample_rate ) ); - - // Load music file into emulator - handle_error( emu->load_file( filename ) ); - - // Start track - handle_error( emu->start_track( track ) ); - - // Begin writing to wave file - Wave_Writer wave( sample_rate, "out.wav" ); - wave.enable_stereo(); - - // Record 10 seconds of track - while ( emu->tell() < 10 * 1000L ) - { - // Sample buffer - const long size = 1024; // can be any multiple of 2 - short buf [size]; - - // Fill buffer - handle_error( emu->play( size, buf ) ); - - // Write samples to wave file - wave.write( buf, size ); - } - - // Cleanup - delete emu; - - return 0; -} - -void handle_error( const char* str ) -{ - if ( str ) - { - printf( "Error: %s\n", str ); getchar(); - exit( EXIT_FAILURE ); - } -} diff --git a/gme/Game_Music_Emu-0.5.2/demo/features.c b/gme/Game_Music_Emu-0.5.2/demo/features.c deleted file mode 100644 index 96a9a8a9..00000000 --- a/gme/Game_Music_Emu-0.5.2/demo/features.c +++ /dev/null @@ -1,149 +0,0 @@ -/* C example that opens any music file type, opens an m3u playlist if present, -prints its info and voice names, customizes the sound, and fades a track out. -Records to "out.wav". */ - -static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ -static char playlist [] = "test.m3u"; /* uses this playlist, if present*/ - -#include "gme/gme.h" - -#include "Wave_Writer.h" /* wave_ functions for writing sound file */ -#include -#include - -void handle_error( const char* ); - -/* Example of loading from memory, which would be useful if using a zip file or -other custom format. In this example it's silly because we could just use -gme_load( &emu, sample_rate, path, 0 ). */ -Music_Emu* load_file( const char* path, long sample_rate ) -{ - Music_Emu* emu; - char* data; - long size; - - /* Read file data into memory. You might read the data from a zip file or - other compressed format. */ - FILE* in = fopen( path, "rb" ); - if ( !in ) - handle_error( "Couldn't open file" ); - fseek( in, 0, SEEK_END ); - size = ftell( in ); - rewind( in ); - - data = malloc( size ); - if ( !data ) - handle_error( "Out of memory" ); - if ( fread( data, size, 1, in ) <= 0 ) - handle_error( "Read error" ); - fclose( in ); - - handle_error( gme_open_data( data, size, &emu, sample_rate ) ); - free( data ); /* a copy is made of the data */ - return emu; -} - -/* Print any warning for most recent emulator action (load, start_track, play) */ -void print_warning( Music_Emu* emu ) -{ - const char* warning = gme_warning( emu ); - if ( warning ) - printf( "**** Warning: %s\n\n", warning ); -} - -static char my_data [] = "Our cleanup function was called"; - -/* Example cleanup function automatically called when emulator is deleted. */ -static void my_cleanup( void* my_data ) -{ - printf( "\n%s\n", (char*) my_data ); -} - -int main() -{ - long sample_rate = 44100; - int track = 0; /* index of track to play (0 = first) */ - int i; - - /* Load file into emulator */ - Music_Emu* emu = load_file( filename, sample_rate ); - print_warning( emu ); - - /* Register cleanup function and confirmation string as data */ - gme_set_user_data( emu, my_data ); - gme_set_user_cleanup( emu, my_cleanup ); - - /* Load .m3u playlist file. All tracks are assumed to use current file. - We ignore error here in case there is no m3u file present. */ - gme_load_m3u( emu, playlist ); - print_warning( emu ); - - /* Get and print main info for track */ - { - track_info_t info; - handle_error( gme_track_info( emu, &info, track ) ); - printf( "System : %s\n", info.system ); - printf( "Game : %s\n", info.game ); - printf( "Author : %s\n", info.author ); - printf( "Copyright: %s\n", info.copyright ); - printf( "Comment : %s\n", info.comment ); - printf( "Dumper : %s\n", info.dumper ); - printf( "Tracks : %d\n", (int) info.track_count ); - printf( "\n" ); - printf( "Track : %d\n", (int) track + 1 ); - printf( "Name : %s\n", info.song ); - printf( "Length : %ld:%02ld", - (long) info.length / 1000 / 60, (long) info.length / 1000 % 60 ); - if ( info.loop_length != 0 ) - printf( " (endless)" ); - printf( "\n\n" ); - } - - /* Print voice names */ - for ( i = 0; i < gme_voice_count( emu ); i++ ) - printf( "Voice %d: %s\n", i, gme_voice_names( emu ) [i] ); - - /* Add some stereo enhancement */ - gme_set_stereo_depth( emu, 0.20 ); - - /* Adjust equalizer for crisp, bassy sound */ - { - gme_equalizer_t eq; - eq.treble = 0.0; - eq.bass = 20; - gme_set_equalizer( emu, &eq ); - } - - /* Start track and begin fade at 10 seconds */ - handle_error( gme_start_track( emu, track ) ); - print_warning( emu ); - gme_set_fade( emu, 10 * 1000L ); - - /* Record track until it ends */ - wave_open( sample_rate, "out.wav" ); - wave_enable_stereo(); - while ( !gme_track_ended( emu ) ) - { - #define buf_size 1024 - short buf [buf_size]; - handle_error( gme_play( emu, buf_size, buf ) ); - print_warning( emu ); - wave_write( buf, buf_size ); - } - - /* Cleanup */ - gme_delete( emu ); - wave_close(); - - getchar(); - return 0; -} - -void handle_error( const char* str ) -{ - if ( str ) - { - printf( "Error: %s\n", str ); getchar(); - exit( EXIT_FAILURE ); - } -} diff --git a/gme/Game_Music_Emu-0.5.2/design.txt b/gme/Game_Music_Emu-0.5.2/design.txt deleted file mode 100644 index 8c8c65b1..00000000 --- a/gme/Game_Music_Emu-0.5.2/design.txt +++ /dev/null @@ -1,194 +0,0 @@ -Game_Music_Emu 0.5.2 Design ---------------------------- -This might be slightly out-of-date at times, but will be a big help in -understanding the library implementation. - - -Architecture ------------- -The library is essentially a bunch of independent game music file -emulators unified with a common interface. - -Gme_File and Music_Emu provide a common interface to the emulators. The -virtual functions are protected rather than public to allow pre- and -post-processing of arguments and data in one place. This allows the -emulator classes to assume that everything is set up properly when -starting a track and playing samples. - -All file input is done with the Data_Reader interface. Many derived -classes are present, for the usual disk-based file and block of memory, -to specialized adaptors for things like reading a subset of data or -combining a block of memory with a Data_Reader to the remaining data. -This makes the library much more flexible with regard to the source of -game music file data. I still added a specialized load_mem() function to -have the emulator keep a pointer to data already read in memory, for -those formats whose files can be absolutely huge (GYM, some VGMs). This -is important if for some reason the caller must load the data ahead of -time, but doesn't want the emulator needlessly making a copy. - -Since silence checking and fading are relatively complex, they are kept -separate from basic file loading and track information, which are -handled in the base class Gme_File. My original intent was to use -Gme_File as the common base class for full emulators and track -information-only readers, but implementing the C interface was much -simpler if both derived from Music_Emu. User C++ code can still benefit -from static checking by using Gme_File where only track information will -be accessed. - -Each emulator generally has three components: main emulator, CPU -emulator, and sound chip emulator(s). Each component has minimal -coupling, so use in a full emulator or stand alone is fairly easy. This -modularity really helps reduce complexity. Blip_Buffer helps a lot with -simplifying the APU interfaces and implementation. - -The "classic" emulators derive from Classic_Emu, which handles -Blip_Buffer filling and multiple channels. It uses Multi_Buffer for -output, allowing you to derive a custom buffer that could output each -voice to a separate sound channel and do different processing on each. -At some point I'm going to implement a better Effects_Buffer that allows -individual control of every channel. - -In implementing the C interface, I wanted a way to specify an emulator -type that didn't require linking in all the emulators. For each emulator -type there is a global object with pointers to functions to create the -emulator or a track information reader. The emulator type is thus a -pointer to this, which conveniently allows for a NULL value. The user -referencing this emulator type object is what ultimately links the -emulator in (unless new Foo_Emu is used in C++, of course). This type -also serves as a useful substitute for RTTI on older C++ compilers. - -Addendum: I have since added gme_type_list(), which causes all listed -emulators to be linked in. To avoid this, I make the list itself -editable in blargg_config.h. Having a built-in list allows -gme_load_file() to take a path and give back an emulator with the file -loaded, which is extremely useful for new users. - - -Interface conventions ----------------------- -If a function retains a pointer to or replaces the value of an object -passed, it takes a pointer so that it will be clear in the caller's -source code that care is required. - -Multi-word names have an underscore '_' separator between individual -words. - -Functions are named with lowercase words. Functions which perform an -action with side-effects are named with a verb phrase (i.e. load, move, -run). Functions which return the value of a piece of state are named -using a noun phrase (i.e. loaded, moved, running). - -Classes are named with capitalized words. Only the first letter of an -acronym is capitalized. Class names are nouns, sometimes suggestive of -what they do (i.e. File_Scanner). - -Structure, enumeration, and typedefs to these and built-in types are -named using lowercase words with a _t suffix. - -Macros are named with all-uppercase words. - -Internal names which can't be hidden due to technical reasons have an -underscore '_' suffix. - - -Managing Complexity -------------------- -Complexity has been a factor in most library decisions. Many features -have been passed by due to the complexity they would add. Once -complexity goes past a certain level, it mentally grasping the library -in its entirety, at which point more defects will occur and be hard to -find. - -I chose 16-bit signed samples because it seems to be the most common -format. Supporting multiple formats would add too much complexity to be -worth it. Other formats can be obtained via conversion. - -I've kept interfaces fairly lean, leaving many possible features -untapped but easy to add if necessary. For example the classic emulators -could have volume and frequency equalization adjusted separately for -each channel, since they each have an associated Blip_Synth. - -Source files of 400 lines or less seem to be the best size to limit -complexity. In a few cases there is no reasonable way to split longer -files, or there is benefit from having the source together in one file. - - -Preventing Bugs ---------------- -I've done many things to reduce the opportunity for defects. A general -principle is to write code so that defects will be as visible as -possible. I've used several techniques to achieve this. - -I put assertions at key points where defects seem likely or where -corruption due to a defect is likely to be visible. I've also put -assertions where violations of the interface are likely. In emulators -where I am unsure of exact hardware operation in a particular case, I -output a debug-only message noting that this has occurred; many times I -haven't implemented a hardware feature because nothing uses it. I've -made code brittle where there is no clear reason flexibility; code -written to handle every possibility sacrifices quality and reliability -to handle vaguely defined situations. - - -Flexibility through indirection -------------------------------- -I've tried to allow the most flexibility of modules by using indirection -to allow extension by the user. This keeps each module simpler and more -focused on its unique task. - -The classic emulators use Multi_Buffer, which potentially allows a -separate Blip_Buffer for each channel. This keeps emulators free of -typical code to allow output in mono, stereo, panning, etc. - -All emulators use a reader object to access file data, allowing it to be -stored in a regular file, compressed archive, memory, or generated -on-the-fly. Again, the library can be kept free of the particulars of -file access and changes required to support new formats. - - -Emulators in general --------------------- -When I wrote the first NES sound emulator, I stored most of the state in -an emulator-specific format, with significant redundancy. In the -register write function I decoded everything into named variables. I -became tired of the verbosity and wanted to more closely model the -hardware, so I moved to a style of storing the last written value to -each register, along with as little other state as possible, mostly the -internal hardware registers. While this involves slightly more -recalculation, in most cases the emulation code is of comparable size. -It also makes state save/restore (for use in a full emulator) much -simpler. Finally, it makes debugging easier since the hardware registers -used in emulation are obvious. - - -CPU Cores ---------- -I've spent lots of time coming up with techniques to optimize the CPU -cores. Some of the most important: execute multiple instructions during -an emulation call, keep state in local variables to allow register -assignment, optimize state representation for most common instructions, -defer status flag calculation until actually needed, read program code -directly without a call to the memory read function, always pre-fetch -the operand byte before decoding instruction, and emulate instructions -using common blocks of code. - -I've successfully used Nes_Cpu in a fairly complete NES emulator, and -I'd like to make all the CPU emulators suitable for use in emulators. It -seems a waste for them to be used only for the small amount of emulation -necessary for game music files. - -I debugged the CPU cores by writing a test shell that ran them in -parallel with other CPU cores and compared all memory accesses and -processor states at each step. This provided good value at little cost. - -The CPU mapping page size is adjustable to allow the best tradeoff -between memory/cache usage and handler granularity. The interface allows -code to be somewhat independent of the page size. - -I optimize program memory accesses to direct reads rather than calls to -the memory read function. My assumption is that it would be difficult to -get useful code out of hardware I/O addresses, so no software will -intentionally execute out of I/O space. Since the page size can be -changed easily, most program memory mapping schemes can be accommodated. -This greatly reduces memory access function calls. - diff --git a/gme/Game_Music_Emu-0.5.2/gme.txt b/gme/Game_Music_Emu-0.5.2/gme.txt deleted file mode 100644 index 2c963d8e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme.txt +++ /dev/null @@ -1,464 +0,0 @@ -Game_Music_Emu 0.5.2 --------------------- -Author : Shay Green -Website: http://www.slack.net/~ant/libs/ -Forum : http://groups.google.com/group/blargg-sound-libs -License: GNU Lesser General Public License (LGPL) - -Contents --------- -* Overview -* C and C++ interfaces -* Function reference -* Error handling -* Emulator types -* M3U playlist support -* Information fields -* Track length -* Loading file data -* Sound parameters -* VGM/GYM YM2413 & YM2612 FM sound -* Modular construction -* Obscure features -* Solving problems -* Deprecated features -* Thanks - - -Overview --------- -This library can open game music files, play tracks, and read game and -track information tags. To play a game music file, do the following: - -* Open the file with gme_open_file() -* Start a track with gme_start_track(); -* Generate samples as needed with gme_play() -* Play samples through speaker using your operating system -* Delete emulator when done with gme_delete() - -Your code must arrange for the generated samples to be played through -the computer's speaker using whatever method your operating system -requires. - -There are many additional features available; you can: - -* Determine of the type of a music file without opening it with -gme_identify_*() -* Load just the file's information tags with gme_info_only -* Load from a block of memory rather than a file with gme_load_data() -* Arrange for a fade-out at a particular time with gme_set_fade -* Find when a track has ended with gme_track_ended() -* Seek to a new time in the track with gme_seek() -* Load an extended m3u playlist with gme_load_m3u() -* Get a list of the voices (channels) and mute them individually with -gme_voice_names() and gme_mute_voice() -* Change the playback tempo without affecting pitch with gme_set_tempo() -* Adjust treble/bass equalization with gme_set_equalizer() -* Associate your own data with an emulator and later get it back with -gme_set_user_data() -* Register a function of yours to be called back when the emulator is -deleted with gme_set_user_cleanup() - -Refer to gme.h for a comprehensive summary of features. - - -C and C++ interfaces --------------------- -While the library is written in C++, an extensive C interface is -provided in gme.h. This C interface will be referred to throughout this -documentation unless a feature is only available in the full C++ -interface. All C interface functions and other names have the gme_ -prefix, so you can recognize a C++-only feature by the lack of gme_ in -the names used (contact me if you'd like a feature added to the C -interface). If you're building a shared library, I highly recommend -sticking to the C interface only, because it will be more stable between -releases of the library than the C++ interface. Finally, the C and C++ -interfaces can be freely mixed without problems. Compare demo/basics.c -with demo/cpp_basics.cpp to see how the C and C++ interfaces translate -between each other. - - -Function reference ------------------- -Read the following header files for a complete reference to functions -and features. The second group of header files can only be used in C++. - -blargg_config.h Library configuration -gme.h C interface (also usable from C++) - -Gme_File.h File loading and track information -Music_Emu.h Track playback and adjustments -Data_Reader.h Custom data readers -Effects_Buffer.h Sound buffer with adjustable stereo echo and panning -M3u_Playlist.h M3U playlist support -Gbs_Emu.h GBS equalizer settings -Nsf_Emu.h NSF equalizer settings -Spc_Emu.h SPC surround disable -Vgm_Emu.h VGM oversampling disable and custom buffer query - - -Error handling --------------- -Functions which can fail have a return type of gme_err_t (blargg_err_t -in the C++ interfaces), which is a pointer to an error string (const -char*). If a function is successful it returns NULL. Errors that you can -easily avoid are checked with debug assertions; gme_err_t return values -are only used for genuine run-time errors that can't be easily predicted -in advance (out of memory, I/O errors, incompatible file data). Your -code should check all error values. - -To improve usability for C programmers, C++ programmers unfamiliar with -exceptions, and compatibility with older C++ compilers, the library does -*not* throw any C++ exceptions and uses malloc() instead of the standard -operator new. This means that you *must* check for NULL when creating a -library object with the new operator. - -When loading a music file in the wrong emulator or trying to load a -non-music file, gme_wrong_file_type is returned. You can check for this -error in C++ like this: - - gme_err_t err = gme_open_file( path, &emu ); - if ( err == gme_wrong_file_type ) - ... - -To check for minor problems, call gme_warning() to get a string -describing the last warning. Your player should allow the user some way -of knowing when this is the case, since these minor errors could affect -playback. Without this information the user can't solve problems as -well. When playing a track, gme_warning() returns minor playback-related -problems (major playback problems end the track immediately and set the -warning string). - - -Emulator types --------------- -The library includes several game music emulators that each support a -different file type. Each is identified by a gme_type_t constant defined -in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use -gme_open_file() or gme_open_data(), the library does the work of -determining the file type and creating an appropriate emulator. If you -want more control over this process, read on. - -There are two basic ways to identify a game music file's type: look at -its file extension, or read the header data. The library includes -functions to help with both methods. The first is preferable because it -is fast and the most common way to identify files. Sometimes the -extension is lost or wrong, so the header must be read. - -Use gme_identify_extension() to find the correct game music type based -on a filename. To identify a file based on its extension and header -contents, use gme_identify_file(). If you read the header data yourself, -use gme_identify_header(). - -If you want to remove support for some music types to reduce your -executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to -support just NSF and GBS, use this: - - #define GME_TYPE_LIST gme_nsf_type, gme_gbs_type - - -M3U playlist support --------------------- -The library supports playlists in an extended m3u format with -gme_load_m3u() to give track names and times to multi-song formats: AY, -GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format -itself is not well-defined so some m3u files won't work properly -(particularly those provided with KSS files). Only m3u files referencing -a single file are supported; your code must handle m3u files covering -more than one game music file, though it can use the built-in m3u -parsing provided by the library. - - -Information fields ------------------- -Support is provided for the various text fields and length information -in a file with gme_track_info(). If you just need track information for -a file (for example, building a playlist), use gme_new_info() in place -of gme_new_emu(), load the file normally, then you can access the track -count and info, but nothing else. - - M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS - ------------------------------------------------------- -Track Count | * * * * * * * * * - | -System | * * * * * * * * * * - | -Game | * * * * * * * - | -Song | * * * * * * * - | -Author | * * * * * * * * - | -Copyright | * * * * * * * * - | -Comment | * * * * - | -Dumper | * * * * - | -Length | * * * * * * - | -Intro Length| * * * - | -Loop Length | * * * - -As listed above, the HES and KSS file formats don't include a track -count, and tracks are often scattered over the 0-255 range, so an m3u -playlist for these is a must. - -Unavailable text fields are set to an empty string and times to -1. Your -code should be prepared for any combination of available and unavailable -fields, as a particular music file might not use all of the supported -fields listed above. - -Currently text fields are truncated to 255 characters. Obscure fields of -some formats are not currently decoded; contact me if you want one -added. - - -Track length ------------- -The library leaves it up to you as to when to stop playing a track. You -can ask for available length information and then tell the library what -time it should start fading the track with gme_set_fade(). By default it -also continually checks for 6 or more seconds of silence to mark the end -of a track. Here is a reasonable algorithm you can use to decide how -long to play a track: - -* If the track length is > 0, use it -* If the loop length > 0, play for intro + loop * 2 -* Otherwise, default to 2.5 minutes (150000 msec) - -If you want to play a track longer than normal, be sure the loop length -isn't zero. See Music_Player.cpp around line 145 for example code. - -By default, the library skips silence at the beginning of a track. It -also continually checks for the end of a non-looping track by watching -for 6 seconds of unbroken silence. When doing this is scans *ahead* by -several seconds so it can report the end of the track after only one -second of silence has actually played. This feature can be disabled with -gme_ignore_silence(). - - -Loading file data ------------------ -The library allows file data to be loaded in many different ways. All -load functions return an error which you should check. The following -examples assume these variables: - - Music_Emu* emu; - gme_err_t error; - -If you're letting the library determine a file's type, you can use -either gme_open_file() or gme_open_data(): - - error = gme_open_file( pathname, &emu ); - error = gme_open_data( pointer, size, &emu ); - -If you're manually determining file type and using used gme_new_emu() to -create an emulator, you can use the following methods of loading: - -* From a block of memory: - - error = gme_load_data( emu, pointer, size ); - -* Have library call your function to read data: - - gme_err_t my_read( void* my_data, void* out, long count ) - { - // code that reads 'count' bytes into 'out' buffer - // and return 0 if no error - } - - error = gme_load_custom( emu, my_read, file_size, my_data ); - -* If you must load the file data into memory yourself, you can have the -library use your data directly *without* making a copy. If you do this, -you must not free the data until you're done playing the file. - - error = emu->load_mem( pointer, size ); - -* If you've already read the first bytes of a file (perhaps to determine -the file type) and want to avoid seeking back to the beginning for -performance reasons, use Remaining_Reader: - - Std_File_Reader in; - error = in.open( file_path ); - - char header [4]; - error = in.read( &header, sizeof header ); - ... - - Remaining_Reader rem( &header, sizeof header, &in ); - error = emu->load( rem ); - -If you merely need access to a file's header after loading, use the -emulator-specific header() functions, after casting the Music_Emu -pointer to the specific emulator's type. This example examines the -chip_flags field of the header if it's an NSF file: - - if ( music_emu->type() == gme_nsf_type ) - { - Nsf_Emu* nsf_emu = (Nsf_Emu*) music_emu; - if ( nsf_emu->header().chip_flags & 0x01 ) - ... - } - -Contact me if you want more information about loading files. - - -Sound parameters ----------------- -All emulators support an arbitrary output sampling rate. A rate of 44100 -Hz should work well on most systems. Since band-limited synthesis is -used, a sampling rate above 48000 Hz is not necessary and will actually -reduce sound quality and performance. - -All emulators also support adjustable gain, mainly for the purpose of -getting consistent volume between different music formats and avoiding -excessive modulation. The gain can only be set *before* setting the -emulator's sampling rate, so it's not useful as a general volume -control. The default gains of emulators are set so that they give -generally similar volumes, though some soundtracks are significantly -louder or quieter than normal. - -Some emulators support adjustable treble and bass frequency equalization -(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer(). -Parameters are specified using gme_equalizer_t eq = { treble_dB, -bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives -normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble -for an extra crisp sound. Bass_freq sets the frequency where bass -response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, -and 15000 Hz removes all bass. For example, the following makes the -sound extra-crisp but lacking bass: - - gme_equalizer_t eq = { 5.0, 1000 }; - gme_set_equalizer( music_emu, &eq ); - -Each emulator's equalization defaults to approximate the particular -console's sound quality; this default can be determined by calling -equalizer() just after creating the emulator. The Music_Emu::tv_eq -profile gives sound as if coming from a TV speaker, and some emulators -include other profiles for different versions of the system. For -example, to use Famicom sound equalization with the NSF emulator, do the -following: - - music_emu->set_equalizer( Nsf_Emu::famicom_eq ); - - -VGM/GYM YM2413 & YM2612 FM sound --------------------------------- -The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound -chip emulator based on the Gens project. Because this has some -inaccuracies, other YM2612 emulators can be used in its place by -re-implementing the interface in YM2612_Emu.h. Available on my website -is a modified version of MAME's YM2612 emulator, which sounds better in -some ways and whose author is still making improvements. - -VGM music files using the YM2413 FM sound chip are also supported, but a -YM2413 emulator isn't included with the library due to technical -reasons. I have put one of the available YM2413 emulators on my website -that can be used directly. - - -Modular construction --------------------- -The library is made of many fairly independent modules. If you're using -only one music file emulator, you can eliminate many of the library -sources from your program. Refer to the files list in readme.txt to get -a general idea of what can be removed, and be sure to edit GME_TYPE_LIST -(see "Emulator types" above). Post to the forum if you'd like me to put -together a smaller version for a particular use, as this only takes me a -few minutes to do. - -If you want to use one of the individual sound chip emulators (or CPU -cores) in your own console emulator, first check the libraries page on -my website since I have released several of them as stand alone -libraries with included documentation and examples on their use. If you -don't find it as a standalone library, contact me and I'll consider -separating it. - -The "classic" sound chips use my Blip_Buffer library, which greatly -simplifies their implementation and efficiently handles band-limited -synthesis. It is also available as a stand alone library with -documentation and many examples. - - -Obscure features ----------------- -The library's flexibility allows many possibilities. Contact me if you -want help implementing ideas or removing limitations. - -* Uses no global/static variables, allowing multiple instances of any -emulator. This is useful in a music player if you want to allow -simultaneous recording or scanning of other tracks while one is already -playing. This will also be useful if your platform disallows global -data. - -* Emulators that support a custom sound buffer can have *every* voice -routed to a different Blip_Buffer, allowing custom processing on each -voice. For example you could record a Game Boy track as a 4-channel -sound file. - -* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive -synthesis on "classic" emulators, which might help on some really old -processors. This significantly lowers sound quality and prevents treble -equalization. Try this if your platform's processor isn't fast enough -for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces -processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth -the quality loss. - - -Solving problems ----------------- -If you're having problems, try the following: - -* If you're getting garbled sound, try this simple siren generator in -place of your call to play(). This will quickly tell whether the problem -is in the library or in your code. - - static void play_siren( long count, short* out ) - { - static double a, a2; - while ( count-- ) - *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); - } - -* Enable debugging support in your environment. This enables assertions -and other run-time checks. - -* Turn the compiler's optimizer is off. Sometimes an optimizer generates -bad code. - -* If multiple threads are being used, ensure that only one at a time is -accessing a given set of objects from the library. This library is not -in general thread-safe, though independent objects can be used in -separate threads. - -* If all else fails, see if the demos work. - - -Deprecated features -------------------- -The following functions and other features have been deprecated and will -be removed in a future release of the library. Alternatives to the -deprecated features are listed to the right. - -Music_Emu::error_count() warning() -load( header, reader ) see "Loading file data" above -Spc_Emu::trailer() track_info() -Spc_Emu::trailer_size() -Gym_Emu::track_length() track_info() -Vgm_Emu::gd3_data() track_info() -Nsfe_Emu::disable_playlist() clear_playlist() - - -Thanks ------- -Big thanks to Chris Moeller (kode54) for help with library testing and -feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and -for original work on openspc++ that was used when developing Spc_Emu. -Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the -start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz, -nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using, -and giving feedback for the library in their respective game music -players. diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp deleted file mode 100644 index 9dc5bb28..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp +++ /dev/null @@ -1,395 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Ay_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" - -// 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). -unsigned 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 ), -}; - -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; - } - } - - output( 0 ); - volume( 1.0 ); - reset(); -} - -void Ay_Apu::reset() -{ - last_time = 0; - noise.delay = 0; - noise.lfsr = 1; - - osc_t* osc = &oscs [osc_count]; - do - { - osc--; - osc->period = period_factor; - osc->delay = 0; - osc->last_amp = 0; - osc->phase = 0; - } - while ( osc != oscs ); - - for ( int i = sizeof regs; --i >= 0; ) - regs [i] = 0; - regs [7] = 0xFF; - write_data_( 13, 0 ); -} - -void Ay_Apu::write_data_( int addr, int data ) -{ - assert( (unsigned) addr < reg_count ); - - if ( (unsigned) addr >= 14 ) - { - #ifdef dprintf - dprintf( "Wrote to I/O port %02X\n", (int) addr ); - #endif - } - - // 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) * (0x100L * 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; - blargg_ulong const old_noise_lfsr = noise.lfsr; - - // envelope period - blip_time_t const env_period_factor = period_factor * 2; // verified - blip_time_t env_period = (regs [12] * 0x100L + 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 = (blargg_ulong) (osc_output->clock_rate() + - inaudible_freq) / (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 volume = amp_table [vol_mode & 0x0F] >> half_vol; - int osc_env_pos = env.pos; - if ( vol_mode & 0x10 ) - { - volume = env.wave [osc_env_pos] >> half_vol; - // 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 - { - blargg_long count = (final_end_time - time + period - 1) / period; - time += count * period; - osc->phase ^= count & 1; - } - - // noise time - blip_time_t ntime = final_end_time; - blargg_ulong 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 - blargg_long remain = end - ntime; - blargg_long 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; - //phase ^= 1; - } - //assert( phase == (delta > 0) ); - phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); - // (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; - - 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; - 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 ) - { - blargg_long 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/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h deleted file mode 100644 index 31956939..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h +++ /dev/null @@ -1,107 +0,0 @@ -// AY-3-8910 sound chip emulator - -// Game_Music_Emu 0.5.2 -#ifndef AY_APU_H -#define AY_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Ay_Apu { -public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Reset sound chip - void reset(); - - // Write to register at specified time - enum { reg_count = 16 }; - void write( blip_time_t time, int addr, int data ); - - // Run sound to specified time, end current time frame, then start 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( blip_time_t length ); - -// Additional features - - // Set sound output of specific oscillator to buffer, where index is - // 0, 1, or 2. If buffer is NULL, the specified oscillator is muted. - enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Set treble equalization (see documentation) - void treble_eq( blip_eq_t const& ); - -public: - Ay_Apu(); - typedef unsigned char byte; -private: - struct osc_t - { - blip_time_t period; - blip_time_t delay; - short last_amp; - short phase; - Blip_Buffer* output; - } oscs [osc_count]; - blip_time_t last_time; - byte latch; - byte regs [reg_count]; - - struct { - blip_time_t delay; - blargg_ulong lfsr; - } noise; - - struct { - blip_time_t delay; - byte const* wave; - int pos; - byte modes [8] [48]; // values already passed through volume table - } env; - - void run_until( blip_time_t ); - void write_data_( int addr, int data ); -public: - enum { amp_range = 255 }; - Blip_Synth synth_; -}; - -inline void Ay_Apu::volume( double v ) { synth_.volume( 0.7 / osc_count / amp_range * v ); } - -inline void Ay_Apu::treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } - -inline void Ay_Apu::write( blip_time_t time, int addr, int data ) -{ - run_until( time ); - write_data_( addr, data ); -} - -inline void Ay_Apu::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Ay_Apu::output( Blip_Buffer* buf ) -{ - osc_output( 0, buf ); - osc_output( 1, buf ); - osc_output( 2, buf ); -} - -inline void Ay_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp deleted file mode 100644 index 6ff7156b..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp +++ /dev/null @@ -1,1665 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -/* -Last validated with zexall 2006.11.21 5:26 PM -* Doesn't implement the R register or immediate interrupt after EI. -* Address wrap-around isn't completely correct, but is prevented from crashing emulator. -*/ - -#include "Ay_Cpu.h" - -#include "blargg_endian.h" -#include - -//#include "z80_cpu_log.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 */ - -#define SYNC_TIME() (void) (s.time = s_time) -#define RELOAD_TIME() (void) (s_time = s.time) - -// Callbacks to emulator - -#define CPU_OUT( cpu, addr, data, TIME )\ - ay_cpu_out( cpu, TIME, addr, data ) - -#define CPU_IN( cpu, addr, TIME )\ - ay_cpu_in( cpu, addr ) - -#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; - -#define SZ28P( n ) szpc [n] -#define SZ28PC( n ) szpc [n] -#define SZ28C( n ) (szpc [n] & ~P04) -#define SZ28( n ) SZ28C( n ) - -#define SET_R( n ) (void) (r.r = n) -#define GET_R() (r.r) - -Ay_Cpu::Ay_Cpu() -{ - state = &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; -} - -void Ay_Cpu::reset( void* m ) -{ - mem = (uint8_t*) m; - - check( state == &state_ ); - state = &state_; - state_.time = 0; - state_.base = 0; - end_time_ = 0; - - memset( &r, 0, sizeof r ); -} - -#define TIME (s_time + s.base) -#define READ_PROG( addr ) (mem [addr]) -#define INSTR( offset ) READ_PROG( pc + (offset) ) -#define GET_ADDR() GET_LE16( &READ_PROG( pc ) ) -#define READ( addr ) READ_PROG( addr ) -#define WRITE( addr, data ) (void) (READ_PROG( addr ) = data) -#define READ_WORD( addr ) GET_LE16( &READ_PROG( addr ) ) -#define WRITE_WORD( addr, data ) SET_LE16( &READ_PROG( addr ), data ) -#define IN( addr ) CPU_IN( this, addr, TIME ) -#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) - -#if BLARGG_BIG_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [n]) -#elif BLARGG_LITTLE_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) -#else - #error "Byte order of CPU must be known" -#endif - -//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) - -// help compiler see that it can just adjust stack offset, saving an extra instruction -#define R16( n, shift, offset )\ - (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) - -#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e -#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f -#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g -#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h - -// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 -static byte const ed_dd_timing [0x100] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, -0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, -0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, -0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -bool Ay_Cpu::run( cpu_time_t end_time ) -{ - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - bool warning = false; - - typedef BOOST::int8_t int8_t; - - union { - regs_t rg; - pairs_t rp; - uint8_t r8_ [8]; // indexed - uint16_t r16_ [4]; - }; - rg = this->r.b; - - cpu_time_t s_time = s.time; - uint8_t* const mem = this->mem; // cache - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 iy = r.iy; - int flags = r.b.flags; - - goto loop; -jr_not_taken: - s_time -= 5; - goto loop; -call_not_taken: - s_time -= 7; -jp_not_taken: - pc += 2; -loop: - - check( (unsigned long) pc < 0x10000 ); - check( (unsigned long) sp < 0x10000 ); - check( (unsigned) flags < 0x100 ); - check( (unsigned) ix < 0x10000 ); - check( (unsigned) iy < 0x10000 ); - - fuint8 opcode; - opcode = READ_PROG( pc ); - pc++; - - static byte const base_timing [0x100] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 - 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 - 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 - 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 - 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B - 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C - 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D - 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E - 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F - }; - - fuint16 data; - data = base_timing [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = READ_PROG( pc ); - - #ifdef Z80_CPU_LOG_H - //log_opcode( opcode, READ_PROG( pc ) ); - z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); - z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), - READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Common - - case 0x00: // NOP - CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. - goto loop; - - case 0x08:{// EX AF,AF' - int temp = r.alt.b.a; - r.alt.b.a = rg.a; - rg.a = temp; - - temp = r.alt.b.flags; - r.alt.b.flags = flags; - flags = temp; - goto loop; - } - - case 0xD3: // OUT (imm),A - pc++; - OUT( data + rg.a * 0x100, rg.a ); - goto loop; - - case 0x2E: // LD L,imm - pc++; - rg.l = data; - goto loop; - - case 0x3E: // LD A,imm - pc++; - rg.a = data; - goto loop; - - case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rg.a = READ( addr ); - goto loop; - } - -// Conditional - -#define ZERO (flags & Z40) -#define CARRY (flags & C01) -#define EVEN (flags & P04) -#define MINUS (flags & S80) - -// JR -#define JR( cond ) {\ - int disp = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) )\ - goto jr_not_taken;\ - pc += disp;\ - goto loop;\ -} - - case 0x20: JR( !ZERO ) // JR NZ,disp - case 0x28: JR( ZERO ) // JR Z,disp - case 0x30: JR( !CARRY ) // JR NC,disp - case 0x38: JR( CARRY ) // JR C,disp - case 0x18: JR( true ) // JR disp - - case 0x10:{// DJNZ disp - int temp = rg.b - 1; - rg.b = temp; - JR( temp ) - } - -// JP -#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; - - case 0xC2: JP( !ZERO ) // JP NZ,addr - case 0xCA: JP( ZERO ) // JP Z,addr - case 0xD2: JP( !CARRY ) // JP NC,addr - case 0xDA: JP( CARRY ) // JP C,addr - case 0xE2: JP( !EVEN ) // JP PO,addr - case 0xEA: JP( EVEN ) // JP PE,addr - case 0xF2: JP( !MINUS ) // JP P,addr - case 0xFA: JP( MINUS ) // JP M,addr - - case 0xC3: // JP addr - pc = GET_ADDR(); - goto loop; - - case 0xE9: // JP HL - pc = rp.hl; - goto loop; - -// RET -#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; - - case 0xC0: RET( !ZERO ) // RET NZ - case 0xC8: RET( ZERO ) // RET Z - case 0xD0: RET( !CARRY ) // RET NC - case 0xD8: RET( CARRY ) // RET C - case 0xE0: RET( !EVEN ) // RET PO - case 0xE8: RET( EVEN ) // RET PE - case 0xF0: RET( !MINUS ) // RET P - case 0xF8: RET( MINUS ) // RET M - - case 0xC9: // RET - ret_taken: - pc = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// CALL -#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; - - case 0xC4: CALL( !ZERO ) // CALL NZ,addr - case 0xCC: CALL( ZERO ) // CALL Z,addr - case 0xD4: CALL( !CARRY ) // CALL NC,addr - case 0xDC: CALL( CARRY ) // CALL C,addr - case 0xE4: CALL( !EVEN ) // CALL PO,addr - case 0xEC: CALL( EVEN ) // CALL PE,addr - case 0xF4: CALL( !MINUS ) // CALL P,addr - case 0xFC: CALL( MINUS ) // CALL M,addr - - case 0xCD:{// CALL addr - call_taken: - fuint16 addr = pc + 2; - pc = GET_ADDR(); - sp = uint16_t (sp - 2); - WRITE_WORD( sp, addr ); - goto loop; - } - - case 0xFF: // RST - if ( (pc - 1) > 0xFFFF ) - { - pc = uint16_t (pc - 1); - s_time -= 11; - goto loop; - } - CASE7( C7, CF, D7, DF, E7, EF, F7 ): - data = pc; - pc = opcode & 0x38; - goto push_data; - -// PUSH/POP - case 0xF5: // PUSH AF - data = rg.a * 0x100u + flags; - goto push_data; - - case 0xC5: // PUSH BC - case 0xD5: // PUSH DE - case 0xE5: // PUSH HL - data = R16( opcode, 4, 0xC5 ); - push_data: - sp = uint16_t (sp - 2); - WRITE_WORD( sp, data ); - goto loop; - - case 0xF1: // POP AF - flags = READ( sp ); - rg.a = READ( sp + 1 ); - sp = uint16_t (sp + 2); - goto loop; - - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL - R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// ADC/ADD/SBC/SUB - case 0x96: // SUB (HL) - case 0x86: // ADD (HL) - flags &= ~C01; - case 0x9E: // SBC (HL) - case 0x8E: // ADC (HL) - data = READ( rp.hl ); - goto adc_data; - - case 0xD6: // SUB A,imm - case 0xC6: // ADD imm - flags &= ~C01; - case 0xDE: // SBC A,imm - case 0xCE: // ADC imm - pc++; - goto adc_data; - - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r - flags &= ~C01; - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r - data = R8( opcode & 7, 0 ); - adc_data: { - int result = data + (flags & C01); - data ^= rg.a; - flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes - if ( flags ) - result = -result; - result += rg.a; - data ^= result; - flags |=(data & H10) | - ((data - -0x80) >> 6 & V04) | - SZ28C( result & 0x1FF ); - rg.a = result; - goto loop; - } - -// CP - case 0xBE: // CP (HL) - data = READ( rp.hl ); - goto cp_data; - - case 0xFE: // CP imm - pc++; - goto cp_data; - - CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r - data = R8( opcode, 0xB8 ); - cp_data: { - int result = rg.a - data; - flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); - data ^= rg.a; - flags |=(((result ^ rg.a) & data) >> 5 & V04) | - (((data & H10) ^ result) & (S80 | H10)); - if ( (uint8_t) result ) - goto loop; - flags |= Z40; - goto loop; - } - -// ADD HL,rp - - case 0x39: // ADD HL,SP - data = sp; - goto add_hl_data; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - data = R16( opcode, 4, 0x09 ); - add_hl_data: { - blargg_ulong sum = rp.hl + data; - data ^= rp.hl; - rp.hl = sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((data ^ sum) >> 8 & H10); - goto loop; - } - - case 0x27:{// DAA - int a = rg.a; - if ( a > 0x99 ) - flags |= C01; - - int adjust = 0x60 & -(flags & C01); - - if ( flags & H10 || (a & 0x0F) > 9 ) - adjust |= 0x06; - - if ( flags & N02 ) - adjust = -adjust; - a += adjust; - - flags = (flags & (C01 | N02)) | - ((rg.a ^ a) & H10) | - SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - /* - case 0x27:{// DAA - // more optimized, but probably not worth the obscurity - int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags - int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 - - if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 - adjust |= 0x06; - - if ( f & N02 ) - adjust = -adjust; - int a = rg.a + adjust; - - flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - */ - -// INC/DEC - case 0x34: // INC (HL) - data = READ( rp.hl ) + 1; - WRITE( rp.hl, data ); - goto inc_set_flags; - - CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r - data = ++R8( opcode >> 3, 0 ); - inc_set_flags: - flags = (flags & C01) | - (((data & 0x0F) - 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x80 ) - goto loop; - flags |= V04; - goto loop; - - case 0x35: // DEC (HL) - data = READ( rp.hl ) - 1; - WRITE( rp.hl, data ); - goto dec_set_flags; - - CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r - data = --R8( opcode >> 3, 0 ); - dec_set_flags: - flags = (flags & C01) | N02 | - (((data & 0x0F) + 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x7F ) - goto loop; - flags |= V04; - goto loop; - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - R16( opcode, 4, 0x03 )++; - goto loop; - - case 0x33: // INC SP - sp = uint16_t (sp + 1); - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - R16( opcode, 4, 0x0B )--; - goto loop; - - case 0x3B: // DEC SP - sp = uint16_t (sp - 1); - goto loop; - -// AND - case 0xA6: // AND (HL) - data = READ( rp.hl ); - goto and_data; - - case 0xE6: // AND imm - pc++; - goto and_data; - - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r - data = R8( opcode, 0xA0 ); - and_data: - rg.a &= data; - flags = SZ28P( rg.a ) | H10; - goto loop; - -// OR - case 0xB6: // OR (HL) - data = READ( rp.hl ); - goto or_data; - - case 0xF6: // OR imm - pc++; - goto or_data; - - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r - data = R8( opcode, 0xB0 ); - or_data: - rg.a |= data; - flags = SZ28P( rg.a ); - goto loop; - -// XOR - case 0xAE: // XOR (HL) - data = READ( rp.hl ); - goto xor_data; - - case 0xEE: // XOR imm - pc++; - goto xor_data; - - CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r - data = R8( opcode, 0xA8 ); - xor_data: - rg.a ^= data; - flags = SZ28P( rg.a ); - goto loop; - -// LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r - WRITE( rp.hl, R8( opcode, 0x70 ) ); - goto loop; - - CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r - CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r - CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r - CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r - CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r - CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r - CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r - R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); - goto loop; - - CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm - R8( opcode >> 3, 0 ) = data; - pc++; - goto loop; - - case 0x36: // LD (HL),imm - pc++; - WRITE( rp.hl, data ); - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) - R8( opcode >> 3, 8 ) = READ( rp.hl ); - goto loop; - - case 0x01: // LD rp,imm - case 0x11: - case 0x21: - R16( opcode, 4, 0x01 ) = GET_ADDR(); - pc += 2; - goto loop; - - case 0x31: // LD sp,imm - sp = GET_ADDR(); - pc += 2; - goto loop; - - case 0x2A:{// LD HL,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rp.hl = READ_WORD( addr ); - goto loop; - } - - case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE( addr, rg.a ); - goto loop; - } - - case 0x22:{// LD (addr),HL - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, rp.hl ); - goto loop; - } - - case 0x02: // LD (BC),A - case 0x12: // LD (DE),A - WRITE( R16( opcode, 4, 0x02 ), rg.a ); - goto loop; - - case 0x0A: // LD A,(BC) - case 0x1A: // LD A,(DE) - rg.a = READ( R16( opcode, 4, 0x0A ) ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - -// Rotate - - case 0x07:{// RLCA - fuint16 temp = rg.a; - temp = (temp << 1) | (temp >> 7); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08 | C01)); - rg.a = temp; - goto loop; - } - - case 0x0F:{// RRCA - fuint16 temp = rg.a; - flags = (flags & (S80 | Z40 | P04)) | - (temp & C01); - temp = (temp << 7) | (temp >> 1); - flags |= temp & (F20 | F08); - rg.a = temp; - goto loop; - } - - case 0x17:{// RLA - blargg_ulong temp = (rg.a << 1) | (flags & C01); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (temp >> 8); - rg.a = temp; - goto loop; - } - - case 0x1F:{// RRA - fuint16 temp = (flags << 7) | (rg.a >> 1); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (rg.a & C01); - rg.a = temp; - goto loop; - } - -// Misc - case 0x2F:{// CPL - fuint16 temp = ~rg.a; - flags = (flags & (S80 | Z40 | P04 | C01)) | - (temp & (F20 | F08)) | - (H10 | N02); - rg.a = temp; - goto loop; - } - - case 0x3F:{// CCF - flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | - (flags << 4 & H10) | - (rg.a & (F20 | F08)); - goto loop; - } - - case 0x37: // SCF - flags = (flags & (S80 | Z40 | P04)) | C01 | - (rg.a & (F20 | F08)); - goto loop; - - case 0xDB: // IN A,(imm) - pc++; - rg.a = IN( data + rg.a * 0x100 ); - goto loop; - - case 0xE3:{// EX (SP),HL - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, rp.hl ); - rp.hl = temp; - goto loop; - } - - case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; - rp.hl = rp.de; - rp.de = temp; - goto loop; - } - - case 0xD9:{// EXX DE,HL - fuint16 temp = r.alt.w.bc; - r.alt.w.bc = rp.bc; - rp.bc = temp; - - temp = r.alt.w.de; - r.alt.w.de = rp.de; - rp.de = temp; - - temp = r.alt.w.hl; - r.alt.w.hl = rp.hl; - rp.hl = temp; - goto loop; - } - - case 0xF3: // DI - r.iff1 = 0; - r.iff2 = 0; - goto loop; - - case 0xFB: // EI - r.iff1 = 1; - r.iff2 = 1; - // TODO: delayed effect - goto loop; - - case 0x76: // HALT - goto halt; - -//////////////////////////////////////// CB prefix - { - case 0xCB: - unsigned data2; - data2 = INSTR( 1 ); - pc++; - switch ( data ) - { - - // Rotate left - - #define RLC( read, write ) {\ - fuint8 result = read;\ - result = uint8_t (result << 1) | (result >> 7);\ - flags = SZ28P( result ) | (result & C01);\ - write;\ - goto loop;\ - } - - case 0x06: // RLC (HL) - s_time += 7; - data = rp.hl; - rlc_data_addr: - RLC( READ( data ), WRITE( data, result ) ) - - CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r - uint8_t& reg = R8( data, 0 ); - RLC( reg, reg = result ) - } - - #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x16: // RL (HL) - s_time += 7; - data = rp.hl; - rl_data_addr: - RL( READ( data ), WRITE( data, result ) ) - - CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r - uint8_t& reg = R8( data, 0x10 ); - RL( reg, reg = result ) - } - - #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x26: // SLA (HL) - s_time += 7; - data = rp.hl; - sla_data_addr: - SLA( READ( data ), 0, WRITE( data, result ) ) - - CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r - uint8_t& reg = R8( data, 0x20 ); - SLA( reg, 0, reg = result ) - } - - case 0x36: // SLL (HL) - s_time += 7; - data = rp.hl; - sll_data_addr: - SLA( READ( data ), 1, WRITE( data, result ) ) - - CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r - uint8_t& reg = R8( data, 0x30 ); - SLA( reg, 1, reg = result ) - } - - // Rotate right - - #define RRC( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = uint8_t (result << 7) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x0E: // RRC (HL) - s_time += 7; - data = rp.hl; - rrc_data_addr: - RRC( READ( data ), WRITE( data, result ) ) - - CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r - uint8_t& reg = R8( data, 0x08 ); - RRC( reg, reg = result ) - } - - #define RR( read, write ) {\ - fuint8 result = read;\ - fuint8 temp = result & C01;\ - result = uint8_t (flags << 7) | (result >> 1);\ - flags = SZ28P( result ) | temp;\ - write;\ - goto loop;\ - } - - case 0x1E: // RR (HL) - s_time += 7; - data = rp.hl; - rr_data_addr: - RR( READ( data ), WRITE( data, result ) ) - - CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r - uint8_t& reg = R8( data, 0x18 ); - RR( reg, reg = result ) - } - - #define SRA( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = (result & 0x80) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x2E: // SRA (HL) - data = rp.hl; - s_time += 7; - sra_data_addr: - SRA( READ( data ), WRITE( data, result ) ) - - CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r - uint8_t& reg = R8( data, 0x28 ); - SRA( reg, reg = result ) - } - - #define SRL( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result >>= 1;\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x3E: // SRL (HL) - s_time += 7; - data = rp.hl; - srl_data_addr: - SRL( READ( data ), WRITE( data, result ) ) - - CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r - uint8_t& reg = R8( data, 0x38 ); - SRL( reg, reg = result ) - } - - // BIT - { - unsigned temp; - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) - s_time += 4; - temp = READ( rp.hl ); - flags &= C01; - goto bit_temp; - CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r - CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r - CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r - CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r - CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r - CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r - CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r - temp = R8( data & 7, 0 ); - flags = (flags & C01) | (temp & (F20 | F08)); - bit_temp: - int masked = temp & 1 << (data >> 3 & 7); - flags |=(masked & S80) | H10 | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - // SET/RES - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) - s_time += 7; - int temp = READ( rp.hl ); - int bit = 1 << (data >> 3 & 7); - temp |= bit; // SET - if ( !(data & 0x40) ) - temp ^= bit; // RES - WRITE( rp.hl, temp ); - goto loop; - } - - CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r - CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r - CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r - CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r - CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r - CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r - CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r - CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r - R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); - goto loop; - - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r - CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r - CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r - R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// ED prefix - { - case 0xED: - pc++; - s_time += ed_dd_timing [data] >> 4; - switch ( data ) - { - { - blargg_ulong temp; - case 0x72: // SBC HL,SP - case 0x7A: // ADC HL,SP - temp = sp; - if ( 0 ) - case 0x42: // SBC HL,BC - case 0x52: // SBC HL,DE - case 0x62: // SBC HL,HL - case 0x4A: // ADC HL,BC - case 0x5A: // ADC HL,DE - case 0x6A: // ADC HL,HL - temp = R16( data >> 3 & 6, 1, 0 ); - blargg_ulong sum = temp + (flags & C01); - flags = ~data >> 2 & N02; - if ( flags ) - sum = -sum; - sum += rp.hl; - temp ^= rp.hl; - temp ^= sum; - flags |=(sum >> 16 & C01) | - (temp >> 8 & H10) | - (sum >> 8 & (S80 | F20 | F08)) | - ((temp - -0x8000) >> 14 & V04); - rp.hl = sum; - if ( (uint16_t) sum ) - goto loop; - flags |= Z40; - goto loop; - } - - CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) - int temp = IN( rp.bc ); - R8( data >> 3, 8 ) = temp; - flags = (flags & C01) | SZ28P( temp ); - goto loop; - } - - case 0x71: // OUT (C),0 - rg.flags = 0; - CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r - OUT( rp.bc, R8( data >> 3, 8 ) ); - goto loop; - - { - unsigned temp; - case 0x73: // LD (ADDR),SP - temp = sp; - if ( 0 ) - case 0x43: // LD (ADDR),BC - case 0x53: // LD (ADDR),DE - temp = R16( data, 4, 0x43 ); - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, temp ); - goto loop; - } - - case 0x4B: // LD BC,(ADDR) - case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - R16( data, 4, 0x4B ) = READ_WORD( addr ); - goto loop; - } - - case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - sp = READ_WORD( addr ); - goto loop; - } - - case 0x67:{// RRD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); - temp = (rg.a & 0xF0) | (temp & 0x0F); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - case 0x6F:{// RLD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); - temp = (rg.a & 0xF0) | (temp >> 4); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG - opcode = 0x10; // flag to do SBC instead of ADC - flags &= ~C01; - data = rg.a; - rg.a = 0; - goto adc_data; - - { - int inc; - case 0xA9: // CPD - case 0xB9: // CPDR - inc = -1; - if ( 0 ) - case 0xA1: // CPI - case 0xB1: // CPIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int result = rg.a - temp; - flags = (flags & C01) | N02 | - ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); - - if ( !(uint8_t) result ) flags |= Z40; - result -= (flags & H10) >> 4; - flags |= result & F08; - flags |= result << 4 & F20; - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( flags & Z40 || data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xA8: // LDD - case 0xB8: // LDDR - inc = -1; - if ( 0 ) - case 0xA0: // LDI - case 0xB0: // LDIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - addr = rp.de; - rp.de = addr + inc; - WRITE( addr, temp ); - - temp += rg.a; - flags = (flags & (S80 | Z40 | C01)) | - (temp & F08) | (temp << 4 & F20); - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xAB: // OUTD - case 0xBB: // OTDR - inc = -1; - if ( 0 ) - case 0xA3: // OUTI - case 0xB3: // OTIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - OUT( rp.bc, temp ); - goto loop; - } - - { - int inc; - case 0xAA: // IND - case 0xBA: // INDR - inc = -1; - if ( 0 ) - case 0xA2: // INI - case 0xB2: // INIR - inc = +1; - - fuint16 addr = rp.hl; - rp.hl = addr + inc; - - int temp = IN( rp.bc ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - WRITE( addr, temp ); - goto loop; - } - - case 0x47: // LD I,A - r.i = rg.a; - goto loop; - - case 0x4F: // LD R,A - SET_R( rg.a ); - dprintf( "LD R,A not supported\n" ); - warning = true; - goto loop; - - case 0x57: // LD A,I - rg.a = r.i; - goto ld_ai_common; - - case 0x5F: // LD A,R - rg.a = GET_R(); - dprintf( "LD A,R not supported\n" ); - warning = true; - ld_ai_common: - flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); - goto loop; - - CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN - r.iff1 = r.iff2; - goto ret_taken; - - case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 - r.im = 0; - goto loop; - - case 0x56: case 0x76: // IM 1 - r.im = 1; - goto loop; - - case 0x5E: case 0x7E: // IM 2 - r.im = 2; - goto loop; - - default: - dprintf( "Opcode $ED $%02X not supported\n", data ); - warning = true; - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// DD/FD prefix - { - fuint16 ixy; - case 0xDD: - ixy = ix; - goto ix_prefix; - case 0xFD: - ixy = iy; - ix_prefix: - pc++; - unsigned data2 = READ_PROG( pc ); - s_time += ed_dd_timing [data] & 0x0F; - switch ( data ) - { - // TODO: more efficient way of avoid negative address - #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) - - #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; - - // ADD/ADC/SUB/SBC - - case 0x96: // SUB (IXY+disp) - case 0x86: // ADD (IXY+disp) - flags &= ~C01; - case 0x9E: // SBC (IXY+disp) - case 0x8E: // ADC (IXY+disp) - pc++; - opcode = data; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto adc_data; - - case 0x94: // SUB HXY - case 0x84: // ADD HXY - flags &= ~C01; - case 0x9C: // SBC HXY - case 0x8C: // ADC HXY - opcode = data; - data = ixy >> 8; - goto adc_data; - - case 0x95: // SUB LXY - case 0x85: // ADD LXY - flags &= ~C01; - case 0x9D: // SBC LXY - case 0x8D: // ADC LXY - opcode = data; - data = (uint8_t) ixy; - goto adc_data; - - { - unsigned temp; - case 0x39: // ADD IXY,SP - temp = sp; - goto add_ixy_data; - - case 0x29: // ADD IXY,HL - temp = ixy; - goto add_ixy_data; - - case 0x09: // ADD IXY,BC - case 0x19: // ADD IXY,DE - temp = R16( data, 4, 0x09 ); - add_ixy_data: { - blargg_ulong sum = ixy + temp; - temp ^= ixy; - ixy = (uint16_t) sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((temp ^ sum) >> 8 & H10); - goto set_ixy; - } - } - - // AND - case 0xA6: // AND (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto and_data; - - case 0xA4: // AND HXY - data = ixy >> 8; - goto and_data; - - case 0xA5: // AND LXY - data = (uint8_t) ixy; - goto and_data; - - // OR - case 0xB6: // OR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto or_data; - - case 0xB4: // OR HXY - data = ixy >> 8; - goto or_data; - - case 0xB5: // OR LXY - data = (uint8_t) ixy; - goto or_data; - - // XOR - case 0xAE: // XOR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto xor_data; - - case 0xAC: // XOR HXY - data = ixy >> 8; - goto xor_data; - - case 0xAD: // XOR LXY - data = (uint8_t) ixy; - goto xor_data; - - // CP - case 0xBE: // CP (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto cp_data; - - case 0xBC: // CP HXY - data = ixy >> 8; - goto cp_data; - - case 0xBD: // CP LXY - data = (uint8_t) ixy; - goto cp_data; - - // LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r - data = R8( data, 0x70 ); - if ( 0 ) - case 0x36: // LD (IXY+disp),imm - pc++, data = READ_PROG( pc ); - pc++; - WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); - goto loop; - - CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY - R8( data >> 3, 8 ) = ixy >> 8; - goto loop; - - case 0x64: // LD HXY,HXY - case 0x6D: // LD LXY,LXY - goto loop; - - CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY - R8( data >> 3, 8 ) = ixy; - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) - pc++; - R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto loop; - - case 0x26: // LD HXY,imm - pc++; - goto ld_hxy_data; - - case 0x65: // LD HXY,LXY - data2 = (uint8_t) ixy; - goto ld_hxy_data; - - CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r - data2 = R8( data, 0x60 ); - ld_hxy_data: - ixy = (uint8_t) ixy | (data2 << 8); - goto set_ixy; - - case 0x2E: // LD LXY,imm - pc++; - goto ld_lxy_data; - - case 0x6C: // LD LXY,HXY - data2 = ixy >> 8; - goto ld_lxy_data; - - CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r - data2 = R8( data, 0x68 ); - ld_lxy_data: - ixy = (ixy & 0xFF00) | data2; - set_ixy: - if ( opcode == 0xDD ) - { - ix = ixy; - goto loop; - } - iy = ixy; - goto loop; - - case 0xF9: // LD SP,IXY - sp = ixy; - goto loop; - - case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, ixy ); - goto loop; - } - - case 0x21: // LD IXY,imm - ixy = GET_ADDR(); - pc += 2; - goto set_ixy; - - case 0x2A:{// LD IXY,(addr) - fuint16 addr = GET_ADDR(); - ixy = READ_WORD( addr ); - pc += 2; - goto set_ixy; - } - - // DD/FD CB prefix - case 0xCB: { - data = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data2 = READ_PROG( pc ); - pc++; - switch ( data2 ) - { - case 0x06: goto rlc_data_addr; // RLC (IXY) - case 0x16: goto rl_data_addr; // RL (IXY) - case 0x26: goto sla_data_addr; // SLA (IXY) - case 0x36: goto sll_data_addr; // SLL (IXY) - case 0x0E: goto rrc_data_addr; // RRC (IXY) - case 0x1E: goto rr_data_addr; // RR (IXY) - case 0x2E: goto sra_data_addr; // SRA (IXY) - case 0x3E: goto srl_data_addr; // SRL (IXY) - - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) - fuint8 temp = READ( data ); - int masked = temp & 1 << (data2 >> 3 & 7); - flags = (flags & C01) | H10 | - (masked & S80) | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) - int temp = READ( data ); - int bit = 1 << (data2 >> 3 & 7); - temp |= bit; // SET - if ( !(data2 & 0x40) ) - temp ^= bit; // RES - WRITE( data, temp ); - goto loop; - } - - default: - dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); - warning = true; - goto loop; - } - assert( false ); - } - - // INC/DEC - case 0x23: // INC IXY - ixy = uint16_t (ixy + 1); - goto set_ixy; - - case 0x2B: // DEC IXY - ixy = uint16_t (ixy - 1); - goto set_ixy; - - case 0x34: // INC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) + 1; - WRITE( ixy, data ); - goto inc_set_flags; - - case 0x35: // DEC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) - 1; - WRITE( ixy, data ); - goto dec_set_flags; - - case 0x24: // INC HXY - ixy = uint16_t (ixy + 0x100); - data = ixy >> 8; - goto inc_xy_common; - - case 0x2C: // INC LXY - data = uint8_t (ixy + 1); - ixy = (ixy & 0xFF00) | data; - inc_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto inc_set_flags; - } - iy = ixy; - goto inc_set_flags; - - case 0x25: // DEC HXY - ixy = uint16_t (ixy - 0x100); - data = ixy >> 8; - goto dec_xy_common; - - case 0x2D: // DEC LXY - data = uint8_t (ixy - 1); - ixy = (ixy & 0xFF00) | data; - dec_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto dec_set_flags; - } - iy = ixy; - goto dec_set_flags; - - // PUSH/POP - case 0xE5: // PUSH IXY - data = ixy; - goto push_data; - - case 0xE1:{// POP IXY - ixy = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto set_ixy; - } - - // Misc - - case 0xE9: // JP (IXY) - pc = ixy; - goto loop; - - case 0xE3:{// EX (SP),IXY - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, ixy ); - ixy = temp; - goto set_ixy; - } - - default: - dprintf( "Unnecessary DD/FD prefix encountered\n" ); - warning = true; - pc--; - goto loop; - } - assert( false ); - } - - } - dprintf( "Unhandled main opcode: $%02X\n", opcode ); - assert( false ); - -halt: - s_time &= 3; // increment by multiple of 4 -out_of_time: - pc--; - - s.time = s_time; - rg.flags = flags; - r.ix = ix; - r.iy = iy; - r.sp = sp; - r.pc = pc; - this->r.b = rg; - this->state_ = s; - this->state = &this->state_; - - return warning; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h deleted file mode 100644 index 07241d5e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h +++ /dev/null @@ -1,92 +0,0 @@ -// Z80 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef AY_CPU_H -#define AY_CPU_H - -#include "blargg_endian.h" - -typedef blargg_long cpu_time_t; - -// must be defined by caller -void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data ); -int ay_cpu_in( class Ay_Cpu*, unsigned addr ); - -class Ay_Cpu { -public: - // Clear all registers and keep pointer to 64K memory passed in - void reset( void* mem_64k ); - - // Run until specified time is reached. Returns true if suspicious/unsupported - // instruction was encountered at any point during run. - bool run( cpu_time_t end_time ); - - // Time of beginning of next instruction - cpu_time_t time() const { return state->time + state->base; } - - // Alter current time. Not supported during run() call. - void set_time( cpu_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } - - typedef BOOST::uint8_t uint8_t; - typedef BOOST::uint16_t uint16_t; - - #if BLARGG_BIG_ENDIAN - struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; - #else - struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; - #endif - BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 ); - - struct pairs_t { uint16_t bc, de, hl, fa; }; - - // Registers are not updated until run() returns - struct registers_t { - uint16_t pc; - uint16_t sp; - uint16_t ix; - uint16_t iy; - union { - regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a - pairs_t w; // w.bc, w.de, w.hl. w.fa - }; - union { - regs_t b; - pairs_t w; - } alt; - uint8_t iff1; - uint8_t iff2; - uint8_t r; - uint8_t i; - uint8_t im; - }; - //registers_t r; (below for efficiency) - - // can read this far past end of memory - enum { cpu_padding = 0x100 }; - -public: - Ay_Cpu(); -private: - uint8_t szpc [0x200]; - uint8_t* mem; - cpu_time_t end_time_; - struct state_t { - cpu_time_t base; - cpu_time_t time; - }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - void set_end_time( cpu_time_t t ); -public: - registers_t r; -}; - -inline void Ay_Cpu::set_end_time( cpu_time_t t ) -{ - cpu_time_t delta = state->base - t; - state->base = t; - state->time += delta; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp deleted file mode 100644 index bdc82e9e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp +++ /dev/null @@ -1,404 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Ay_Emu.h" - -#include "blargg_endian.h" -#include - -/* 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" - -long const spectrum_clock = 3546900; -long const cpc_clock = 2000000; - -unsigned const ram_start = 0x4000; -int const osc_count = Ay_Apu::osc_count + 1; - -Ay_Emu::Ay_Emu() -{ - beeper_output = 0; - set_type( gme_ay_type ); - - static const char* const names [osc_count] = { - "Wave 1", "Wave 2", "Wave 3", "Beeper" - }; - set_voice_names( names ); - - static int const types [osc_count] = { - wave_type | 0, wave_type | 1, wave_type | 2, mixed_type | 0 - }; - set_voice_types( types ); - set_silence_lookahead( 6 ); -} - -Ay_Emu::~Ay_Emu() { } - -// Track info - -static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int min_size ) -{ - long pos = ptr - (byte const*) file.header; - long file_size = file.end - (byte const*) file.header; - assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); - int offset = (BOOST::int16_t) get_be16( ptr ); - if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) ) - return 0; - return ptr + offset; -} - -static blargg_err_t parse_header( byte const* in, long size, Ay_Emu::file_t* out ) -{ - typedef Ay_Emu::header_t header_t; - out->header = (header_t const*) in; - out->end = in + size; - - if ( size < Ay_Emu::header_size ) - return gme_wrong_file_type; - - header_t const& h = *(header_t const*) in; - if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) - return gme_wrong_file_type; - - out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); - if ( !out->tracks ) - return "Missing track data"; - - return 0; -} - -static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int track ) -{ - Gme_File::copy_field_( out->song, (char const*) get_data( file, file.tracks + track * 4, 1 ) ); - byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 ); - if ( track_info ) - out->length = get_be16( track_info + 4 ) * (1000L / 50); // frames to msec - - Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) ); - Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) ); -} - -blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const -{ - copy_ay_fields( file, out, track ); - return 0; -} - -struct Ay_File : Gme_Info_ -{ - Ay_Emu::file_t file; - - Ay_File() { set_type( gme_ay_type ); } - - blargg_err_t load_mem_( byte const* begin, long size ) - { - RETURN_ERR( parse_header( begin, size, &file ) ); - set_track_count( file.header->max_track + 1 ); - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int track ) const - { - copy_ay_fields( file, out, track ); - return 0; - } -}; - -static Music_Emu* new_ay_emu () { return BLARGG_NEW Ay_Emu ; } -static Music_Emu* new_ay_file() { return BLARGG_NEW Ay_File; } - -gme_type_t_ const gme_ay_type [1] = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; - -// Setup - -blargg_err_t Ay_Emu::load_mem_( byte const* in, long size ) -{ - assert( offsetof (header_t,track_info [2]) == header_size ); - - RETURN_ERR( parse_header( in, size, &file ) ); - set_track_count( file.header->max_track + 1 ); - - if ( file.header->vers > 2 ) - set_warning( "Unknown file version" ); - - set_voice_count( osc_count ); - apu.volume( gain() ); - - return setup_buffer( spectrum_clock ); -} - -void Ay_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* ) -{ - if ( i >= Ay_Apu::osc_count ) - beeper_output = center; - else - apu.osc_output( i, center ); -} - -// Emulation - -void Ay_Emu::set_tempo_( double t ) -{ - play_period = blip_time_t (clock_rate() / 50 / t); -} - -blargg_err_t Ay_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET - memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 ); - memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start ); - memset( mem.padding1, 0xFF, sizeof mem.padding1 ); - memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 ); - - // locate data blocks - byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); - if ( !data ) return "File data missing"; - - byte const* const more_data = get_data( file, data + 10, 6 ); - if ( !more_data ) return "File data missing"; - - byte const* blocks = get_data( file, data + 12, 8 ); - if ( !blocks ) return "File data missing"; - - // initial addresses - cpu::reset( mem.ram ); - r.sp = get_be16( more_data ); - r.b.a = r.b.b = r.b.d = r.b.h = data [8]; - r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; - r.alt.w = r.w; - r.ix = r.iy = r.w.hl; - - unsigned addr = get_be16( blocks ); - if ( !addr ) return "File data missing"; - - unsigned init = get_be16( more_data + 2 ); - if ( !init ) - init = addr; - - // copy blocks into memory - do - { - blocks += 2; - unsigned len = get_be16( blocks ); blocks += 2; - if ( addr + len > 0x10000 ) - { - set_warning( "Bad data block size" ); - len = 0x10000 - addr; - } - check( len ); - byte const* in = get_data( file, blocks, 0 ); blocks += 2; - if ( len > blargg_ulong (file.end - in) ) - { - set_warning( "Missing file data" ); - len = file.end - in; - } - //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); - if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data - dprintf( "Block addr in ROM\n" ); - memcpy( mem.ram + addr, in, len ); - - if ( file.end - blocks < 8 ) - { - set_warning( "Missing file data" ); - break; - } - } - while ( (addr = get_be16( blocks )) != 0 ); - - // copy and configure driver - static byte const passive [] = { - 0xF3, // DI - 0xCD, 0, 0, // CALL init - 0xED, 0x5E, // LOOP: IM 2 - 0xFB, // EI - 0x76, // HALT - 0x18, 0xFA // JR LOOP - }; - static byte const active [] = { - 0xF3, // DI - 0xCD, 0, 0, // CALL init - 0xED, 0x56, // LOOP: IM 1 - 0xFB, // EI - 0x76, // HALT - 0xCD, 0, 0, // CALL play - 0x18, 0xF7 // JR LOOP - }; - memcpy( mem.ram, passive, sizeof passive ); - unsigned play_addr = get_be16( more_data + 4 ); - //dprintf( "Play: $%04X\n", play_addr ); - if ( play_addr ) - { - memcpy( mem.ram, active, sizeof active ); - mem.ram [ 9] = play_addr; - mem.ram [10] = play_addr >> 8; - } - mem.ram [2] = init; - mem.ram [3] = init >> 8; - - mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) - - memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh) - - beeper_delta = int (apu.amp_range * 0.65); - last_beeper = 0; - apu.reset(); - next_play = play_period; - - // start at spectrum speed - change_clock_rate( spectrum_clock ); - set_tempo( tempo() ); - - spectrum_mode = false; - cpc_mode = false; - cpc_latch = 0; - - return 0; -} - -// Emulation - -void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) -{ - if ( !cpc_mode ) - { - switch ( addr & 0xFEFF ) - { - case 0xFEFD: - spectrum_mode = true; - apu_addr = data & 0x0F; - return; - - case 0xBEFD: - spectrum_mode = true; - apu.write( time, apu_addr, data ); - return; - } - } - - if ( !spectrum_mode ) - { - switch ( addr >> 8 ) - { - case 0xF6: - switch ( data & 0xC0 ) - { - case 0xC0: - apu_addr = cpc_latch & 0x0F; - goto enable_cpc; - - case 0x80: - apu.write( time, apu_addr, 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; - change_clock_rate( cpc_clock ); - set_tempo( tempo() ); - } -} - -void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) -{ - Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu); - - if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode ) - { - int delta = emu.beeper_delta; - data &= 0x10; - if ( emu.last_beeper != data ) - { - emu.last_beeper = data; - emu.beeper_delta = -delta; - emu.spectrum_mode = true; - if ( emu.beeper_output ) - emu.apu.synth_.offset( time, delta, emu.beeper_output ); - } - } - else - { - emu.cpu_out_misc( time, addr, data ); - } -} - -int ay_cpu_in( Ay_Cpu*, unsigned 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; -} - -blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) -{ - set_time( 0 ); - if ( !(spectrum_mode | cpc_mode) ) - duration /= 2; // until mode is set, leave room for halved clock rate - - while ( time() < duration ) - { - cpu::run( min( duration, (blip_time_t) next_play ) ); - - if ( time() >= next_play ) - { - next_play += play_period; - - if ( r.iff1 ) - { - if ( mem.ram [r.pc] == 0x76 ) - r.pc++; - - r.iff1 = r.iff2 = 0; - - mem.ram [--r.sp] = uint8_t (r.pc >> 8); - mem.ram [--r.sp] = uint8_t (r.pc); - r.pc = 0x38; - cpu::adjust_time( 12 ); - if ( r.im == 2 ) - { - cpu::adjust_time( 6 ); - unsigned addr = r.i * 0x100u + 0xFF; - r.pc = mem.ram [(addr + 1) & 0xFFFF] * 0x100u + mem.ram [addr]; - } - } - } - } - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - adjust_time( -duration ); - - apu.end_frame( duration ); - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h deleted file mode 100644 index ba8445d3..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h +++ /dev/null @@ -1,70 +0,0 @@ -// Sinclair Spectrum AY music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef AY_EMU_H -#define AY_EMU_H - -#include "Classic_Emu.h" -#include "Ay_Apu.h" -#include "Ay_Cpu.h" - -class Ay_Emu : private Ay_Cpu, public Classic_Emu { - typedef Ay_Cpu cpu; -public: - // AY file header - enum { header_size = 0x14 }; - struct header_t - { - 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; } -public: - Ay_Emu(); - ~Ay_Emu(); - struct file_t { - header_t const* header; - byte const* end; - byte const* tracks; - }; -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); -private: - file_t file; - - unsigned play_addr; - cpu_time_t play_period; - cpu_time_t next_play; - Blip_Buffer* beeper_output; - int beeper_delta; - int last_beeper; - int apu_addr; - int cpc_latch; - bool spectrum_mode; - bool cpc_mode; - - // large items - struct { - byte padding1 [0x100]; - byte ram [0x10000 + 0x100]; - } mem; - Ay_Apu apu; - friend void ay_cpu_out( Ay_Cpu*, cpu_time_t, unsigned addr, int data ); - void cpu_out_misc( cpu_time_t, unsigned addr, int data ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp b/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp deleted file mode 100644 index 07e9d658..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp +++ /dev/null @@ -1,446 +0,0 @@ -// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ - -#include "Blip_Buffer.h" -#include "blargg_common.h" -#include -#include -#include -#include -#include - -/* 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 */ - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -int const silent_buf_size = 1; // size used for Silent_Blip_Buffer - -Blip_Buffer::Blip_Buffer() -{ - factor_ = 0x7fffffff; - offset_ = 0; - buffer_ = 0; - buffer_size_ = 0; - sample_rate_ = 0; - reader_accum_ = 0; - bass_shift_ = 0; - clock_rate_ = 0; - bass_freq_ = 16; - length_ = 0; - - // assumptions code makes about implementation-defined features - #ifndef NDEBUG - // right shift of negative value preserves sign - buf_t_ i = -0x7FFFFFFE; - assert( (i >> 1) == -0x3FFFFFFF ); - - // casting to short truncates to 16 bits and sign-extends - i = 0x18000; - assert( (short) i == -0x8000 ); - #endif -} - -Blip_Buffer::~Blip_Buffer() -{ - if ( buffer_size_ != silent_buf_size ) - free( buffer_ ); -} - -Silent_Blip_Buffer::Silent_Blip_Buffer() -{ - factor_ = 0; - buffer_ = buf; - buffer_size_ = silent_buf_size; - memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow -} - -void Blip_Buffer::clear( int entire_buffer ) -{ - offset_ = 0; - reader_accum_ = 0; - modified_ = 0; - if ( buffer_ ) - { - long count = (entire_buffer ? buffer_size_ : samples_avail()); - memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); - } -} - -Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) -{ - if ( buffer_size_ == silent_buf_size ) - { - assert( 0 ); - return "Internal (tried to resize Silent_Blip_Buffer)"; - } - - // start with maximum length that resampled time can represent - long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; - if ( msec != blip_max_length ) - { - long s = (new_rate * (msec + 1) + 999) / 1000; - if ( s < new_size ) - new_size = s; - else - assert( 0 ); // fails if requested buffer length exceeds limit - } - - if ( buffer_size_ != new_size ) - { - void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); - if ( !p ) - return "Out of memory"; - buffer_ = (buf_t_*) p; - } - - buffer_size_ = new_size; - assert( buffer_size_ != silent_buf_size ); - - // update things based on the sample rate - sample_rate_ = new_rate; - length_ = new_size * 1000 / new_rate - 1; - if ( msec ) - assert( length_ == msec ); // ensure length is same as that passed in - if ( clock_rate_ ) - clock_rate( clock_rate_ ); - bass_freq( bass_freq_ ); - - clear(); - - return 0; // success -} - -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const -{ - double ratio = (double) sample_rate_ / rate; - blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large - return (blip_resampled_time_t) factor; -} - -void Blip_Buffer::bass_freq( int freq ) -{ - bass_freq_ = freq; - int shift = 31; - if ( freq > 0 ) - { - shift = 13; - long f = (freq << 16) / sample_rate_; - while ( (f >>= 1) && --shift ) { } - } - bass_shift_ = shift; -} - -void Blip_Buffer::end_frame( blip_time_t t ) -{ - offset_ += t * factor_; - assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length -} - -void Blip_Buffer::remove_silence( long count ) -{ - assert( count <= samples_avail() ); // tried to remove more samples than available - offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; -} - -long Blip_Buffer::count_samples( blip_time_t t ) const -{ - unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; - unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; - return (long) (last_sample - first_sample); -} - -blip_time_t Blip_Buffer::count_clocks( long count ) const -{ - if ( !factor_ ) - { - assert( 0 ); // sample rate and clock rates must be set first - return 0; - } - - if ( count > buffer_size_ ) - count = buffer_size_; - blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; - return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); -} - -void Blip_Buffer::remove_samples( long count ) -{ - if ( count ) - { - remove_silence( count ); - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + blip_buffer_extra_; - memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); - memset( buffer_ + remain, 0, count * sizeof *buffer_ ); - } -} - -// Blip_Synth_ - -Blip_Synth_Fast_::Blip_Synth_Fast_() -{ - buf = 0; - last_amp = 0; - delta_factor = 0; -} - -void Blip_Synth_Fast_::volume_unit( double new_unit ) -{ - delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); -} - -#if !BLIP_BUFFER_FAST - -Blip_Synth_::Blip_Synth_( short* p, int w ) : - impulses( p ), - width( w ) -{ - volume_unit_ = 0.0; - kernel_unit = 0; - buf = 0; - last_amp = 0; - delta_factor = 0; -} - -#undef PI -#define PI 3.1415926535897932384626433832795029 - -static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) -{ - if ( cutoff >= 0.999 ) - cutoff = 0.999; - - if ( treble < -300.0 ) - treble = -300.0; - if ( treble > 5.0 ) - treble = 5.0; - - double const maxh = 4096.0; - double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); - double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); - double const to_angle = PI / 2 / maxh / oversample; - for ( int i = 0; i < count; i++ ) - { - double angle = ((i - count) * 2 + 1) * to_angle; - double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); - double cos_nc_angle = cos( maxh * cutoff * angle ); - double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); - double cos_angle = cos( angle ); - - c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; - double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); - double b = 2.0 - cos_angle - cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d - } -} - -void blip_eq_t::generate( float* out, int count ) const -{ - // lower cutoff freq for narrow kernels with their wider transition band - // (8 points->1.49, 16 points->1.15) - double oversample = blip_res * 2.25 / count + 0.85; - double half_rate = sample_rate * 0.5; - if ( cutoff_freq ) - oversample = half_rate / cutoff_freq; - double cutoff = rolloff_freq * oversample / half_rate; - - gen_sinc( out, count, blip_res * oversample, treble, cutoff ); - - // apply (half of) hamming window - double to_fraction = PI / (count - 1); - for ( int i = count; i--; ) - out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); -} - -void Blip_Synth_::adjust_impulse() -{ - // sum pairs for each phase and add error correction to end of first half - int const size = impulses_size(); - for ( int p = blip_res; p-- >= blip_res / 2; ) - { - int p2 = blip_res - 2 - p; - long error = kernel_unit; - for ( int i = 1; i < size; i += blip_res ) - { - error -= impulses [i + p ]; - error -= impulses [i + p2]; - } - if ( p == p2 ) - error /= 2; // phase = 0.5 impulse uses same half for both sides - impulses [size - blip_res + p] += (short) error; - //printf( "error: %ld\n", error ); - } - - //for ( int i = blip_res; i--; printf( "\n" ) ) - // for ( int j = 0; j < width / 2; j++ ) - // printf( "%5ld,", impulses [j * blip_res + i + 1] ); -} - -void Blip_Synth_::treble_eq( blip_eq_t const& eq ) -{ - float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; - - int const half_size = blip_res / 2 * (width - 1); - eq.generate( &fimpulse [blip_res], half_size ); - - int i; - - // need mirror slightly past center for calculation - for ( i = blip_res; i--; ) - fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; - - // starts at 0 - for ( i = 0; i < blip_res; i++ ) - fimpulse [i] = 0.0f; - - // find rescale factor - double total = 0.0; - for ( i = 0; i < half_size; i++ ) - total += fimpulse [blip_res + i]; - - //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB - //double const base_unit = 37888.0; // allows treble to +5 dB - double const base_unit = 32768.0; // necessary for blip_unscaled to work - double rescale = base_unit / 2 / total; - kernel_unit = (long) base_unit; - - // integrate, first difference, rescale, convert to int - double sum = 0.0; - double next = 0.0; - int const impulses_size = this->impulses_size(); - for ( i = 0; i < impulses_size; i++ ) - { - impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); - sum += fimpulse [i]; - next += fimpulse [i + blip_res]; - } - adjust_impulse(); - - // volume might require rescaling - double vol = volume_unit_; - if ( vol ) - { - volume_unit_ = 0.0; - volume_unit( vol ); - } -} - -void Blip_Synth_::volume_unit( double new_unit ) -{ - if ( new_unit != volume_unit_ ) - { - // use default eq if it hasn't been set yet - if ( !kernel_unit ) - treble_eq( -8.0 ); - - volume_unit_ = new_unit; - double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; - - if ( factor > 0.0 ) - { - int shift = 0; - - // if unit is really small, might need to attenuate kernel - while ( factor < 2.0 ) - { - shift++; - factor *= 2.0; - } - - if ( shift ) - { - kernel_unit >>= shift; - assert( kernel_unit > 0 ); // fails if volume unit is too low - - // keep values positive to avoid round-towards-zero of sign-preserving - // right shift for negative values - long offset = 0x8000 + (1 << (shift - 1)); - long offset2 = 0x8000 >> shift; - for ( int i = impulses_size(); i--; ) - impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); - adjust_impulse(); - } - } - delta_factor = (int) floor( factor + 0.5 ); - //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); - } -} -#endif - -long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) -{ - long count = samples_avail(); - if ( count > max_samples ) - count = max_samples; - - if ( count ) - { - int const bass = BLIP_READER_BASS( *this ); - BLIP_READER_BEGIN( reader, *this ); - - if ( !stereo ) - { - for ( blip_long n = count; n; --n ) - { - blip_long s = BLIP_READER_READ( reader ); - if ( (blip_sample_t) s != s ) - s = 0x7FFF - (s >> 24); - *out++ = (blip_sample_t) s; - BLIP_READER_NEXT( reader, bass ); - } - } - else - { - for ( blip_long n = count; n; --n ) - { - blip_long s = BLIP_READER_READ( reader ); - if ( (blip_sample_t) s != s ) - s = 0x7FFF - (s >> 24); - *out = (blip_sample_t) s; - out += 2; - BLIP_READER_NEXT( reader, bass ); - } - } - BLIP_READER_END( reader, *this ); - - remove_samples( count ); - } - return count; -} - -void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) -{ - if ( buffer_size_ == silent_buf_size ) - { - assert( 0 ); - return; - } - - buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; - - int const sample_shift = blip_sample_bits - 16; - int prev = 0; - while ( count-- ) - { - blip_long s = (blip_long) *in++ << sample_shift; - *out += s - prev; - prev = s; - ++out; - } - *out -= prev; -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h b/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h deleted file mode 100644 index 73341b8c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h +++ /dev/null @@ -1,485 +0,0 @@ -// Band-limited sound synthesis buffer - -// Blip_Buffer 0.4.1 -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - -// internal -#include -#include -typedef int32_t blip_long; -typedef uint32_t blip_ulong; - -// Time unit at source clock rate -typedef blip_long blip_time_t; - -// Output samples are 16-bit signed, with a range of -32768 to 32767 -typedef short blip_sample_t; -enum { blip_sample_max = 32767 }; - -class Blip_Buffer { -public: - typedef const char* blargg_err_t; - - // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults - // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there - // isn't enough memory, returns error without affecting current buffer setup. - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); - - // Set number of source time units per second - void clock_rate( long ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begins - // a new time frame at the end of the current frame. - void end_frame( blip_time_t time ); - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Returns number of samples actually read and removed. If stereo is - // true, increments 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); - -// Additional optional features - - // Current output sample rate - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // Number of source time units per second - long clock_rate() const; - - // Set frequency high-pass filter frequency, where higher values reduce bass more - void bass_freq( int frequency ); - - // Number of samples delay from synthesis to samples read out - int output_latency() const; - - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clears out any samples waiting rather than the entire buffer. - void clear( int entire_buffer = 1 ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - -// Experimental features - - // Count number of clocks needed until 'count' samples will be available. - // If buffer can't even hold 'count' samples, returns number of clocks until - // buffer becomes full. - blip_time_t count_clocks( long count ) const; - - // Number of raw samples that can be mixed within frame of specified duration. - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( blip_sample_t const* buf, long count ); - - // not documented yet - void set_modified() { modified_ = 1; } - int clear_modified() { int b = modified_; modified_ = 0; return b; } - typedef blip_ulong blip_resampled_time_t; - void remove_silence( long count ); - blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } - blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } - blip_resampled_time_t clock_rate_factor( long clock_rate ) const; -public: - Blip_Buffer(); - ~Blip_Buffer(); - - // Deprecated - typedef blip_resampled_time_t resampled_time_t; - blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } - blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } -private: - // noncopyable - Blip_Buffer( const Blip_Buffer& ); - Blip_Buffer& operator = ( const Blip_Buffer& ); -public: - typedef blip_time_t buf_t_; - blip_ulong factor_; - blip_resampled_time_t offset_; - buf_t_* buffer_; - blip_long buffer_size_; - blip_long reader_accum_; - int bass_shift_; -private: - long sample_rate_; - long clock_rate_; - int bass_freq_; - int length_; - int modified_; - friend class Blip_Reader; -}; - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -// Number of bits in resample ratio fraction. Higher values give a more accurate ratio -// but reduce maximum buffer size. -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif - -// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in -// noticeable broadband noise when synthesizing high frequency square waves. -// Affects size of Blip_Synth objects since they store the waveform directly. -#ifndef BLIP_PHASE_BITS - #if BLIP_BUFFER_FAST - #define BLIP_PHASE_BITS 8 - #else - #define BLIP_PHASE_BITS 6 - #endif -#endif - - // Internal - typedef blip_ulong blip_resampled_time_t; - int const blip_widest_impulse_ = 16; - int const blip_buffer_extra_ = blip_widest_impulse_ + 2; - int const blip_res = 1 << BLIP_PHASE_BITS; - class blip_eq_t; - - class Blip_Synth_Fast_ { - public: - Blip_Buffer* buf; - int last_amp; - int delta_factor; - - void volume_unit( double ); - Blip_Synth_Fast_(); - void treble_eq( blip_eq_t const& ) { } - }; - - class Blip_Synth_ { - public: - Blip_Buffer* buf; - int last_amp; - int delta_factor; - - void volume_unit( double ); - Blip_Synth_( short* impulses, int width ); - void treble_eq( blip_eq_t const& ); - private: - double volume_unit_; - short* const impulses; - int const width; - blip_long kernel_unit; - int impulses_size() const { return blip_res / 2 * width + 1; } - void adjust_impulse(); - }; - -// Quality level. Start with blip_good_quality. -const int blip_med_quality = 8; -const int blip_good_quality = 12; -const int blip_high_quality = 16; - -// Range specifies the greatest expected change in amplitude. Calculate it -// by finding the difference between the maximum and minimum expected -// amplitudes (max - min). -template -class Blip_Synth { -public: - // Set overall volume of waveform - void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } - - // Configure low-pass filter (see blip_buffer.txt) - void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } - - // Get/set Blip_Buffer used for output - Blip_Buffer* output() const { return impl.buf; } - void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } - - // Update amplitude of waveform at given time. Using this requires a separate - // Blip_Synth for each waveform. - void update( blip_time_t time, int amplitude ); - -// Low-level interface - - // Add an amplitude transition of specified delta, optionally into specified buffer - // rather than the one set with output(). Delta can be positive or negative. - // The actual change in amplitude is delta * (volume / range) - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } - - // Works directly in terms of fractional output samples. Contact author for more info. - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - - // Same as offset(), except code is inlined for higher performance - void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t t, int delta ) const { - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); - } - -private: -#if BLIP_BUFFER_FAST - Blip_Synth_Fast_ impl; -#else - Blip_Synth_ impl; - typedef short imp_t; - imp_t impulses [blip_res * (quality / 2) + 1]; -public: - Blip_Synth() : impl( impulses, quality ) { } -#endif -}; - -// Low-pass equalization parameters -class blip_eq_t { -public: - // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce - // treble, small positive values (0 to 5.0) increase treble. - blip_eq_t( double treble_db = 0 ); - - // See blip_buffer.txt - blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); - -private: - double treble; - long rolloff_freq; - long sample_rate; - long cutoff_freq; - void generate( float* out, int count ) const; - friend class Blip_Synth_; -}; - -int const blip_sample_bits = 30; - -// Dummy Blip_Buffer to direct sound output to, for easy muting without -// having to stop sound code. -class Silent_Blip_Buffer : public Blip_Buffer { - buf_t_ buf [blip_buffer_extra_ + 1]; -public: - // The following cannot be used (an assertion will fail if attempted): - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); - blip_time_t count_clocks( long count ) const; - void mix_samples( blip_sample_t const* buf, long count ); - - Silent_Blip_Buffer(); -}; - - #if defined (__GNUC__) || _MSC_VER >= 1100 - #define BLIP_RESTRICT __restrict - #else - #define BLIP_RESTRICT - #endif - -// Optimized reading from Blip_Buffer, for use in custom sample output - -// Begin reading from buffer. Name should be unique to the current block. -#define BLIP_READER_BEGIN( name, blip_buffer ) \ - const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ - blip_long name##_reader_accum = (blip_buffer).reader_accum_ - -// Get value to pass to BLIP_READER_NEXT() -#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) - -// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal -// code at the cost of having no bass control -int const blip_reader_default_bass = 9; - -// Current sample -#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) - -// Current raw sample in full internal resolution -#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) - -// Advance to next sample -#define BLIP_READER_NEXT( name, bass ) \ - (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) - -// End reading samples from buffer. The number of samples read must now be removed -// using Blip_Buffer::remove_samples(). -#define BLIP_READER_END( name, blip_buffer ) \ - (void) ((blip_buffer).reader_accum_ = name##_reader_accum) - - -// Compatibility with older version -const long blip_unscaled = 65535; -const int blip_low_quality = blip_med_quality; -const int blip_best_quality = blip_high_quality; - -// Deprecated; use BLIP_READER macros as follows: -// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); -// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); -// r.read() -> BLIP_READER_READ( r ) -// r.read_raw() -> BLIP_READER_READ_RAW( r ) -// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) -// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) -// r.end( buf ) -> BLIP_READER_END( r, buf ) -class Blip_Reader { -public: - int begin( Blip_Buffer& ); - blip_long read() const { return accum >> (blip_sample_bits - 16); } - blip_long read_raw() const { return accum; } - void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } - void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } - -private: - const Blip_Buffer::buf_t_* buf; - blip_long accum; -}; - -// End of public interface - -#include - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the - // need for a longer buffer as set by set_sample_rate(). - assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); - delta *= impl.delta_factor; - blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); - int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); - -#if BLIP_BUFFER_FAST - blip_long left = buf [0] + delta; - - // Kind of crappy, but doing shift after multiply results in overflow. - // Alternate way of delaying multiply by delta_factor results in worse - // sub-sample resolution. - blip_long right = (delta >> BLIP_PHASE_BITS) * phase; - left -= right; - right += buf [1]; - - buf [0] = left; - buf [1] = right; -#else - - int const fwd = (blip_widest_impulse_ - quality) / 2; - int const rev = fwd + quality - 2; - int const mid = quality / 2 - 1; - - imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; - - #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - - // straight forward implementation resulted in better code on GCC for x86 - - #define ADD_IMP( out, in ) \ - buf [out] += (blip_long) imp [blip_res * (in)] * delta - - #define BLIP_FWD( i ) {\ - ADD_IMP( fwd + i, i );\ - ADD_IMP( fwd + 1 + i, i + 1 );\ - } - #define BLIP_REV( r ) {\ - ADD_IMP( rev - r, r + 1 );\ - ADD_IMP( rev + 1 - r, r );\ - } - - BLIP_FWD( 0 ) - if ( quality > 8 ) BLIP_FWD( 2 ) - if ( quality > 12 ) BLIP_FWD( 4 ) - { - ADD_IMP( fwd + mid - 1, mid - 1 ); - ADD_IMP( fwd + mid , mid ); - imp = impulses + phase; - } - if ( quality > 12 ) BLIP_REV( 6 ) - if ( quality > 8 ) BLIP_REV( 4 ) - BLIP_REV( 2 ) - - ADD_IMP( rev , 1 ); - ADD_IMP( rev + 1, 0 ); - - #else - - // for RISC processors, help compiler by reading ahead of writes - - #define BLIP_FWD( i ) {\ - blip_long t0 = i0 * delta + buf [fwd + i];\ - blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ - i0 = imp [blip_res * (i + 2)];\ - buf [fwd + i] = t0;\ - buf [fwd + 1 + i] = t1;\ - } - #define BLIP_REV( r ) {\ - blip_long t0 = i0 * delta + buf [rev - r];\ - blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ - i0 = imp [blip_res * (r - 1)];\ - buf [rev - r] = t0;\ - buf [rev + 1 - r] = t1;\ - } - - blip_long i0 = *imp; - BLIP_FWD( 0 ) - if ( quality > 8 ) BLIP_FWD( 2 ) - if ( quality > 12 ) BLIP_FWD( 4 ) - { - blip_long t0 = i0 * delta + buf [fwd + mid - 1]; - blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; - imp = impulses + phase; - i0 = imp [blip_res * mid]; - buf [fwd + mid - 1] = t0; - buf [fwd + mid ] = t1; - } - if ( quality > 12 ) BLIP_REV( 6 ) - if ( quality > 8 ) BLIP_REV( 4 ) - BLIP_REV( 2 ) - - blip_long t0 = i0 * delta + buf [rev ]; - blip_long t1 = *imp * delta + buf [rev + 1]; - buf [rev ] = t0; - buf [rev + 1] = t1; - #endif - -#endif -} - -#undef BLIP_FWD -#undef BLIP_REV - -template -#if BLIP_BUFFER_FAST - inline -#endif -void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const -{ - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); -} - -template -#if BLIP_BUFFER_FAST - inline -#endif -void Blip_Synth::update( blip_time_t t, int amp ) -{ - int delta = amp - impl.last_amp; - impl.last_amp = amp; - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); -} - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } -inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : - treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } - -inline int Blip_Buffer::length() const { return length_; } -inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } -inline long Blip_Buffer::sample_rate() const { return sample_rate_; } -inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } -inline long Blip_Buffer::clock_rate() const { return clock_rate_; } -inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } - -inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) -{ - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum_; - return blip_buf.bass_shift_; -} - -int const blip_max_length = 0; -int const blip_default_length = 250; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp deleted file mode 100644 index 063444fe..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Classic_Emu.h" - -#include "Multi_Buffer.h" -#include - -/* 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" - -Classic_Emu::Classic_Emu() -{ - buf = 0; - stereo_buffer = 0; - voice_types = 0; - - // 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; -} - -void Classic_Emu::set_equalizer_( equalizer_t const& eq ) -{ - Music_Emu::set_equalizer_( eq ); - update_eq( eq.treble ); - if ( buf ) - buf->bass_freq( equalizer().bass ); -} - -blargg_err_t Classic_Emu::set_sample_rate_( long 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, 0, 0, 0 ); - } - else - { - Multi_Buffer::channel_t ch = buf->channel( i, (voice_types ? voice_types [i] : 0) ); - 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( long rate ) -{ - clock_rate_ = rate; - buf->clock_rate( rate ); -} - -blargg_err_t Classic_Emu::setup_buffer( long rate ) -{ - change_clock_rate( rate ); - RETURN_ERR( buf->set_channel_count( voice_count() ) ); - set_equalizer( equalizer() ); - buf_changed_count = buf->channels_changed_count(); - return 0; -} - -blargg_err_t Classic_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - buf->clear(); - return 0; -} - -blargg_err_t Classic_Emu::play_( long count, sample_t* out ) -{ - long remain = count; - while ( remain ) - { - 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(); - } - int msec = buf->length(); - blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; - RETURN_ERR( run_clocks( clocks_emulated, msec ) ); - assert( clocks_emulated ); - buf->end_frame( clocks_emulated ); - } - } - return 0; -} - -// Rom_Data - -blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in, - int header_size, void* header_out, int fill, long pad_size ) -{ - long file_offset = pad_size - header_size; - - rom_addr = 0; - mask = 0; - size_ = 0; - rom.clear(); - - file_size_ = in.remain(); - if ( file_size_ <= header_size ) // <= because there must be data after header - return gme_wrong_file_type; - blargg_err_t err = rom.resize( file_offset + file_size_ + pad_size ); - if ( !err ) - err = in.read( rom.begin() + file_offset, file_size_ ); - if ( err ) - { - rom.clear(); - return err; - } - - file_size_ -= header_size; - memcpy( header_out, &rom [file_offset], header_size ); - - memset( rom.begin() , fill, pad_size ); - memset( rom.end() - pad_size, fill, pad_size ); - - return 0; -} - -void Rom_Data_::set_addr_( long addr, int unit ) -{ - rom_addr = addr - unit - pad_extra; - - long rounded = (addr + file_size_ + unit - 1) / unit * unit; - if ( rounded <= 0 ) - { - rounded = 0; - } - else - { - int shift = 0; - unsigned long max_addr = (unsigned long) (rounded - 1); - while ( max_addr >> shift ) - shift++; - mask = (1L << shift) - 1; - } - - if ( addr < 0 ) - addr = 0; - size_ = rounded; - if ( rom.resize( rounded - rom_addr + pad_extra ) ) { } // OK if shrink fails - - if ( 0 ) - { - dprintf( "addr: %X\n", addr ); - dprintf( "file_size: %d\n", file_size_ ); - dprintf( "rounded: %d\n", rounded ); - dprintf( "mask: $%X\n", mask ); - } -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h deleted file mode 100644 index 8cd822ca..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h +++ /dev/null @@ -1,127 +0,0 @@ -// Common aspects of emulators which use Blip_Buffer for sound output - -// Game_Music_Emu 0.5.2 -#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 { -public: - Classic_Emu(); - ~Classic_Emu(); - void set_buffer( Multi_Buffer* ); -protected: - // Services - enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; - void set_voice_types( int const* t ) { voice_types = t; } - blargg_err_t setup_buffer( long clock_rate ); - long clock_rate() const { return clock_rate_; } - void change_clock_rate( long ); // experimental - - // Overridable - virtual void set_voice( int index, Blip_Buffer* center, - Blip_Buffer* left, Blip_Buffer* right ) = 0; - virtual void update_eq( blip_eq_t const& ) = 0; - virtual blargg_err_t start_track_( int track ) = 0; - virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) = 0; -protected: - blargg_err_t set_sample_rate_( long sample_rate ); - void mute_voices_( int ); - void set_equalizer_( equalizer_t const& ); - blargg_err_t play_( long, sample_t* ); -private: - Multi_Buffer* buf; - Multi_Buffer* stereo_buffer; // NULL if using custom buffer - long 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; -} - -// ROM data handler, used by several Classic_Emu derivitives. Loads file data -// with padding on both sides, allowing direct use in bank mapping. The main purpose -// is to allow all file data to be loaded with only one read() call (for efficiency). - -class Rom_Data_ { -public: - typedef unsigned char byte; -protected: - enum { pad_extra = 8 }; - blargg_vector rom; - long file_size_; - blargg_long rom_addr; - blargg_long mask; - blargg_long size_; // TODO: eliminate - - blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out, - int fill, long pad_size ); - void set_addr_( long addr, int unit ); -}; - -template -class Rom_Data : public Rom_Data_ { - enum { pad_size = unit + pad_extra }; -public: - // Load file data, using already-loaded header 'h' if not NULL. Copy header - // from loaded file data into *out and fill unmapped bytes with 'fill'. - blargg_err_t load( Data_Reader& in, int header_size, void* header_out, int fill ) - { - return load_rom_data_( in, header_size, header_out, fill, pad_size ); - } - - // Size of file data read in (excluding header) - long file_size() const { return file_size_; } - - // Pointer to beginning of file data - byte* begin() const { return rom.begin() + pad_size; } - - // Set address that file data should start at - void set_addr( long addr ) { set_addr_( addr, unit ); } - - // Free data - void clear() { rom.clear(); } - - // Size of data + start addr, rounded to a multiple of unit - long size() const { return size_; } - - // Pointer to unmapped page filled with same value - byte* unmapped() { return rom.begin(); } - - // Mask address to nearest power of two greater than size() - blargg_long mask_addr( blargg_long addr ) const - { - #ifdef check - check( addr <= mask ); - #endif - return addr & mask; - } - - // Pointer to page starting at addr. Returns unmapped() if outside data. - byte* at_addr( blargg_long addr ) - { - blargg_ulong offset = mask_addr( addr ) - rom_addr; - if ( offset > blargg_ulong (rom.size() - pad_size) ) - offset = 0; // unmapped - return &rom [offset]; - } -}; - -#ifndef GME_APU_HOOK - #define GME_APU_HOOK( emu, addr, data ) ((void) 0) -#endif - -#ifndef GME_FRAME_HOOK - #define GME_FRAME_HOOK( emu ) ((void) 0) -#else - #define GME_FRAME_HOOK_DEFINED 1 -#endif - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp b/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp deleted file mode 100644 index 5bbfbf55..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// 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/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h b/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h deleted file mode 100644 index 00b53b9e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h +++ /dev/null @@ -1,151 +0,0 @@ -// 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/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp b/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp deleted file mode 100644 index 8644517c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Dual_Resampler.h" - -#include -#include - -/* 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" - -unsigned const resampler_extra = 256; - -Dual_Resampler::Dual_Resampler() { } - -Dual_Resampler::~Dual_Resampler() { } - -blargg_err_t Dual_Resampler::reset( int pairs ) -{ - // expand allocations a bit - RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) ); - resize( pairs ); - resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2); - return resampler.buffer_size( resampler_size ); -} - -void Dual_Resampler::resize( int pairs ) -{ - int new_sample_buf_size = pairs * 2; - 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.ratio()) * 2 + 2; - clear(); - } -} - -void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) -{ - long pair_count = sample_buf_size >> 1; - blip_time_t blip_time = blip_buf.count_clocks( pair_count ); - int sample_count = oversamples_per_frame - resampler.written(); - - int new_count = play_frame( blip_time, sample_count, resampler.buffer() ); - assert( new_count < resampler_size ); - - blip_buf.end_frame( blip_time ); - assert( blip_buf.samples_avail() == pair_count ); - - resampler.write( new_count ); - - long count = resampler.read( sample_buf.begin(), sample_buf_size ); - assert( count == (long) sample_buf_size ); - - mix_samples( blip_buf, out ); - blip_buf.remove_samples( pair_count ); -} - -void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_buf ) -{ - // empty extra buffer - long remain = sample_buf_size - buf_pos; - if ( remain ) - { - if ( remain > count ) - remain = count; - count -= remain; - memcpy( out, &sample_buf [buf_pos], remain * sizeof *out ); - out += remain; - buf_pos += remain; - } - - // entire frames - while ( count >= (long) sample_buf_size ) - { - play_frame_( blip_buf, out ); - out += sample_buf_size; - count -= sample_buf_size; - } - - // extra - if ( count ) - { - play_frame_( blip_buf, sample_buf.begin() ); - buf_pos = count; - memcpy( out, sample_buf.begin(), count * sizeof *out ); - out += count; - } -} - -void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out ) -{ - Blip_Reader sn; - int bass = sn.begin( blip_buf ); - const dsample_t* in = sample_buf.begin(); - - for ( int n = sample_buf_size >> 1; n--; ) - { - int s = sn.read(); - blargg_long l = (blargg_long) in [0] * 2 + s; - if ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - sn.next( bass ); - blargg_long r = (blargg_long) in [1] * 2 + s; - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - in += 2; - out [0] = l; - out [1] = r; - out += 2; - } - - sn.end( blip_buf ); -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h b/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h deleted file mode 100644 index 61beb8a0..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h +++ /dev/null @@ -1,50 +0,0 @@ -// Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. - -// Game_Music_Emu 0.5.2 -#ifndef DUAL_RESAMPLER_H -#define DUAL_RESAMPLER_H - -#include "Fir_Resampler.h" -#include "Blip_Buffer.h" - -class Dual_Resampler { -public: - Dual_Resampler(); - virtual ~Dual_Resampler(); - - typedef short dsample_t; - - double setup( double oversample, double rolloff, double gain ); - blargg_err_t reset( int max_pairs ); - void resize( int pairs_per_frame ); - void clear(); - - void dual_play( long count, dsample_t* out, Blip_Buffer& ); - -protected: - virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; -private: - - blargg_vector sample_buf; - int sample_buf_size; - int oversamples_per_frame; - int buf_pos; - int resampler_size; - - Fir_Resampler<12> resampler; - void mix_samples( Blip_Buffer&, dsample_t* ); - void play_frame_( Blip_Buffer&, dsample_t* ); -}; - -inline double Dual_Resampler::setup( double oversample, double rolloff, double gain ) -{ - return resampler.time_ratio( oversample, rolloff, gain * 0.5 ); -} - -inline void Dual_Resampler::clear() -{ - buf_pos = sample_buf_size; - resampler.clear(); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp b/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp deleted file mode 100644 index 730f8e94..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Effects_Buffer.h" - -#include - -/* 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" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -typedef blargg_long fixed_t; - -#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) -#define FMUL( x, y ) (((x) * (y)) >> 15) - -const unsigned echo_size = 4096; -const unsigned echo_mask = echo_size - 1; -BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2 - -const unsigned reverb_size = 8192 * 2; -const unsigned reverb_mask = reverb_size - 1; -BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2 - -Effects_Buffer::config_t::config_t() -{ - pan_1 = -0.15f; - pan_2 = 0.15f; - reverb_delay = 88.0f; - reverb_level = 0.12f; - echo_delay = 61.0f; - echo_level = 0.10f; - delay_variance = 18.0f; - effects_enabled = false; -} - -void Effects_Buffer::set_depth( double d ) -{ - float f = (float) d; - config_t c; - c.pan_1 = -0.6f * f; - c.pan_2 = 0.6f * f; - c.reverb_delay = 880 * 0.1f; - c.echo_delay = 610 * 0.1f; - if ( f > 0.5 ) - f = 0.5; // TODO: more linear reduction of extreme reverb/echo - c.reverb_level = 0.5f * f; - c.echo_level = 0.30f * f; - c.delay_variance = 180 * 0.1f; - c.effects_enabled = (d > 0.0f); - config( c ); -} - -Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 ) -{ - buf_count = center_only ? max_buf_count - 4 : max_buf_count; - - echo_pos = 0; - reverb_pos = 0; - - stereo_remain = 0; - effect_remain = 0; - effects_enabled = false; - set_depth( 0 ); -} - -Effects_Buffer::~Effects_Buffer() { } - -blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) -{ - if ( !echo_buf.size() ) - RETURN_ERR( echo_buf.resize( echo_size ) ); - - if ( !reverb_buf.size() ) - RETURN_ERR( reverb_buf.resize( reverb_size ) ); - - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - - config( config_ ); - clear(); - - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Effects_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Effects_Buffer::bass_freq( int freq ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( freq ); -} - -void Effects_Buffer::clear() -{ - stereo_remain = 0; - effect_remain = 0; - if ( echo_buf.size() ) - memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); - - if ( reverb_buf.size() ) - memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); - - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -inline int pin_range( int n, int max, int min = 0 ) -{ - if ( n < min ) - return min; - if ( n > max ) - return max; - return n; -} - -void Effects_Buffer::config( const config_t& cfg ) -{ - channels_changed(); - - // clear echo and reverb buffers - if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf.size() ) - { - memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); - memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); - } - - config_ = cfg; - - if ( config_.effects_enabled ) - { - // convert to internal format - - chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); - chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0]; - - chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 ); - chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; - - chans.reverb_level = TO_FIXED( config_.reverb_level ); - chans.echo_level = TO_FIXED( config_.echo_level ); - - int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); - - int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate()); - chans.reverb_delay_l = pin_range( reverb_size - - (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); - chans.reverb_delay_r = pin_range( reverb_size + 1 - - (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); - - int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); - chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset), - echo_size - 1 ); - chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), - echo_size - 1 ); - - chan_types [0].center = &bufs [0]; - chan_types [0].left = &bufs [3]; - chan_types [0].right = &bufs [4]; - - chan_types [1].center = &bufs [1]; - chan_types [1].left = &bufs [3]; - chan_types [1].right = &bufs [4]; - - chan_types [2].center = &bufs [2]; - chan_types [2].left = &bufs [5]; - chan_types [2].right = &bufs [6]; - assert( 2 < chan_types_count ); - } - else - { - // set up outputs - for ( unsigned i = 0; i < chan_types_count; i++ ) - { - channel_t& c = chan_types [i]; - c.center = &bufs [0]; - c.left = &bufs [1]; - c.right = &bufs [2]; - } - } - - if ( buf_count < max_buf_count ) - { - for ( int i = 0; i < chan_types_count; i++ ) - { - channel_t& c = chan_types [i]; - c.left = c.center; - c.right = c.center; - } - } -} - -Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type ) -{ - int out = 2; - if ( !type ) - { - out = i % 5; - if ( out > 2 ) - out = 2; - } - else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) - { - out = type & 1; - } - return chan_types [out]; -} - -void Effects_Buffer::end_frame( blip_time_t clock_count ) -{ - int bufs_used = 0; - for ( int i = 0; i < buf_count; i++ ) - { - bufs_used |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); - } - - int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); - if ( (bufs_used & stereo_mask) && buf_count == max_buf_count ) - stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - if ( effects_enabled || config_.effects_enabled ) - effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - effects_enabled = config_.effects_enabled; -} - -long Effects_Buffer::samples_avail() const -{ - return bufs [0].samples_avail() * 2; -} - -long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) -{ - require( total_samples % 2 == 0 ); // count must be even - - long remain = bufs [0].samples_avail(); - if ( remain > (total_samples >> 1) ) - remain = (total_samples >> 1); - total_samples = remain; - while ( remain ) - { - int active_bufs = buf_count; - long count = remain; - - // optimizing mixing to skip any channels which had nothing added - if ( effect_remain ) - { - if ( count > effect_remain ) - count = effect_remain; - - if ( stereo_remain ) - { - mix_enhanced( out, count ); - } - else - { - mix_mono_enhanced( out, count ); - active_bufs = 3; - } - } - else if ( stereo_remain ) - { - mix_stereo( out, count ); - active_bufs = 3; - } - else - { - mix_mono( out, count ); - active_bufs = 1; - } - - out += count * 2; - remain -= count; - - stereo_remain -= count; - if ( stereo_remain < 0 ) - stereo_remain = 0; - - effect_remain -= count; - if ( effect_remain < 0 ) - effect_remain = 0; - - for ( int i = 0; i < buf_count; i++ ) - { - if ( i < active_bufs ) - bufs [i].remove_samples( count ); - else - bufs [i].remove_silence( count ); // keep time synchronized - } - } - - return total_samples * 2; -} - -void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [0] ); - BLIP_READER_BEGIN( c, bufs [0] ); - - // unrolled loop - for ( blargg_long n = count >> 1; n; --n ) - { - blargg_long cs0 = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - - blargg_long cs1 = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - - if ( (BOOST::int16_t) cs0 != cs0 ) - cs0 = 0x7FFF - (cs0 >> 24); - ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); - - if ( (BOOST::int16_t) cs1 != cs1 ) - cs1 = 0x7FFF - (cs1 >> 24); - ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); - out += 4; - } - - if ( count & 1 ) - { - int s = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - out [0] = s; - out [1] = s; - if ( (BOOST::int16_t) s != s ) - { - s = 0x7FFF - (s >> 24); - out [0] = s; - out [1] = s; - } - } - - BLIP_READER_END( c, bufs [0] ); -} - -void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [0] ); - BLIP_READER_BEGIN( c, bufs [0] ); - BLIP_READER_BEGIN( l, bufs [1] ); - BLIP_READER_BEGIN( r, bufs [2] ); - - while ( count-- ) - { - int cs = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - int left = cs + BLIP_READER_READ( l ); - int right = cs + BLIP_READER_READ( r ); - BLIP_READER_NEXT( l, bass ); - BLIP_READER_NEXT( r, bass ); - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - - BLIP_READER_END( r, bufs [2] ); - BLIP_READER_END( l, bufs [1] ); - BLIP_READER_END( c, bufs [0] ); -} - -void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [2] ); - BLIP_READER_BEGIN( center, bufs [2] ); - BLIP_READER_BEGIN( sq1, bufs [0] ); - BLIP_READER_BEGIN( sq2, bufs [1] ); - - blip_sample_t* const reverb_buf = this->reverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - while ( count-- ) - { - int sum1_s = BLIP_READER_READ( sq1 ); - int sum2_s = BLIP_READER_READ( sq2 ); - - BLIP_READER_NEXT( sq1, bass ); - BLIP_READER_NEXT( sq2, bass ); - - int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + - FMUL( sum2_s, chans.pan_2_levels [0] ) + - reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - - int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + - FMUL( sum2_s, chans.pan_2_levels [1] ) + - reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - - fixed_t reverb_level = chans.reverb_level; - reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); - reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); - reverb_pos = (reverb_pos + 2) & reverb_mask; - - int sum3_s = BLIP_READER_READ( center ); - BLIP_READER_NEXT( center, bass ); - - int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); - int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - - echo_buf [echo_pos] = sum3_s; - echo_pos = (echo_pos + 1) & echo_mask; - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); -} - -void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [2] ); - BLIP_READER_BEGIN( center, bufs [2] ); - BLIP_READER_BEGIN( l1, bufs [3] ); - BLIP_READER_BEGIN( r1, bufs [4] ); - BLIP_READER_BEGIN( l2, bufs [5] ); - BLIP_READER_BEGIN( r2, bufs [6] ); - BLIP_READER_BEGIN( sq1, bufs [0] ); - BLIP_READER_BEGIN( sq2, bufs [1] ); - - blip_sample_t* const reverb_buf = this->reverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - while ( count-- ) - { - int sum1_s = BLIP_READER_READ( sq1 ); - int sum2_s = BLIP_READER_READ( sq2 ); - - BLIP_READER_NEXT( sq1, bass ); - BLIP_READER_NEXT( sq2, bass ); - - int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + - FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + - reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - - int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + - FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + - reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - - BLIP_READER_NEXT( l1, bass ); - BLIP_READER_NEXT( r1, bass ); - - fixed_t reverb_level = chans.reverb_level; - reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); - reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); - reverb_pos = (reverb_pos + 2) & reverb_mask; - - int sum3_s = BLIP_READER_READ( center ); - BLIP_READER_NEXT( center, bass ); - - int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); - int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - - BLIP_READER_NEXT( l2, bass ); - BLIP_READER_NEXT( r2, bass ); - - echo_buf [echo_pos] = sum3_s; - echo_pos = (echo_pos + 1) & echo_mask; - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( l1, bufs [3] ); - BLIP_READER_END( r1, bufs [4] ); - BLIP_READER_END( l2, bufs [5] ); - BLIP_READER_END( r2, bufs [6] ); - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h b/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h deleted file mode 100644 index eb0aa67a..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h +++ /dev/null @@ -1,86 +0,0 @@ -// Multi-channel effects buffer with panning, echo and reverb - -// Game_Music_Emu 0.5.2 -#ifndef EFFECTS_BUFFER_H -#define EFFECTS_BUFFER_H - -#include "Multi_Buffer.h" - -// Effects_Buffer uses several buffers and outputs stereo sample pairs. -class Effects_Buffer : public Multi_Buffer { -public: - // If center_only is true, only center buffers are created and - // less memory is used. - Effects_Buffer( bool center_only = false ); - - // Channel Effect Center Pan - // --------------------------------- - // 0,5 reverb pan_1 - // 1,6 reverb pan_2 - // 2,7 echo - - // 3 echo - - // 4 echo - - - // Channel configuration - struct config_t { - double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right - double pan_2; - double echo_delay; // msec - double echo_level; // 0.0 to 1.0 - double reverb_delay; // msec - double delay_variance; // difference between left/right delays (msec) - double reverb_level; // 0.0 to 1.0 - bool effects_enabled; // if false, use optimized simple mixer - config_t(); - }; - - // Set configuration of buffer - virtual void config( const config_t& ); - void set_depth( double ); - -public: - ~Effects_Buffer(); - blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int, int ); - void end_frame( blip_time_t ); - long read_samples( blip_sample_t*, long ); - long samples_avail() const; -private: - typedef long fixed_t; - - enum { max_buf_count = 7 }; - Blip_Buffer bufs [max_buf_count]; - enum { chan_types_count = 3 }; - channel_t chan_types [3]; - config_t config_; - long stereo_remain; - long effect_remain; - int buf_count; - bool effects_enabled; - - blargg_vector reverb_buf; - blargg_vector echo_buf; - int reverb_pos; - int echo_pos; - - struct { - fixed_t pan_1_levels [2]; - fixed_t pan_2_levels [2]; - int echo_delay_l; - int echo_delay_r; - fixed_t echo_level; - int reverb_delay_l; - int reverb_delay_r; - fixed_t reverb_level; - } chans; - - void mix_mono( blip_sample_t*, blargg_long ); - void mix_stereo( blip_sample_t*, blargg_long ); - void mix_enhanced( blip_sample_t*, blargg_long ); - void mix_mono_enhanced( blip_sample_t*, blargg_long ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp b/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp deleted file mode 100644 index 4e0a4631..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Fir_Resampler.h" - -#include -#include -#include -#include - -/* Copyright (C) 2004-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" - -#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 ), - write_offset( width * stereo - stereo ), - impulses( impulses_ ) -{ - write_pos = 0; - res = 1; - imp_phase = 0; - skip_bits = 0; - step = stereo; - ratio_ = 1.0; -} - -Fir_Resampler_::~Fir_Resampler_() { } - -void Fir_Resampler_::clear() -{ - imp_phase = 0; - if ( buf.size() ) - { - write_pos = &buf [write_offset]; - memset( buf.begin(), 0, write_offset * sizeof buf [0] ); - } -} - -blargg_err_t Fir_Resampler_::buffer_size( int new_size ) -{ - RETURN_ERR( buf.resize( new_size + write_offset ) ); - clear(); - return 0; -} - -double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) -{ - ratio_ = new_factor; - - double fstep = 0.0; - { - double least_error = 2; - double pos = 0; - res = -1; - for ( int r = 1; r <= max_res; r++ ) - { - pos += ratio_; - double nearest = floor( pos + 0.5 ); - double error = fabs( pos - nearest ); - if ( error < least_error ) - { - res = r; - fstep = nearest / res; - least_error = error; - } - } - } - - skip_bits = 0; - - step = stereo * (int) floor( fstep ); - - ratio_ = fstep; - fstep = fmod( fstep, 1.0 ); - - double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; - double pos = 0.0; - input_per_cycle = 0; - for ( int i = 0; i < res; i++ ) - { - gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, - double (0x7FFF * gain * filter), - (int) width_, impulses + i * width_ ); - - pos += fstep; - input_per_cycle += step; - if ( pos >= 0.9999999 ) - { - pos -= 1.0; - skip_bits |= 1 << i; - input_per_cycle++; - } - } - - clear(); - - return ratio_; -} - -int Fir_Resampler_::input_needed( blargg_long output_count ) const -{ - blargg_long input_count = 0; - - unsigned long skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - while ( (output_count -= 2) > 0 ) - { - input_count += step + (skip & 1) * stereo; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count -= 2; - } - - long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); - if ( input_extra < 0 ) - input_extra = 0; - return input_extra; -} - -int Fir_Resampler_::avail_( blargg_long input_count ) const -{ - int cycle_count = input_count / input_per_cycle; - int output_count = cycle_count * res * stereo; - input_count -= cycle_count * input_per_cycle; - - blargg_ulong skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - while ( input_count >= 0 ) - { - input_count -= step + (skip & 1) * stereo; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count += 2; - } - return output_count; -} - -int Fir_Resampler_::skip_input( long count ) -{ - int remain = write_pos - buf.begin(); - int max_count = remain - width_ * stereo; - if ( count > max_count ) - count = max_count; - - remain -= count; - write_pos = &buf [remain]; - memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); - - return count; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h b/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h deleted file mode 100644 index 339dfce3..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h +++ /dev/null @@ -1,171 +0,0 @@ -// Finite impulse response (FIR) resampler with adjustable FIR size - -// Game_Music_Emu 0.5.2 -#ifndef FIR_RESAMPLER_H -#define FIR_RESAMPLER_H - -#include "blargg_common.h" -#include - -class Fir_Resampler_ { -public: - - // Use Fir_Resampler (below) - - // Set input/output resampling ratio and optionally low-pass rolloff and gain. - // Returns actual ratio used (rounded to internal precision). - double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); - - // Current input/output ratio - double ratio() const { return ratio_; } - -// Input - - typedef short sample_t; - - // Resize and clear input buffer - blargg_err_t buffer_size( int ); - - // Clear input buffer. At least two output samples will be available after - // two input samples are written. - void clear(); - - // Number of input samples that can be written - int max_write() const { return buf.end() - write_pos; } - - // Pointer to place to write input samples - sample_t* buffer() { return write_pos; } - - // Notify resampler that 'count' input samples have been written - void write( long count ); - - // Number of input samples in buffer - int written() const { return write_pos - &buf [write_offset]; } - - // Skip 'count' input samples. Returns number of samples actually skipped. - int skip_input( long count ); - -// Output - - // Number of extra input samples needed until 'count' output samples are available - int input_needed( blargg_long count ) const; - - // Number of output samples available - int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } - -public: - ~Fir_Resampler_(); -protected: - enum { stereo = 2 }; - enum { max_res = 32 }; - blargg_vector buf; - sample_t* write_pos; - int res; - int imp_phase; - int const width_; - int const write_offset; - blargg_ulong skip_bits; - int step; - int input_per_cycle; - double ratio_; - sample_t* impulses; - - Fir_Resampler_( int width, sample_t* ); - int avail_( blargg_long input_count ) const; -}; - -// Width is number of points in FIR. Must be even and 4 or more. More points give -// better quality and rolloff effectiveness, and take longer to calculate. -template -class Fir_Resampler : public Fir_Resampler_ { - BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 ); - short impulses [max_res] [width]; -public: - Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } - - // Read at most 'count' samples. Returns number of samples actually read. - typedef short sample_t; - int read( sample_t* out, blargg_long count ); -}; - -// End of public interface - -inline void Fir_Resampler_::write( long count ) -{ - write_pos += count; - assert( write_pos <= buf.end() ); -} - -template -int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) -{ - sample_t* out = out_begin; - const sample_t* in = buf.begin(); - sample_t* end_pos = write_pos; - blargg_ulong skip = skip_bits >> imp_phase; - sample_t const* imp = impulses [imp_phase]; - int remain = res - imp_phase; - int const step = this->step; - - count >>= 1; - - if ( end_pos - in >= width * stereo ) - { - end_pos -= width * stereo; - do - { - count--; - - // accumulate in extended precision - blargg_long l = 0; - blargg_long r = 0; - - const sample_t* i = in; - if ( count < 0 ) - break; - - for ( int n = width / 2; n; --n ) - { - int pt0 = imp [0]; - l += pt0 * i [0]; - r += pt0 * i [1]; - int pt1 = imp [1]; - imp += 2; - l += pt1 * i [2]; - r += pt1 * i [3]; - i += 4; - } - - remain--; - - l >>= 15; - r >>= 15; - - in += (skip * stereo) & stereo; - skip >>= 1; - in += step; - - if ( !remain ) - { - imp = impulses [0]; - skip = skip_bits; - remain = res; - } - - out [0] = (sample_t) l; - out [1] = (sample_t) r; - out += 2; - } - while ( in <= end_pos ); - } - - imp_phase = res - remain; - - int left = write_pos - in; - write_pos = &buf [left]; - memmove( buf.begin(), in, left * sizeof *in ); - - return out - out_begin; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp deleted file mode 100644 index 932ebb83..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp +++ /dev/null @@ -1,306 +0,0 @@ -// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ - -#include "Gb_Apu.h" - -#include - -/* 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" - -unsigned const vol_reg = 0xFF24; -unsigned const status_reg = 0xFF26; - -Gb_Apu::Gb_Apu() -{ - square1.synth = &square_synth; - square2.synth = &square_synth; - wave.synth = &other_synth; - noise.synth = &other_synth; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = &wave; - oscs [3] = &noise; - - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - osc.regs = ®s [i * 5]; - osc.output = 0; - osc.outputs [0] = 0; - osc.outputs [1] = 0; - osc.outputs [2] = 0; - osc.outputs [3] = 0; - } - - set_tempo( 1.0 ); - volume( 1.0 ); - reset(); -} - -void Gb_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - other_synth.treble_eq( eq ); -} - -void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - require( (center && left && right) || (!center && !left && !right) ); - Gb_Osc& osc = *oscs [index]; - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; -} - -void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); -} - -void Gb_Apu::update_volume() -{ - // TODO: doesn't handle differing left/right global volume (support would - // require modification to all oscillator code) - int data = regs [vol_reg - start_addr]; - double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; - square_synth.volume( vol ); - other_synth.volume( vol ); -} - -static unsigned char const powerup_regs [0x20] = { - 0x80,0x3F,0x00,0xFF,0xBF, // square 1 - 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 - 0x7F,0xFF,0x9F,0xFF,0xBF, // wave - 0xFF,0xFF,0x00,0x00,0xBF, // noise - 0x00, // left/right enables - 0x77, // master volume - 0x80, // power - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF -}; - -void Gb_Apu::set_tempo( double t ) -{ - frame_period = 4194304 / 256; // 256 Hz - if ( t != 1.0 ) - frame_period = blip_time_t (frame_period / t); -} - -void Gb_Apu::reset() -{ - next_frame_time = 0; - last_time = 0; - frame_count = 0; - - square1.reset(); - square2.reset(); - wave.reset(); - noise.reset(); - noise.bits = 1; - wave.wave_pos = 0; - - // avoid click at beginning - regs [vol_reg - start_addr] = 0x77; - update_volume(); - - regs [status_reg - start_addr] = 0x01; // force power - write_register( 0, status_reg, 0x00 ); - - static unsigned char const initial_wave [] = { - 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table - 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA - }; - memcpy( wave.wave, initial_wave, sizeof wave.wave ); -} - -void Gb_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_time ); // end_time must not be before previous time - if ( end_time == last_time ) - return; - - while ( true ) - { - blip_time_t time = next_frame_time; - if ( time > end_time ) - time = end_time; - - // run oscillators - for ( int i = 0; i < osc_count; ++i ) - { - Gb_Osc& osc = *oscs [i]; - if ( osc.output ) - { - osc.output->set_modified(); // TODO: misses optimization opportunities? - int playing = false; - if ( osc.enabled && osc.volume && - (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) - playing = -1; - switch ( i ) - { - case 0: square1.run( last_time, time, playing ); break; - case 1: square2.run( last_time, time, playing ); break; - case 2: wave .run( last_time, time, playing ); break; - case 3: noise .run( last_time, time, playing ); break; - } - } - } - last_time = time; - - if ( time == end_time ) - break; - - next_frame_time += frame_period; - - // 256 Hz actions - square1.clock_length(); - square2.clock_length(); - wave.clock_length(); - noise.clock_length(); - - frame_count = (frame_count + 1) & 3; - if ( frame_count == 0 ) - { - // 64 Hz actions - square1.clock_envelope(); - square2.clock_envelope(); - noise.clock_envelope(); - } - - if ( frame_count & 1 ) - square1.clock_sweep(); // 128 Hz action - } -} - -void Gb_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - assert( next_frame_time >= end_time ); - next_frame_time -= end_time; - - assert( last_time >= end_time ); - last_time -= end_time; -} - -void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) -{ - require( (unsigned) data < 0x100 ); - - int reg = addr - start_addr; - if ( (unsigned) reg >= register_count ) - return; - - run_until( time ); - - int old_reg = regs [reg]; - regs [reg] = data; - - if ( addr < vol_reg ) - { - write_osc( reg / 5, reg, data ); - } - else if ( addr == vol_reg && data != old_reg ) // global volume - { - // return all oscs to 0 - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - int amp = osc.last_amp; - osc.last_amp = 0; - if ( amp && osc.enabled && osc.output ) - other_synth.offset( time, -amp, osc.output ); - } - - if ( wave.outputs [3] ) - other_synth.offset( time, 30, wave.outputs [3] ); - - update_volume(); - - if ( wave.outputs [3] ) - other_synth.offset( time, -30, wave.outputs [3] ); - - // oscs will update with new amplitude when next run - } - else if ( addr == 0xFF25 || addr == status_reg ) - { - int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; - int flags = regs [0xFF25 - start_addr] & mask; - - // left/right assignments - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - osc.enabled &= mask; - int bits = flags >> i; - Blip_Buffer* old_output = osc.output; - osc.output_select = (bits >> 3 & 2) | (bits & 1); - osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output ) - { - int amp = osc.last_amp; - osc.last_amp = 0; - if ( amp && old_output ) - other_synth.offset( time, -amp, old_output ); - } - } - - if ( addr == status_reg && data != old_reg ) - { - if ( !(data & 0x80) ) - { - for ( unsigned i = 0; i < sizeof powerup_regs; i++ ) - { - if ( i != status_reg - start_addr ) - write_register( time, i + start_addr, powerup_regs [i] ); - } - } - else - { - //dprintf( "APU powered on\n" ); - } - } - } - else if ( addr >= 0xFF30 ) - { - int index = (addr & 0x0F) * 2; - wave.wave [index] = data >> 4; - wave.wave [index + 1] = data & 0x0F; - } -} - -int Gb_Apu::read_register( blip_time_t time, unsigned addr ) -{ - run_until( time ); - - int index = addr - start_addr; - require( (unsigned) index < register_count ); - int data = regs [index]; - - if ( addr == status_reg ) - { - data = (data & 0x80) | 0x70; - for ( int i = 0; i < osc_count; i++ ) - { - const Gb_Osc& osc = *oscs [i]; - if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) - data |= 1 << i; - } - } - - return data; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h deleted file mode 100644 index e74ebc55..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h +++ /dev/null @@ -1,90 +0,0 @@ -// Nintendo Game Boy PAPU sound chip emulator - -// Gb_Snd_Emu 0.1.5 -#ifndef GB_APU_H -#define GB_APU_H - -#include "Gb_Oscs.h" - -class Gb_Apu { -public: - - // Set overall volume of all oscillators, where 1.0 is full volume - void volume( double ); - - // Set treble equalization - void treble_eq( const blip_eq_t& ); - - // Outputs can be assigned to a single buffer for mono output, or to three - // buffers for stereo output (using Stereo_Buffer to do the mixing). - - // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silences all oscillators. - void output( Blip_Buffer* mono ); - void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, - // silences oscillator. - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* mono ); - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Reset oscillators and internal state - void reset(); - - // Reads and writes at addr must satisfy start_addr <= addr <= end_addr - enum { start_addr = 0xFF10 }; - enum { end_addr = 0xFF3F }; - enum { register_count = end_addr - start_addr + 1 }; - - // Write 'data' to address at specified time - void write_register( blip_time_t, unsigned addr, int data ); - - // Read from address at specified time - int read_register( blip_time_t, unsigned addr ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new frame at time 0. - void end_frame( blip_time_t ); - - void set_tempo( double ); - -public: - Gb_Apu(); -private: - // noncopyable - Gb_Apu( const Gb_Apu& ); - Gb_Apu& operator = ( const Gb_Apu& ); - - Gb_Osc* oscs [osc_count]; - blip_time_t next_frame_time; - blip_time_t last_time; - blip_time_t frame_period; - double volume_unit; - int frame_count; - - Gb_Square square1; - Gb_Square square2; - Gb_Wave wave; - Gb_Noise noise; - BOOST::uint8_t regs [register_count]; - Gb_Square::Synth square_synth; // used by squares - Gb_Wave::Synth other_synth; // used by wave and noise - - void update_volume(); - void run_until( blip_time_t ); - void write_osc( int index, int reg, int data ); -}; - -inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - -inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } - -inline void Gb_Apu::volume( double vol ) -{ - volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; - update_volume(); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp deleted file mode 100644 index b1f22bd9..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp +++ /dev/null @@ -1,1056 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gb_Cpu.h" - -#include - -//#include "gb_cpu_log.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 "gb_cpu_io.h" - -#include "blargg_source.h" - -// Common instructions: -// -// 365880 FA LD A,IND16 -// 355863 20 JR NZ -// 313655 21 LD HL,IMM -// 274580 28 JR Z -// 252878 FE CMP IMM -// 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) - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -inline void Gb_Cpu::set_code_page( int i, uint8_t* p ) -{ - state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size ); -} - -void Gb_Cpu::reset( void* unmapped ) -{ - check( state == &state_ ); - state = &state_; - - state_.remain = 0; - - for ( int i = 0; i < page_count + 1; i++ ) - set_code_page( i, (uint8_t*) unmapped ); - - memset( &r, 0, sizeof r ); - //interrupts_enabled = false; - - blargg_verify_byte_order(); -} - -void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - set_code_page( first_page + i, (uint8_t*) data + i * page_size ); -} - -#define READ( addr ) CPU_READ( this, (addr), s.remain ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), s.remain );} -#define READ_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out ) -#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) - -unsigned const z_flag = 0x80; -unsigned const n_flag = 0x40; -unsigned const h_flag = 0x20; -unsigned const c_flag = 0x10; - -bool Gb_Cpu::run( blargg_long cycle_count ) -{ - state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; - state_t s; - this->state = &s; - memcpy( &s, &this->state_, sizeof s ); - - typedef BOOST::uint16_t uint16_t; - -#if BLARGG_BIG_ENDIAN - #define R8( n ) (r8_ [n]) -#elif BLARGG_LITTLE_ENDIAN - #define R8( n ) (r8_ [(n) ^ 1]) -#else - #error "Byte order of CPU must be known" -#endif - - union { - core_regs_t rg; // individual registers - - struct { - BOOST::uint16_t bc, de, hl, unused; // pairs - } rp; - - uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) - BOOST::uint16_t r16 [4]; // indexed pairs - }; - BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); - - rg = r; - unsigned pc = r.pc; - unsigned sp = r.sp; - unsigned flags = r.flags; - -loop: - - check( (unsigned long) pc < 0x10000 ); - check( (unsigned long) sp < 0x10000 ); - check( (flags & ~0xF0) == 0 ); - - uint8_t const* instr = s.code_map [pc >> page_shift]; - unsigned op; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - op = instr [pc]; - pc++; - instr += pc; - #else - instr += PAGE_OFFSET( pc ); - op = *instr++; - pc++; - #endif - -#define GET_ADDR() GET_LE16( instr ) - - if ( !--s.remain ) - goto stop; - - unsigned data; - data = *instr; - - #ifdef GB_CPU_LOG_H - gb_cpu_log( "new", pc - 1, op, data, instr [1] ); - #endif - - switch ( op ) - { - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ -{\ - pc++;\ - int offset = (BOOST::int8_t) data;\ - if ( !(cond) ) goto loop;\ - pc = uint16_t (pc + offset);\ - goto loop;\ -} - -// Most Common - - case 0x20: // JR NZ - BRANCH( !(flags & z_flag) ) - - case 0x21: // LD HL,IMM (common) - rp.hl = GET_ADDR(); - pc += 2; - goto loop; - - case 0x28: // JR Z - BRANCH( flags & z_flag ) - - { - unsigned temp; - case 0xF0: // LD A,(0xFF00+imm) - temp = data | 0xFF00; - pc++; - goto ld_a_ind_comm; - - case 0xF2: // LD A,(0xFF00+C) - temp = rg.c | 0xFF00; - goto ld_a_ind_comm; - - 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; - } - - case 0xBE: // CMP (HL) - data = READ( rp.hl ); - goto cmp_comm; - - case 0xB8: // CMP B - case 0xB9: // CMP C - case 0xBA: // CMP D - case 0xBB: // CMP E - case 0xBC: // CMP H - case 0xBD: // CMP L - data = R8( op & 7 ); - goto cmp_comm; - - case 0xFE: // CMP IMM - pc++; - cmp_comm: - op = rg.a; - data = op - data; - sub_set_flags: - flags = ((op & 15) - (data & 15)) & h_flag; - flags |= (data >> 4) & c_flag; - flags |= n_flag; - if ( data & 0xFF ) - goto loop; - flags |= z_flag; - 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) - unsigned addr = rp.hl; - READ_FAST( addr, R8( (op >> 3) & 7 ) ); - goto loop; - } - - case 0xC4: // CNZ (next-most-common) - pc += 2; - if ( flags & z_flag ) - goto loop; - call: - pc -= 2; - case 0xCD: // CALL (most-common) - data = pc + 2; - pc = GET_ADDR(); - push: - sp = (sp - 1) & 0xFFFF; - WRITE( sp, data >> 8 ); - sp = (sp - 1) & 0xFFFF; - WRITE( sp, data & 0xFF ); - goto loop; - - case 0xC8: // RNZ (next-most-common) - if ( !(flags & z_flag) ) - goto loop; - case 0xC9: // RET (most common) - ret: - pc = READ( sp ); - pc += 0x100 * READ( sp + 1 ); - sp = (sp + 2) & 0xFFFF; - 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: - pc++; - // now data is the opcode - switch ( data ) { - - { - int temp; - case 0x46: // BIT b,(HL) - case 0x4E: - case 0x56: - case 0x5E: - case 0x66: - case 0x6E: - case 0x76: - case 0x7E: - { - unsigned addr = rp.hl; - READ_FAST( addr, temp ); - 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: - temp = R8( data & 7 ); - bit_comm: - int bit = (~data >> 3) & 7; - flags &= ~n_flag; - flags |= h_flag | z_flag; - flags ^= (temp << bit) & z_flag; - goto loop; - } - - case 0x86: // RES b,(HL) - case 0x8E: - case 0x96: - case 0x9E: - case 0xA6: - case 0xAE: - case 0xB6: - case 0xBE: - case 0xC6: // SET b,(HL) - case 0xCE: - case 0xD6: - case 0xDE: - case 0xE6: - case 0xEE: - case 0xF6: - case 0xFE: { - int temp = READ( rp.hl ); - int bit = 1 << ((data >> 3) & 7); - temp &= ~bit; - if ( !(data & 0x40) ) - bit = 0; - WRITE( rp.hl, temp | bit ); - 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; - - { - int temp; - case 0x36: // SWAP (HL) - temp = READ( 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 - temp = R8( data & 7 ); - swap_comm: - op = (temp >> 4) | (temp << 4); - flags = 0; - goto shift_comm; - } - -// Shift/Rotate - - case 0x06: // RLC (HL) - case 0x16: // RL (HL) - case 0x26: // SLA (HL) - op = READ( rp.hl ); - goto rl_comm; - - case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A - op = R8( data & 7 ); - goto rl_comm; - - case 0x3E: // SRL (HL) - data += 0x10; // bump up to 0x4n to avoid preserving sign bit - case 0x1E: // RR (HL) - case 0x0E: // RRC (HL) - case 0x2E: // SRA (HL) - op = READ( rp.hl ); - goto rr_comm; - - case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A - data += 0x10; // bump up to 0x4n - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A - case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A - case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A - op = R8( data & 7 ); - goto rr_comm; - - } // CB op - assert( false ); // unhandled CB op - - case 0x07: // RLCA - case 0x17: // RLA - data = op; - op = rg.a; - rl_comm: - op <<= 1; - op |= ((data & flags) >> 4) & 1; // RL and carry is set - flags = (op >> 4) & c_flag; // C = bit shifted out - if ( data < 0x10 ) // RLC - op |= op >> 8; - // SLA doesn't fill lower bit - goto shift_comm; - - case 0x0F: // RRCA - case 0x1F: // RRA - data = op; - op = rg.a; - rr_comm: - op |= (data & flags) << 4; // RR and carry is set - flags = (op << 4) & c_flag; // C = bit shifted out - if ( data < 0x10 ) // RRC - op |= op << 8; - op >>= 1; - if ( data & 0x20 ) // SRA propagates sign bit - op |= (op << 1) & 0x80; - shift_comm: - data &= 7; - if ( !(op & 0xFF) ) - flags |= z_flag; - if ( data == 6 ) - goto write_hl_op_ff; - R8( data ) = op; - 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( rp.hl, op & 0xFF ); - 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( data, sp&0xFF ); - data++; - WRITE( 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 [op >> 4] = GET_ADDR(); - pc += 2; - goto loop; - - { - unsigned temp; - case 0xE0: // LD (0xFF00+imm),A - temp = data | 0xFF00; - pc++; - goto write_data_rg_a; - - case 0xE2: // LD (0xFF00+C),A - temp = rg.c | 0xFF00; - goto write_data_rg_a; - - 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( 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( 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 [op >> 4]++; - goto loop; - - case 0x33: // INC SP - sp = (sp + 1) & 0xFFFF; - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - r16 [op >> 4]--; - goto loop; - - case 0x3B: // DEC SP - sp = (sp - 1) & 0xFFFF; - goto loop; - - case 0x34: // INC (HL) - op = rp.hl; - data = READ( op ); - data++; - WRITE( op, data & 0xFF ); - 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; - R8( op ) = data = R8( op ) + 1; - inc_comm: - flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); - goto loop; - - case 0x35: // DEC (HL) - op = rp.hl; - data = READ( op ); - data--; - WRITE( op, data & 0xFF ); - 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: - flags = (flags & c_flag) | n_flag | (((data & 15) + 0x31) & h_flag); - if ( data & 0xFF ) - goto loop; - flags |= z_flag; - goto loop; - -// Add 16-bit - - { - blargg_ulong temp; // need more than 16 bits for carry - unsigned prev; - - case 0xF8: // LD HL,SP+imm - temp = BOOST::int8_t (data); // sign-extend to 16 bits - pc++; - flags = 0; - temp += sp; - prev = sp; - goto add_16_hl; - - case 0xE8: // ADD SP,IMM - temp = BOOST::int8_t (data); // sign-extend to 16 bits - pc++; - flags = 0; - temp += sp; - prev = sp; - sp = temp & 0xFFFF; - goto add_16_comm; - - case 0x39: // ADD HL,SP - temp = sp; - goto add_hl_comm; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - temp = r16 [op >> 4]; - add_hl_comm: - prev = rp.hl; - temp += prev; - flags &= z_flag; - add_16_hl: - rp.hl = temp; - add_16_comm: - flags |= (temp >> 12) & c_flag; - flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; - goto loop; - } - - case 0x86: // ADD (HL) - data = READ( 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: - flags = rg.a; - data += flags; - flags = ((data & 15) - (flags & 15)) & h_flag; - flags |= (data >> 4) & c_flag; - rg.a = data; - if ( data & 0xFF ) - goto loop; - flags |= z_flag; - goto loop; - -// Add/Subtract - - case 0x8E: // ADC (HL) - data = READ( 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: - data += (flags >> 4) & 1; - data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? - goto add_comm; - - case 0x96: // SUB (HL) - data = READ( 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: - op = rg.a; - data = op - data; - rg.a = data; - goto sub_set_flags; - - case 0x9E: // SBC (HL) - data = READ( 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: - data += (flags >> 4) & 1; - data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? - goto sub_comm; - -// 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( rp.hl ); - pc--; - case 0xE6: // AND IMM - pc++; - and_comm: - rg.a &= data; - case 0xA7: // AND A - flags = h_flag | (((rg.a - 1) >> 1) & z_flag); - 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( rp.hl ); - pc--; - case 0xF6: // OR IMM - pc++; - or_comm: - rg.a |= data; - case 0xB7: // OR A - flags = ((rg.a - 1) >> 1) & z_flag; - 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( rp.hl ); - pc--; - case 0xEE: // XOR IMM - pc++; - xor_comm: - data ^= rg.a; - rg.a = data; - data--; - flags = (data >> 1) & z_flag; - goto loop; - - case 0xAF: // XOR A - rg.a = 0; - flags = z_flag; - goto loop; - -// Stack - - case 0xF1: // POP FA - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL (common) - data = READ( sp ); - r16 [(op >> 4) & 3] = data + 0x100 * READ( sp + 1 ); - sp = (sp + 2) & 0xFFFF; - if ( op != 0xF1 ) - goto loop; - flags = rg.flags & 0xF0; - 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 FA - data = (flags << 8) | rg.a; - goto push; - -// Flow control - - case 0xFF: - if ( pc == idle_addr + 1 ) - goto stop; - case 0xC7: case 0xCF: case 0xD7: case 0xDF: // RST - case 0xE7: case 0xEF: case 0xF7: - data = pc; - pc = (op & 0x38) + rst_base; - goto push; - - case 0xCC: // CZ - pc += 2; - if ( flags & z_flag ) - goto call; - goto loop; - - case 0xD4: // CNC - pc += 2; - if ( !(flags & c_flag) ) - goto call; - goto loop; - - case 0xDC: // CC - pc += 2; - if ( flags & c_flag ) - goto call; - goto loop; - - case 0xD9: // RETI - //interrupts_enabled = 1; - goto ret; - - case 0xC0: // RZ - if ( !(flags & z_flag) ) - goto ret; - goto loop; - - case 0xD0: // RNC - if ( !(flags & c_flag) ) - goto ret; - goto loop; - - case 0xD8: // RC - if ( flags & c_flag ) - goto ret; - goto loop; - - case 0x18: // JR - BRANCH( true ) - - case 0x30: // JR NC - BRANCH( !(flags & c_flag) ) - - case 0x38: // JR C - BRANCH( flags & c_flag ) - - case 0xE9: // JP_HL - pc = rp.hl; - goto loop; - - case 0xC3: // JP (next-most-common) - pc = GET_ADDR(); - goto loop; - - case 0xC2: // JP NZ - pc += 2; - if ( !(flags & z_flag) ) - goto jp_taken; - goto loop; - - case 0xCA: // JP Z (most common) - pc += 2; - if ( !(flags & z_flag) ) - goto loop; - jp_taken: - pc -= 2; - pc = GET_ADDR(); - goto loop; - - case 0xD2: // JP NC - pc += 2; - if ( !(flags & c_flag) ) - goto jp_taken; - goto loop; - - case 0xDA: // JP C - pc += 2; - if ( flags & c_flag ) - goto jp_taken; - goto loop; - -// Flags - - case 0x2F: // CPL - rg.a = ~rg.a; - flags |= n_flag | h_flag; - goto loop; - - case 0x3F: // CCF - flags = (flags ^ c_flag) & ~(n_flag | h_flag); - goto loop; - - case 0x37: // SCF - flags = (flags | c_flag) & ~(n_flag | h_flag); - goto loop; - - case 0xF3: // DI - //interrupts_enabled = 0; - goto loop; - - case 0xFB: // EI - //interrupts_enabled = 1; - goto loop; - -// Special - - case 0xDD: case 0xD3: case 0xDB: case 0xE3: case 0xE4: // ? - case 0xEB: case 0xEC: case 0xF4: case 0xFD: case 0xFC: - case 0x10: // STOP - case 0x27: // DAA (I'll have to implement this eventually...) - case 0xBF: - case 0xED: // Z80 prefix - case 0x76: // HALT - s.remain++; - goto stop; - } - - // If this fails then the case above is missing an opcode - assert( false ); - -stop: - pc--; - - // copy state back - STATIC_CAST(core_regs_t&,r) = rg; - r.pc = pc; - r.sp = sp; - r.flags = flags; - - this->state = &state_; - memcpy( &this->state_, &s, sizeof this->state_ ); - - return s.remain > 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h deleted file mode 100644 index 953fbaf5..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h +++ /dev/null @@ -1,93 +0,0 @@ -// Nintendo Game Boy CPU emulator -// Treats every instruction as taking 4 cycles - -// Game_Music_Emu 0.5.2 -#ifndef GB_CPU_H -#define GB_CPU_H - -#include "blargg_common.h" -#include "blargg_endian.h" - -typedef unsigned gb_addr_t; // 16-bit CPU address - -class Gb_Cpu { - enum { clocks_per_instr = 4 }; -public: - typedef BOOST::uint8_t uint8_t; - - // Clear registers and map all pages to unmapped - void reset( void* unmapped = 0 ); - - // Map code memory (memory accessed via the program counter). Start and size - // must be multiple of page_size. - enum { page_size = 0x2000 }; - void map_code( gb_addr_t start, unsigned size, void* code ); - - uint8_t* get_code( gb_addr_t ); - - // Push a byte on the stack - void push_byte( int ); - - // Game Boy Z80 registers. *Not* kept updated during a call to run(). - struct core_regs_t { - #if BLARGG_BIG_ENDIAN - uint8_t b, c, d, e, h, l, flags, a; - #else - uint8_t c, b, e, d, l, h, a, flags; - #endif - }; - - struct registers_t : core_regs_t { - long pc; // more than 16 bits to allow overflow detection - BOOST::uint16_t sp; - }; - registers_t r; - - // Interrupt enable flag set by EI and cleared by DI - //bool interrupts_enabled; // unused - - // Base address for RST vectors (normally 0) - gb_addr_t rst_base; - - // If CPU executes opcode 0xFF at this address, it treats as illegal instruction - enum { idle_addr = 0xF00D }; - - // Run CPU for at least 'count' cycles and return false, or return true if - // illegal instruction is encountered. - bool run( blargg_long count ); - - // Number of clock cycles remaining for most recent run() call - blargg_long remain() const { return state->remain * clocks_per_instr; } - - // Can read this many bytes past end of a page - enum { cpu_padding = 8 }; - -public: - Gb_Cpu() : rst_base( 0 ) { state = &state_; } - enum { page_shift = 13 }; - enum { page_count = 0x10000 >> page_shift }; -private: - // noncopyable - Gb_Cpu( const Gb_Cpu& ); - Gb_Cpu& operator = ( const Gb_Cpu& ); - - struct state_t { - uint8_t* code_map [page_count + 1]; - blargg_long remain; - }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - - void set_code_page( int, uint8_t* ); -}; - -inline BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) -{ - return state->code_map [addr >> page_shift] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp b/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp deleted file mode 100644 index 735653fa..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ - -#include "Gb_Apu.h" - -#include - -/* 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" - -// Gb_Osc - -void Gb_Osc::reset() -{ - delay = 0; - last_amp = 0; - length = 0; - output_select = 3; - output = outputs [output_select]; -} - -void Gb_Osc::clock_length() -{ - if ( (regs [4] & len_enabled_mask) && length ) - length--; -} - -// Gb_Env - -void Gb_Env::clock_envelope() -{ - if ( env_delay && !--env_delay ) - { - env_delay = regs [2] & 7; - int v = volume - 1 + (regs [2] >> 2 & 2); - if ( (unsigned) v < 15 ) - volume = v; - } -} - -bool Gb_Env::write_register( int reg, int data ) -{ - switch ( reg ) - { - case 1: - length = 64 - (regs [1] & 0x3F); - break; - - case 2: - if ( !(data >> 4) ) - enabled = false; - break; - - case 4: - if ( data & trigger ) - { - env_delay = regs [2] & 7; - volume = regs [2] >> 4; - enabled = true; - if ( length == 0 ) - length = 64; - return true; - } - } - return false; -} - -// Gb_Square - -void Gb_Square::reset() -{ - phase = 0; - sweep_freq = 0; - sweep_delay = 0; - Gb_Env::reset(); -} - -void Gb_Square::clock_sweep() -{ - int sweep_period = (regs [0] & period_mask) >> 4; - if ( sweep_period && sweep_delay && !--sweep_delay ) - { - sweep_delay = sweep_period; - regs [3] = sweep_freq & 0xFF; - regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); - - int offset = sweep_freq >> (regs [0] & shift_mask); - if ( regs [0] & 0x08 ) - offset = -offset; - sweep_freq += offset; - - if ( sweep_freq < 0 ) - { - sweep_freq = 0; - } - else if ( sweep_freq >= 2048 ) - { - sweep_delay = 0; // don't modify channel frequency any further - sweep_freq = 2048; // silence sound immediately - } - } -} - -void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) -{ - if ( sweep_freq == 2048 ) - playing = false; - - static unsigned char const table [4] = { 1, 2, 4, 6 }; - int const duty = table [regs [1] >> 6]; - int amp = volume & playing; - if ( phase >= duty ) - amp = -amp; - - int frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 - { - // really high frequency results in DC at half volume - amp = volume >> 1; - playing = false; - } - - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - int const period = (2048 - frequency) * 4; - Blip_Buffer* const output = this->output; - int phase = this->phase; - int delta = amp * 2; - do - { - phase = (phase + 1) & 7; - if ( phase == 0 || phase == duty ) - { - delta = -delta; - synth->offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->phase = phase; - last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Gb_Noise - -void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) -{ - int amp = volume & playing; - int tap = 13 - (regs [3] & 8); - if ( bits >> tap & 2 ) - amp = -amp; - - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; - int period = table [regs [3] & 7] << (regs [3] >> 4); - - // keep parallel resampled time to eliminate time conversion in the loop - Blip_Buffer* const output = this->output; - const blip_resampled_time_t resampled_period = - output->resampled_duration( period ); - blip_resampled_time_t resampled_time = output->resampled_time( time ); - unsigned bits = this->bits; - int delta = amp * 2; - - do - { - unsigned changed = (bits >> tap) + 1; - time += period; - bits <<= 1; - if ( changed & 2 ) - { - delta = -delta; - bits |= 1; - synth->offset_resampled( resampled_time, delta, output ); - } - resampled_time += resampled_period; - } - while ( time < end_time ); - - this->bits = bits; - last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Gb_Wave - -inline void Gb_Wave::write_register( int reg, int data ) -{ - switch ( reg ) - { - case 0: - if ( !(data & 0x80) ) - enabled = false; - break; - - case 1: - length = 256 - regs [1]; - break; - - case 2: - volume = data >> 5 & 3; - break; - - case 4: - if ( data & trigger & regs [0] ) - { - wave_pos = 0; - enabled = true; - if ( length == 0 ) - length = 256; - } - } -} - -void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) -{ - int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 - int frequency; - { - int amp = (wave [wave_pos] >> volume_shift & playing) * 2; - - frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 - { - amp = 30 >> volume_shift & playing; - playing = false; - } - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - int const period = (2048 - frequency) * 2; - int wave_pos = (this->wave_pos + 1) & (wave_size - 1); - - do - { - int amp = (wave [wave_pos] >> volume_shift) * 2; - wave_pos = (wave_pos + 1) & (wave_size - 1); - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->wave_pos = (wave_pos - 1) & (wave_size - 1); - } - delay = time - end_time; -} - -// Gb_Apu::write_osc - -void Gb_Apu::write_osc( int index, int reg, int data ) -{ - reg -= index * 5; - Gb_Square* sq = &square2; - switch ( index ) - { - case 0: - sq = &square1; - case 1: - if ( sq->write_register( reg, data ) && index == 0 ) - { - square1.sweep_freq = square1.frequency(); - if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) - { - square1.sweep_delay = 1; // cause sweep to recalculate now - square1.clock_sweep(); - } - } - break; - - case 2: - wave.write_register( reg, data ); - break; - - case 3: - if ( noise.write_register( reg, data ) ) - noise.bits = 0x7FFF; - } -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h b/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h deleted file mode 100644 index d7f88ea1..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h +++ /dev/null @@ -1,83 +0,0 @@ -// Private oscillators used by Gb_Apu - -// Gb_Snd_Emu 0.1.5 -#ifndef GB_OSCS_H -#define GB_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct Gb_Osc -{ - enum { trigger = 0x80 }; - enum { len_enabled_mask = 0x40 }; - - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int output_select; - BOOST::uint8_t* regs; // osc's 5 registers - - int delay; - int last_amp; - int volume; - int length; - int enabled; - - void reset(); - void clock_length(); - int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } -}; - -struct Gb_Env : Gb_Osc -{ - int env_delay; - - void reset(); - void clock_envelope(); - bool write_register( int, int ); -}; - -struct Gb_Square : Gb_Env -{ - enum { period_mask = 0x70 }; - enum { shift_mask = 0x07 }; - - typedef Blip_Synth Synth; - Synth const* synth; - int sweep_delay; - int sweep_freq; - int phase; - - void reset(); - void clock_sweep(); - void run( blip_time_t, blip_time_t, int playing ); -}; - -struct Gb_Noise : Gb_Env -{ - typedef Blip_Synth Synth; - Synth const* synth; - unsigned bits; - - void run( blip_time_t, blip_time_t, int playing ); -}; - -struct Gb_Wave : Gb_Osc -{ - typedef Blip_Synth Synth; - Synth const* synth; - int wave_pos; - enum { wave_size = 32 }; - BOOST::uint8_t wave [wave_size]; - - void write_register( int, int ); - void run( blip_time_t, blip_time_t, int playing ); -}; - -inline void Gb_Env::reset() -{ - env_delay = 0; - Gb_Osc::reset(); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp deleted file mode 100644 index 30a147e5..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp +++ /dev/null @@ -1,288 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gbs_Emu.h" - -#include "blargg_endian.h" -#include - -/* 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" - -Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000 }; -Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 300 }; - -Gbs_Emu::Gbs_Emu() -{ - set_type( gme_gbs_type ); - - 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 | 0, mixed_type | 0 - }; - set_voice_types( types ); - - set_silence_lookahead( 6 ); - set_max_initial_silence( 21 ); - set_gain( 1.2 ); - - static equalizer_t const eq = { -1.0, 120 }; - set_equalizer( eq ); -} - -Gbs_Emu::~Gbs_Emu() { } - -void Gbs_Emu::unload() -{ - rom.clear(); - 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 ); -} - -blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const -{ - copy_gbs_fields( header_, out ); - return 0; -} - -static blargg_err_t check_gbs_header( void const* header ) -{ - if ( memcmp( header, "GBS", 3 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Gbs_File : Gme_Info_ -{ - Gbs_Emu::header_t h; - - Gbs_File() { set_type( gme_gbs_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - - set_track_count( h.track_count ); - return check_gbs_header( &h ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_gbs_fields( h, out ); - return 0; - } -}; - -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 ) -{ - assert( offsetof (header_t,copyright [32]) == header_size ); - RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); - - set_track_count( header_.track_count ); - RETURN_ERR( check_gbs_header( &header_ ) ); - - if ( header_.vers != 1 ) - set_warning( "Unknown file version" ); - - if ( header_.timer_mode & 0x78 ) - set_warning( "Invalid timer mode" ); - - unsigned 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" ); - - set_voice_count( Gb_Apu::osc_count ); - - apu.volume( gain() ); - - return setup_buffer( 4194304 ); -} - -void Gbs_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - apu.osc_output( i, c, l, r ); -} - -// Emulation - -// see gb_cpu_io.h for read/write functions - -void Gbs_Emu::set_bank( int n ) -{ - blargg_long addr = rom.mask_addr( n * (blargg_long) bank_size ); - if ( addr == 0 && rom.size() > bank_size ) - { - // TODO: what is the correct behavior? Current Game & Watch Gallery - // rip requires that this have no effect or set to bank 1. - //dprintf( "Selected ROM bank 0\n" ); - return; - //n = 1; - } - cpu::map_code( bank_size, bank_size, rom.at_addr( addr ) ); -} - -void Gbs_Emu::update_timer() -{ - if ( header_.timer_mode & 0x04 ) - { - static byte const rates [4] = { 10, 4, 6, 8 }; - int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7); - play_period = (256L - ram [hi_page + 6]) << shift; - } - else - { - play_period = 70224; // 59.73 Hz - } - if ( tempo() != 1.0 ) - play_period = blip_time_t (play_period / tempo()); -} - -static BOOST::uint8_t const sound_data [Gb_Apu::register_count] = { - 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 - 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 - 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave - 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise - 0x77, 0xF3, 0xF1, // vin/volume, status, power mode - 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused - 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data - 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48 -}; - -void Gbs_Emu::cpu_jsr( gb_addr_t addr ) -{ - check( cpu::r.sp == get_le16( header_.stack_ptr ) ); - cpu::r.pc = addr; - cpu_write( --cpu::r.sp, idle_addr >> 8 ); - cpu_write( --cpu::r.sp, idle_addr&0xFF ); -} - -void Gbs_Emu::set_tempo_( double t ) -{ - apu.set_tempo( t ); - update_timer(); -} - -blargg_err_t Gbs_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - 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 - - apu.reset(); - for ( int i = 0; i < (int) sizeof sound_data; i++ ) - apu.write_register( 0, i + apu.start_addr, sound_data [i] ); - - cpu::reset( rom.unmapped() ); - - unsigned load_addr = get_le16( header_.load_addr ); - cpu::rst_base = load_addr; - rom.set_addr( load_addr ); - - 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 ); - - ram [hi_page + 6] = header_.timer_modulo; - ram [hi_page + 7] = header_.timer_mode; - update_timer(); - next_play = play_period; - - cpu::r.a = track; - cpu::r.pc = idle_addr; - cpu::r.sp = get_le16( header_.stack_ptr ); - cpu_time = 0; - cpu_jsr( get_le16( header_.init_addr ) ); - - return 0; -} - -blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) -{ - cpu_time = 0; - while ( cpu_time < duration ) - { - long count = duration - cpu_time; - cpu_time = duration; - bool result = cpu::run( count ); - cpu_time -= cpu::remain(); - - if ( result ) - { - if ( cpu::r.pc == idle_addr ) - { - if ( next_play > duration ) - { - cpu_time = duration; - break; - } - - if ( cpu_time < next_play ) - cpu_time = next_play; - next_play += play_period; - cpu_jsr( get_le16( header_.play_addr ) ); - GME_FRAME_HOOK( this ); - // TODO: handle timer rates different than 60 Hz - } - 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 $%.2x at $%.4x\n", - (int) *cpu::get_code( cpu::r.pc ), (int) cpu::r.pc ); - cpu::r.pc = (cpu::r.pc + 1) & 0xFFFF; - cpu_time += 6; - } - } - } - - duration = cpu_time; - next_play -= cpu_time; - if ( next_play < 0 ) // could go negative if routine is taking too long to return - next_play = 0; - apu.end_frame( cpu_time ); - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h deleted file mode 100644 index 93fe691e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h +++ /dev/null @@ -1,88 +0,0 @@ -// Nintendo Game Boy GBS music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef GBS_EMU_H -#define GBS_EMU_H - -#include "Classic_Emu.h" -#include "Gb_Apu.h" -#include "Gb_Cpu.h" - -class Gbs_Emu : private Gb_Cpu, public Classic_Emu { - typedef Gb_Cpu cpu; -public: - // Equalizer profiles for Game Boy Color speaker and headphones - static equalizer_t const handheld_eq; - static equalizer_t const headphones_eq; - - // GBS file header - enum { header_size = 112 }; - struct header_t - { - 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]; - char author [32]; - char copyright [32]; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_gbs_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - -public: - Gbs_Emu(); - ~Gbs_Emu(); -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); -private: - // rom - enum { bank_size = 0x4000 }; - Rom_Data rom; - void set_bank( int ); - - // timer - blip_time_t cpu_time; - blip_time_t play_period; - blip_time_t next_play; - void update_timer(); - - header_t header_; - void cpu_jsr( gb_addr_t ); - -public: private: friend class Gb_Cpu; - blip_time_t clock() const { return cpu_time - cpu::remain(); } - - enum { joypad_addr = 0xFF00 }; - enum { ram_addr = 0xA000 }; - enum { hi_page = 0xFF00 - ram_addr }; - byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; - Gb_Apu apu; - - int cpu_read( gb_addr_t ); - void cpu_write( gb_addr_t, int ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp b/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp deleted file mode 100644 index 6821c3a5..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gme_File.h" - -#include "blargg_endian.h" -#include - -/* 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" - -const char gme_wrong_file_type [] = "Wrong file type for this emulator"; - -void Gme_File::clear_playlist() -{ - playlist.clear(); - clear_playlist_(); - track_count_ = raw_track_count_; -} - -void Gme_File::unload() -{ - clear_playlist(); // *before* clearing track count - track_count_ = 0; - raw_track_count_ = 0; - file_data.clear(); -} - -Gme_File::Gme_File() -{ - type_ = 0; - user_data_ = 0; - user_cleanup_ = 0; - unload(); // clears fields - blargg_verify_byte_order(); // used by most emulator types, so save them the trouble -} - -Gme_File::~Gme_File() -{ - if ( user_cleanup_ ) - user_cleanup_( user_data_ ); -} - -blargg_err_t Gme_File::load_mem_( byte const* data, long size ) -{ - require( data != file_data.begin() ); // load_mem_() or load_() must be overridden - Mem_File_Reader in( data, size ); - return load_( in ); -} - -blargg_err_t Gme_File::load_( Data_Reader& in ) -{ - RETURN_ERR( file_data.resize( in.remain() ) ); - RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); - return load_mem_( file_data.begin(), file_data.size() ); -} - -// public load functions call this at beginning -void Gme_File::pre_load() { unload(); } - -void Gme_File::post_load_() { } - -// public load functions call this at end -blargg_err_t Gme_File::post_load( blargg_err_t err ) -{ - if ( !track_count() ) - set_track_count( type()->track_count ); - if ( !err ) - post_load_(); - else - unload(); - - return err; -} - -// Public load functions - -blargg_err_t Gme_File::load_mem( void const* in, long size ) -{ - pre_load(); - return post_load( load_mem_( (byte const*) in, size ) ); -} - -blargg_err_t Gme_File::load( Data_Reader& in ) -{ - pre_load(); - return post_load( load_( in ) ); -} - -blargg_err_t Gme_File::load_file( const char* path ) -{ - pre_load(); - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - return post_load( load_( in ) ); -} - -blargg_err_t Gme_File::load_remaining_( void const* h, long s, Data_Reader& in ) -{ - Remaining_Reader rem( h, s, &in ); - return load( rem ); -} - -// Track info - -void Gme_File::copy_field_( char* out, const char* in, int in_size ) -{ - if ( !in || !*in ) - return; - - // remove spaces/junk from beginning - while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) - { - in++; - in_size--; - } - - // truncate - if ( in_size > max_field_ ) - in_size = max_field_; - - // find terminator - int len = 0; - while ( len < in_size && in [len] ) - len++; - - // remove spaces/junk from end - while ( len && unsigned (in [len - 1]) <= ' ' ) - len--; - - // copy - out [len] = 0; - memcpy( out, in, len ); - - // strip out stupid fields that should have been left blank - if ( !strcmp( out, "?" ) || !strcmp( out, "" ) || !strcmp( out, "< ? >" ) ) - out [0] = 0; -} - -void Gme_File::copy_field_( char* out, const char* in ) -{ - copy_field_( out, in, max_field_ ); -} - -blargg_err_t Gme_File::remap_track_( int* track_io ) const -{ - if ( (unsigned) *track_io >= (unsigned) track_count() ) - return "Invalid track"; - - if ( (unsigned) *track_io < (unsigned) playlist.size() ) - { - M3u_Playlist::entry_t const& e = playlist [*track_io]; - *track_io = 0; - if ( e.track >= 0 ) - { - *track_io = e.track; - if ( !(type_->flags_ & 0x02) ) - *track_io -= e.decimal_track; - } - if ( *track_io >= raw_track_count_ ) - return "Invalid track in m3u playlist"; - } - else - { - check( !playlist.size() ); - } - return 0; -} - -blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const -{ - out->track_count = track_count(); - out->length = -1; - out->loop_length = -1; - out->intro_length = -1; - out->song [0] = 0; - - out->game [0] = 0; - out->author [0] = 0; - out->copyright [0] = 0; - out->comment [0] = 0; - out->dumper [0] = 0; - out->system [0] = 0; - - copy_field_( out->system, type()->system ); - - int remapped = track; - RETURN_ERR( remap_track_( &remapped ) ); - RETURN_ERR( track_info_( out, remapped ) ); - - // override with m3u info - if ( playlist.size() ) - { - M3u_Playlist::info_t const& i = playlist.info(); - copy_field_( out->game , i.title ); - copy_field_( out->author, i.engineer ); - copy_field_( out->author, i.composer ); - copy_field_( out->dumper, i.ripping ); - - M3u_Playlist::entry_t const& e = playlist [track]; - copy_field_( out->song, e.name ); - if ( e.length >= 0 ) out->length = e.length * 1000L; - if ( e.intro >= 0 ) out->intro_length = e.intro * 1000L; - if ( e.loop >= 0 ) out->loop_length = e.loop * 1000L; - } - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h b/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h deleted file mode 100644 index a535633b..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h +++ /dev/null @@ -1,145 +0,0 @@ -// Common interface to game music file loading and information - -// Game_Music_Emu 0.5.2 -#ifndef GME_FILE_H -#define GME_FILE_H - -#include "gme.h" -#include "blargg_common.h" -#include "Data_Reader.h" -#include "M3u_Playlist.h" - -// Error returned if file is wrong type -//extern const char gme_wrong_file_type []; // declared in gme.h - -struct Gme_File { -public: -// File loading - - // Each loads game music data from a file and returns an error if - // file is wrong type or is seriously corrupt. They also set warning - // string for minor problems. - - // Load from file on disk - blargg_err_t load_file( const char* path ); - - // Load from custom data source (see Data_Reader.h) - blargg_err_t load( Data_Reader& ); - - // Load from file already read into memory. Keeps pointer to data, so you - // must not free it until you're done with the file. - blargg_err_t load_mem( void const* data, long size ); - - // Load an m3u playlist. Must be done after loading main music file. - blargg_err_t load_m3u( const char* path ); - blargg_err_t load_m3u( Data_Reader& in ); - - // Clears any loaded m3u playlist and any internal playlist that the music - // format supports (NSFE for example). - void clear_playlist(); - -// Informational - - // Type of emulator. For example if this returns gme_nsfe_type, this object - // is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary. - gme_type_t type() const; - - // Most recent warning string, or NULL if none. Clears current warning after - // returning. - const char* warning(); - - // Number of tracks or 0 if no file has been loaded - int track_count() const; - - // Get information for a track (length, name, author, etc.) - // See gme.h for definition of struct track_info_t. - blargg_err_t track_info( track_info_t* out, int track ) const; - -// User data/cleanup - - // Set/get pointer to data you want to associate with this emulator. - // You can use this for whatever you want. - void set_user_data( void* p ) { user_data_ = p; } - void* user_data() const { return user_data_; } - - // Register cleanup function to be called when deleting emulator, or NULL to - // clear it. Passes user_data to cleanup function. - void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } - -public: - // deprecated - int error_count() const; // use warning() -public: - Gme_File(); - virtual ~Gme_File(); - BLARGG_DISABLE_NOTHROW - typedef BOOST::uint8_t byte; -protected: - // Services - void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } - void set_warning( const char* s ) { warning_ = s; } - void set_type( gme_type_t t ) { type_ = t; } - blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining ); - - // Overridable - virtual void unload(); // called before loading file and if loading fails - virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() - virtual blargg_err_t load_mem_( byte const* data, long size ); // use data in memory - virtual blargg_err_t track_info_( track_info_t* out, int track ) const = 0; - virtual void pre_load(); - virtual void post_load_(); - virtual void clear_playlist_() { } - -public: - blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu -private: - // noncopyable - Gme_File( const Gme_File& ); - Gme_File& operator = ( const Gme_File& ); - - gme_type_t type_; - int track_count_; - int raw_track_count_; - const char* warning_; - void* user_data_; - gme_user_cleanup_t user_cleanup_; - M3u_Playlist playlist; - char playlist_warning [64]; - blargg_vector file_data; // only if loaded into memory using default load - - blargg_err_t load_m3u_( blargg_err_t ); - blargg_err_t post_load( blargg_err_t err ); -public: - // track_info field copying - enum { max_field_ = 255 }; - static void copy_field_( char* out, const char* in ); - static void copy_field_( char* out, const char* in, int len ); -}; - -Music_Emu* gme_new_( Music_Emu*, long sample_rate ); - -#define GME_COPY_FIELD( in, out, name ) \ - { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } - -#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 gme_type_t Gme_File::type() const { return type_; } -inline int Gme_File::error_count() const { return warning_ != 0; } -inline int Gme_File::track_count() const { return track_count_; } - -inline const char* Gme_File::warning() -{ - const char* s = warning_; - warning_ = 0; - return s; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp deleted file mode 100644 index 499a9ca2..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp +++ /dev/null @@ -1,379 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gym_Emu.h" - -#include "blargg_endian.h" -#include - -/* 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" - -double const min_tempo = 0.25; -double const oversample_factor = 5 / 3.0; -double const fm_gain = 3.0; - -const long base_clock = 53700300; -const long clock_rate = base_clock / 15; - -Gym_Emu::Gym_Emu() -{ - data = 0; - pos = 0; - set_type( gme_gym_type ); - - static const char* const names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - set_voice_names( names ); - set_silence_lookahead( 1 ); // tracks should already be trimmed -} - -Gym_Emu::~Gym_Emu() { } - -// Track info - -static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out ) -{ - if ( !memcmp( h.tag, "GYMX", 4 ) ) - { - length = length * 50 / 3; // 1000 / 60 - long loop = get_le32( h.loop_start ); - if ( loop ) - { - out->intro_length = loop * 50 / 3; - out->loop_length = length - out->intro_length; - } - else - { - out->length = length; - out->intro_length = length; // make it clear that track is no longer than length - out->loop_length = 0; - } - - // more stupidity where the field should have been left - if ( strcmp( h.song, "Unknown Song" ) ) - GME_COPY_FIELD( h, out, song ); - - if ( strcmp( h.game, "Unknown Game" ) ) - GME_COPY_FIELD( h, out, game ); - - if ( strcmp( h.copyright, "Unknown Publisher" ) ) - GME_COPY_FIELD( h, out, copyright ); - - if ( strcmp( h.dumper, "Unknown Person" ) ) - GME_COPY_FIELD( h, out, dumper ); - - if ( strcmp( h.comment, "Header added by YMAMP" ) ) - GME_COPY_FIELD( h, out, comment ); - } -} - -blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const -{ - get_gym_info( header_, track_length(), out ); - return 0; -} - -static long gym_track_length( byte const* p, byte const* end ) -{ - long time = 0; - while ( p < end ) - { - switch ( *p++ ) - { - case 0: - time++; - break; - - case 1: - case 2: - p += 2; - break; - - case 3: - p += 1; - break; - } - } - return time; -} - -long Gym_Emu::track_length() const { return gym_track_length( data, data_end ); } - -static blargg_err_t check_header( byte const* in, long size, int* data_offset = 0 ) -{ - if ( size < 4 ) - return gme_wrong_file_type; - - if ( memcmp( in, "GYMX", 4 ) == 0 ) - { - if ( size < Gym_Emu::header_size + 1 ) - return gme_wrong_file_type; - - if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) - return "Packed GYM file not supported"; - - if ( data_offset ) - *data_offset = Gym_Emu::header_size; - } - else if ( *in > 3 ) - { - return gme_wrong_file_type; - } - - return 0; -} - -struct Gym_File : Gme_Info_ -{ - byte const* file_begin; - byte const* file_end; - int data_offset; - - Gym_File() { set_type( gme_gym_type ); } - - blargg_err_t load_mem_( byte const* in, long size ) - { - file_begin = in; - file_end = in + size; - data_offset = 0; - return check_header( in, size, &data_offset ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - long length = gym_track_length( &file_begin [data_offset], file_end ); - get_gym_info( *(Gym_Emu::header_t const*) file_begin, length, out ); - return 0; - } -}; - -static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; } -static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } - -gme_type_t_ const gme_gym_type [1] = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }; - -// Setup - -blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate ) -{ - blip_eq_t eq( -32, 8000, sample_rate ); - apu.treble_eq( eq ); - dac_synth.treble_eq( eq ); - apu.volume( 0.135 * fm_gain * gain() ); - dac_synth.volume( 0.125 / 256 * fm_gain * gain() ); - double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() ); - fm_sample_rate = sample_rate * factor; - - RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); - blip_buf.clock_rate( clock_rate ); - - RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) ); - RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) ); - - return 0; -} - -void Gym_Emu::set_tempo_( double t ) -{ - if ( t < min_tempo ) - { - set_tempo( min_tempo ); - return; - } - - if ( blip_buf.sample_rate() ) - { - clocks_per_frame = long (clock_rate / 60 / tempo()); - Dual_Resampler::resize( long (sample_rate() / (60.0 * tempo())) ); - } -} - -void Gym_Emu::mute_voices_( int mask ) -{ - Music_Emu::mute_voices_( mask ); - fm.mute_voices( mask ); - dac_muted = (mask & 0x40) != 0; - apu.output( (mask & 0x80) ? 0 : &blip_buf ); -} - -blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) -{ - assert( offsetof (header_t,packed [4]) == header_size ); - int offset = 0; - RETURN_ERR( check_header( in, size, &offset ) ); - set_voice_count( 8 ); - - data = in + offset; - data_end = in + size; - loop_begin = 0; - - if ( offset ) - header_ = *(header_t const*) in; - else - memset( &header_, 0, sizeof header_ ); - - return 0; -} - -// Emulation - -blargg_err_t Gym_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - - pos = data; - loop_remain = get_le32( header_.loop_start ); - - prev_dac_count = 0; - dac_enabled = false; - dac_amp = -1; - - fm.reset(); - apu.reset(); - blip_buf.clear(); - Dual_Resampler::clear(); - return 0; -} - -void Gym_Emu::run_dac( int dac_count ) -{ - // Guess beginning and end of sample and adjust rate and buffer position accordingly. - - // count dac samples in next frame - int next_dac_count = 0; - const byte* p = this->pos; - int cmd; - while ( (cmd = *p++) != 0 ) - { - int data = *p++; - if ( cmd <= 2 ) - ++p; - if ( cmd == 1 && data == 0x2A ) - next_dac_count++; - } - - // detect beginning and end of sample - int rate_count = dac_count; - int start = 0; - if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) - { - rate_count = next_dac_count; - start = next_dac_count - dac_count; - } - else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) - { - rate_count = prev_dac_count; - } - - // Evenly space samples within buffer section being used - blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count; - - blip_resampled_time_t time = blip_buf.resampled_time( 0 ) + - period * start + (period >> 1); - - int dac_amp = this->dac_amp; - if ( dac_amp < 0 ) - dac_amp = dac_buf [0]; - - for ( int i = 0; i < dac_count; i++ ) - { - int delta = dac_buf [i] - dac_amp; - dac_amp += delta; - dac_synth.offset_resampled( time, delta, &blip_buf ); - time += period; - } - this->dac_amp = dac_amp; -} - -void Gym_Emu::parse_frame() -{ - int dac_count = 0; - const byte* pos = this->pos; - - if ( loop_remain && !--loop_remain ) - loop_begin = pos; // find loop on first time through sequence - - int cmd; - while ( (cmd = *pos++) != 0 ) - { - int data = *pos++; - if ( cmd == 1 ) - { - int data2 = *pos++; - if ( data != 0x2A ) - { - if ( data == 0x2B ) - dac_enabled = (data2 & 0x80) != 0; - - fm.write0( data, data2 ); - } - else if ( dac_count < (int) sizeof dac_buf ) - { - dac_buf [dac_count] = data2; - dac_count += dac_enabled; - } - } - else if ( cmd == 2 ) - { - fm.write1( data, *pos++ ); - } - else if ( cmd == 3 ) - { - apu.write_data( 0, data ); - } - else - { - // to do: many GYM streams are full of errors, and error count should - // reflect cases where music is really having problems - //log_error(); - --pos; // put data back - } - } - - // loop - if ( pos >= data_end ) - { - check( pos == data_end ); - - if ( loop_begin ) - pos = loop_begin; - else - set_track_ended(); - } - this->pos = pos; - - // dac - if ( dac_count && !dac_muted ) - run_dac( dac_count ); - prev_dac_count = dac_count; -} - -int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) -{ - if ( !track_ended() ) - parse_frame(); - - apu.end_frame( blip_time ); - - memset( buf, 0, sample_count * sizeof *buf ); - fm.run( sample_count >> 1, buf ); - - return sample_count; -} - -blargg_err_t Gym_Emu::play_( long count, sample_t* out ) -{ - Dual_Resampler::dual_play( count, out, blip_buf ); - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h deleted file mode 100644 index 05419ea2..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h +++ /dev/null @@ -1,82 +0,0 @@ -// Sega Genesis/Mega Drive GYM music file emulator -// Includes with PCM timing recovery to improve sample quality. - -// Game_Music_Emu 0.5.2 -#ifndef GYM_EMU_H -#define GYM_EMU_H - -#include "Dual_Resampler.h" -#include "Ym2612_Emu.h" -#include "Music_Emu.h" -#include "Sms_Apu.h" - -class Gym_Emu : public Music_Emu, private Dual_Resampler { -public: - // GYM file header - enum { header_size = 428 }; - struct header_t - { - char tag [4]; - char song [32]; - char game [32]; - char copyright [32]; - char emulator [32]; - char dumper [32]; - char comment [256]; - byte loop_start [4]; // in 1/60 seconds, 0 if not looped - byte packed [4]; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_gym_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - enum { gym_rate = 60 }; - long track_length() const; // use track_info() - -public: - Gym_Emu(); - ~Gym_Emu(); -protected: - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t set_sample_rate_( long sample_rate ); - blargg_err_t start_track_( int ); - blargg_err_t play_( long count, sample_t* ); - void mute_voices_( int ); - void set_tempo_( double ); - int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); -private: - // sequence data begin, loop begin, current position, end - const byte* data; - const byte* loop_begin; - const byte* pos; - const byte* data_end; - blargg_long loop_remain; // frames remaining until loop beginning has been located - header_t header_; - double fm_sample_rate; - blargg_long clocks_per_frame; - void parse_frame(); - - // dac (pcm) - int dac_amp; - int prev_dac_count; - bool dac_enabled; - bool dac_muted; - void run_dac( int ); - - // sound - Blip_Buffer blip_buf; - Ym2612_Emu fm; - Blip_Synth dac_synth; - Sms_Apu apu; - byte dac_buf [1024]; -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp deleted file mode 100644 index 22389121..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Hes_Apu.h" - -#include - -/* 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" - -bool const center_waves = true; // reduces asymmetry and clamping when starting notes - -Hes_Apu::Hes_Apu() -{ - Hes_Osc* osc = &oscs [osc_count]; - do - { - osc--; - osc->outputs [0] = 0; - osc->outputs [1] = 0; - osc->chans [0] = 0; - osc->chans [1] = 0; - osc->chans [2] = 0; - } - while ( osc != oscs ); - - reset(); -} - -void Hes_Apu::reset() -{ - latch = 0; - balance = 0xFF; - - Hes_Osc* osc = &oscs [osc_count]; - do - { - osc--; - memset( osc, 0, offsetof (Hes_Osc,outputs) ); - osc->noise_lfsr = 1; - osc->control = 0x40; - osc->balance = 0xFF; - } - while ( osc != oscs ); -} - -void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - oscs [index].chans [0] = center; - oscs [index].chans [1] = left; - oscs [index].chans [2] = right; - - Hes_Osc* osc = &oscs [osc_count]; - do - { - osc--; - balance_changed( *osc ); - } - while ( osc != oscs ); -} - -void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) -{ - Blip_Buffer* const osc_outputs_0 = outputs [0]; // cache often-used values - if ( osc_outputs_0 && control & 0x80 ) - { - int dac = this->dac; - - int const volume_0 = volume [0]; - { - int delta = dac * volume_0 - last_amp [0]; - if ( delta ) - synth_.offset( last_time, delta, osc_outputs_0 ); - osc_outputs_0->set_modified(); - } - - Blip_Buffer* const osc_outputs_1 = outputs [1]; - int const volume_1 = volume [1]; - if ( osc_outputs_1 ) - { - int delta = dac * volume_1 - last_amp [1]; - if ( delta ) - synth_.offset( last_time, delta, osc_outputs_1 ); - osc_outputs_1->set_modified(); - } - - blip_time_t time = last_time + delay; - if ( time < end_time ) - { - if ( noise & 0x80 ) - { - if ( volume_0 | volume_1 ) - { - // noise - int const period = (32 - (noise & 0x1F)) * 64; // TODO: correct? - unsigned noise_lfsr = this->noise_lfsr; - do - { - int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); - // Implemented using "Galios configuration" - // TODO: find correct LFSR algorithm - noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); - //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - synth_.offset( time, delta * volume_0, osc_outputs_0 ); - if ( osc_outputs_1 ) - synth_.offset( time, delta * volume_1, osc_outputs_1 ); - } - time += period; - } - while ( time < end_time ); - - this->noise_lfsr = noise_lfsr; - assert( noise_lfsr ); - } - } - else if ( !(control & 0x40) ) - { - // wave - int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop - int period = this->period * 2; - if ( period >= 14 && (volume_0 | volume_1) ) - { - do - { - int new_dac = wave [phase]; - phase = (phase + 1) & 0x1F; - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - synth_.offset( time, delta * volume_0, osc_outputs_0 ); - if ( osc_outputs_1 ) - synth_.offset( time, delta * volume_1, osc_outputs_1 ); - } - time += period; - } - while ( time < end_time ); - } - else - { - if ( !period ) - { - // TODO: Gekisha Boy assumes that period = 0 silences wave - //period = 0x1000 * 2; - period = 1; - //if ( !(volume_0 | volume_1) ) - // dprintf( "Used period 0\n" ); - } - - // maintain phase when silent - blargg_long count = (end_time - time + period - 1) / period; - phase += count; // phase will be masked below - time += count * period; - } - this->phase = (phase - 1) & 0x1F; // undo pre-advance - } - } - time -= end_time; - if ( time < 0 ) - time = 0; - delay = time; - - this->dac = dac; - last_amp [0] = dac * volume_0; - last_amp [1] = dac * volume_1; - } - last_time = end_time; -} - -void Hes_Apu::balance_changed( Hes_Osc& osc ) -{ - static short const log_table [32] = { // ~1.5 db per step - #define ENTRY( factor ) short (factor * Hes_Osc::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; - - left = log_table [left ]; - right = log_table [right]; - - // optimizing for the common case of being centered also allows easy - // panning using Effects_Buffer - osc.outputs [0] = osc.chans [0]; // center - osc.outputs [1] = 0; - if ( left != right ) - { - osc.outputs [0] = osc.chans [1]; // left - osc.outputs [1] = osc.chans [2]; // right - } - - if ( center_waves ) - { - osc.last_amp [0] += (left - osc.volume [0]) * 16; - osc.last_amp [1] += (right - osc.volume [1]) * 16; - } - - osc.volume [0] = left; - osc.volume [1] = right; -} - -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; - - Hes_Osc* osc = &oscs [osc_count]; - do - { - osc--; - osc->run_until( synth, time ); - balance_changed( *oscs ); - } - while ( osc != oscs ); - } - } - else if ( latch < osc_count ) - { - Hes_Osc& osc = oscs [latch]; - osc.run_until( synth, 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: - if ( &osc >= &oscs [4] ) - 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 ) -{ - Hes_Osc* osc = &oscs [osc_count]; - do - { - osc--; - if ( end_time > osc->last_time ) - osc->run_until( synth, end_time ); - assert( osc->last_time >= end_time ); - osc->last_time -= end_time; - } - while ( osc != oscs ); -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h deleted file mode 100644 index ca0c932f..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h +++ /dev/null @@ -1,66 +0,0 @@ -// Turbo Grafx 16 (PC Engine) PSG sound chip emulator - -// Game_Music_Emu 0.5.2 -#ifndef HES_APU_H -#define HES_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct Hes_Osc -{ - unsigned char wave [32]; - short volume [2]; - int last_amp [2]; - int delay; - int period; - unsigned char noise; - unsigned char phase; - unsigned char balance; - unsigned char dac; - blip_time_t last_time; - - Blip_Buffer* outputs [2]; - Blip_Buffer* chans [3]; - unsigned noise_lfsr; - unsigned char control; - - enum { amp_range = 0x8000 }; - typedef Blip_Synth synth_t; - - void run_until( synth_t& synth, blip_time_t ); -}; - -class Hes_Apu { -public: - void treble_eq( blip_eq_t const& ); - void volume( double ); - - enum { osc_count = 6 }; - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - void reset(); - - enum { start_addr = 0x0800 }; - enum { end_addr = 0x0809 }; - void write_data( blip_time_t, int addr, int data ); - - void end_frame( blip_time_t ); - -public: - Hes_Apu(); -private: - Hes_Osc oscs [osc_count]; - int latch; - int balance; - Hes_Osc::synth_t synth; - - void balance_changed( Hes_Osc& ); - void recalc_chans(); -}; - -inline void Hes_Apu::volume( double v ) { synth.volume( 1.8 / osc_count / Hes_Osc::amp_range * v ); } - -inline void Hes_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp deleted file mode 100644 index 2615a0bb..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp +++ /dev/null @@ -1,1303 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Hes_Cpu.h" - -#include "blargg_endian.h" - -//#include "hes_cpu_log.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 */ - -// 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; - -#define FLUSH_TIME() (void) (s.time = s_time) -#define CACHE_TIME() (void) (s_time = s.time) - -#include "hes_cpu_io.h" - -#include "blargg_source.h" - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -// status flags -int const st_n = 0x80; -int const st_v = 0x40; -int const st_t = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; - -void Hes_Cpu::reset() -{ - check( state == &state_ ); - state = &state_; - - state_.time = 0; - state_.base = 0; - irq_time_ = future_hes_time; - end_time_ = future_hes_time; - - r.status = st_i; - r.sp = 0; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - - blargg_verify_byte_order(); -} - -void Hes_Cpu::set_mmr( int reg, int bank ) -{ - assert( (unsigned) reg <= page_count ); // allow page past end to be set - assert( (unsigned) bank < 0x100 ); - mmr [reg] = bank; - uint8_t const* code = CPU_SET_MMR( this, reg, bank ); - state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift ); -} - -#define TIME (s_time + s.base) - -#define READ( addr ) CPU_READ( this, (addr), TIME ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} -#define READ_LOW( addr ) (ram [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) -#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - -bool Hes_Cpu::run( hes_time_t end_time ) -{ - bool illegal_encountered = false; - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - // even on x86, using s.time in place of s_time was slower - fint16 s_time = s.time; - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; - SET_SP( r.sp ); - - #define IS_NEG (nz & 0x8080) - - #define CALC_STATUS( out ) do {\ - out = status & (st_v | st_d | st_i);\ - out |= ((nz >> 8) | nz) & st_n;\ - out |= c >> 8 & st_c;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & (st_v | st_d | st_i);\ - nz = in << 8;\ - c = nz;\ - nz |= ~in & st_z;\ - } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 - { - fuint8 temp = r.status; - SET_STATUS( temp ); - } - - goto loop; -branch_not_taken: - s_time -= 2; -loop: - - #ifndef NDEBUG - { - hes_time_t correct = end_time_; - if ( !(status & st_i) && correct > irq_time_ ) - correct = irq_time_; - check( s.base == correct ); - /* - static long count; - if ( count == 1844 ) Debugger(); - if ( s.base != correct ) dprintf( "%ld\n", count ); - count++; - */ - } - #endif - - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - - uint8_t const* instr = s.code_map [pc >> page_shift]; - fuint8 opcode; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - opcode = instr [pc]; - pc++; - instr += pc; - #else - instr += PAGE_OFFSET( pc ); - opcode = *instr++; - pc++; - #endif - - // TODO: each reference lists slightly different timing values, ugh - static uint8_t 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,6,// 0 - 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1 - 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2 - 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3 - 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4 - 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5 - 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6 - 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7 - 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8 - 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9 - 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A - 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B - 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C - 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D - 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E - 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F - }; // 0x00 was 8 - - fuint16 data; - data = clock_table [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = *instr; - - #ifdef HES_CPU_LOG_H - log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], - instr [3], instr [4], instr [5] ); - //log_opcode( opcode ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// 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_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) -#define PAGE_CROSS_PENALTY( lsb ) - -// Branch - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ -{\ - fint16 offset = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) ) goto branch_not_taken;\ - pc = BOOST::uint16_t (pc + offset);\ - goto loop;\ -} - - case 0xF0: // BEQ - BRANCH( !((uint8_t) nz) ); - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x10: // BPL - BRANCH( !IS_NEG ); - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x80: // BRA - branch_taken: - BRANCH( true ); - - case 0xFF: - if ( pc == idle_addr + 1 ) - goto idle_done; - 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: { - fuint16 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_PROG( data ) ); - goto loop; - } - -// Subroutine - - case 0x44: // BSR - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, pc ); - goto branch_taken; - - case 0x20: { // JSR - fuint16 temp = pc + 1; - pc = GET_ADDR(); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x60: // RTS - pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); - pc += 1 + READ_LOW( sp ); - sp = (sp - 0xFE) | 0x100; - goto loop; - - case 0x00: // BRK - goto handle_brk; - -// Common - - case 0xBD:{// LDA abs,X - PAGE_CROSS_PENALTY( data + x ); - fuint16 addr = GET_ADDR() + x; - pc += 2; - CPU_READ_FAST( this, addr, TIME, nz ); - a = nz; - goto loop; - } - - case 0x9D:{// STA abs,X - fuint16 addr = GET_ADDR() + x; - pc += 2; - CPU_WRITE_FAST( this, addr, a, TIME ); - goto loop; - } - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xAE:{// LDX abs - fuint16 addr = GET_ADDR(); - pc += 2; - CPU_READ_FAST( this, addr, TIME, nz ); - x = nz; - goto loop; - } - - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - -// Load/store - - { - fuint16 addr; - case 0x91: // STA (ind),Y - addr = 0x100 * READ_LOW( uint8_t (data + 1) ); - addr += READ_LOW( data ) + y; - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - data = uint8_t (data + x); - case 0x92: // STA (ind) - addr = 0x100 * READ_LOW( uint8_t (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: - CPU_WRITE_FAST( this, addr, a, TIME ); - goto loop; - } - - { - fuint16 addr; - case 0xA1: // LDA (ind,X) - data = uint8_t (data + x); - case 0xB2: // LDA (ind) - addr = 0x100 * READ_LOW( uint8_t (data + 1) ); - addr += READ_LOW( data ); - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - PAGE_CROSS_PENALTY( addr ); - addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); - pc++; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - data += y; - PAGE_CROSS_PENALTY( data ); - case 0xAD: // LDA abs - addr = data + 0x100 * GET_MSB(); - pc += 2; - a_nz_read_addr: - CPU_READ_FAST( this, addr, TIME, nz ); - a = nz; - goto loop; - } - - case 0xBE:{// LDX abs,y - PAGE_CROSS_PENALTY( data + y ); - fuint16 addr = GET_ADDR() + y; - pc += 2; - FLUSH_TIME(); - x = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (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 - fuint16 addr; - ADD_PAGE( addr ); - FLUSH_TIME(); - nz = READ( addr ); - CACHE_TIME(); - goto bit_common; - } - case 0x34: // BIT zp,x - data = uint8_t (data + x); - case 0x24: // BIT zp - data = READ_LOW( data ); - case 0x89: // BIT imm - nz = data; - bit_common: - pc++; - status &= ~st_v; - status |= nz & st_v; - 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; - - { - fuint16 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( addr ); - CACHE_TIME(); - goto tst_common; - } - - case 0xA3: // TST zp,x - nz = READ_LOW( uint8_t (GET_MSB() + x) ); - goto tst_common; - - case 0x83: // TST zp - nz = READ_LOW( GET_MSB() ); - tst_common: - pc += 2; - status &= ~st_v; - status |= nz & st_v; - 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; - - { - fuint16 addr; - case 0x0C: // TSB abs - case 0x1C: // TRB abs - addr = GET_ADDR(); - pc++; - goto txb_addr; - - // TODO: everyone lists different behaviors for the status flags, ugh - case 0x04: // TSB zp - case 0x14: // TRB zp - addr = data + ram_addr; - txb_addr: - FLUSH_TIME(); - nz = a | READ( addr ); - if ( opcode & 0x10 ) - nz ^= a; // bits from a will already be set, so this clears them - status &= ~st_v; - status |= nz & st_v; - pc++; - WRITE( 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( data, 0 ); - CACHE_TIME(); - goto loop; - - case 0x74: // STZ zp,x - data = uint8_t (data + x); - case 0x64: // STZ zp - pc++; - WRITE_LOW( data, 0 ); - goto loop; - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (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 = uint8_t (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_CROSS_PENALTY( data ); - case 0xAC:{// LDY abs - fuint16 addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - fuint16 addr = GET_ADDR(); - pc += 2; - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - fuint16 addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( 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 &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - fuint16 addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( 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 &= 0xFF; - goto loop; - -// Logical - -#define ARITH_ADDR_MODES( op )\ - case op - 0x04: /* (ind,x) */\ - data = uint8_t (data + x);\ - case op + 0x0D: /* (ind) */\ - data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ - goto ptr##op;\ - case op + 0x0C:{/* (ind),y */\ - fuint16 temp = READ_LOW( data ) + y;\ - PAGE_CROSS_PENALTY( temp );\ - data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - goto ptr##op;\ - }\ - case op + 0x10: /* zp,X */\ - data = uint8_t (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_CROSS_PENALTY( data );\ - case op + 0x08: /* abs */\ - ADD_PAGE( data );\ - ptr##op:\ - FLUSH_TIME();\ - data = READ( data );\ - CACHE_TIME();\ - case op + 0x04: /* imm */\ - imm##op: - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - 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 ( status & st_d ) - dprintf( "Decimal mode not supported\n" ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= ov >> 2 & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) 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 = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) 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( 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( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (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 = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (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_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - - case 0x1A: // INA - INC_DEC_AXY( a, +1 ) - - case 0xE8: // INX - INC_DEC_AXY( x, +1 ) - - case 0xC8: // INY - INC_DEC_AXY( y, +1 ) - - case 0x3A: // DEA - INC_DEC_AXY( a, -1 ) - - case 0xCA: // DEX - INC_DEC_AXY( x, -1 ) - - case 0x88: // DEY - INC_DEC_AXY( y, -1 ) - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = (unsigned) -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 = (unsigned) -1; - inc_common: - FLUSH_TIME(); - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xA8: // TAY - y = a; - nz = a; - goto loop; - - case 0x98: // TYA - a = y; - nz = y; - goto loop; - - case 0xAA: // TAX - x = a; - nz = a; - goto loop; - - case 0x8A: // TXA - a = x; - 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 ) {\ - fuint8 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 - PUSH( a ); - goto loop; - - case 0xDA: // PHX - PUSH( x ); - goto loop; - - case 0x5A: // PHY - PUSH( y ); - goto loop; - - case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - this->r.status = status; // update externally-visible I flag - if ( (data ^ status) & st_i ) - { - hes_time_t new_time = end_time_; - if ( !(status & st_i) && new_time > irq_time_ ) - new_time = irq_time_; - blargg_long delta = s.base - new_time; - s.base = new_time; - s_time += delta; - } - goto loop; - } - - #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100 - - case 0x68: // PLA - a = nz = POP(); - goto loop; - - case 0xFA: // PLX - x = nz = POP(); - goto loop; - - case 0x7A: // PLY - y = nz = POP(); - goto loop; - - case 0x28:{// PLP - fuint8 temp = POP(); - fuint8 changed = status ^ temp; - SET_STATUS( temp ); - if ( !(changed & st_i) ) - goto loop; // I flag didn't change - if ( status & st_i ) - goto handle_sei; - goto handle_cli; - } - #undef POP - - case 0x08: { // PHP - fuint8 temp; - CALC_STATUS( temp ); - PUSH( temp | st_b ); - goto loop; - } - -// Flags - - case 0x38: // SEC - c = (unsigned) ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) - { - if ( TIME < irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = 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; - irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop - goto loop; - } - delayed_cli: - dprintf( "Delayed CLI not supported\n" ); // TODO: implement - goto loop; - } - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - dprintf( "Delayed SEI not supported\n" ); // TODO: implement - goto loop; - } - -// Special - - case 0x53:{// TAM - fuint8 const 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 = mmr; - do - { - if ( data & 1 ) - a = *in; - in++; - } - while ( (data >>= 1) != 0 ); - goto loop; - } - - case 0x03: // ST0 - case 0x13: // ST1 - case 0x23:{// ST2 - fuint16 addr = opcode >> 4; - if ( addr ) - addr++; - pc++; - FLUSH_TIME(); - CPU_WRITE_VDP( this, addr, data, TIME ); - 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 - //fuint16 operand = GET_MSB(); - dprintf( "SET not handled\n" ); - //switch ( data ) - //{ - //} - illegal_encountered = true; - goto loop; - } - -// Block transfer - - { - fuint16 in_alt; - fint16 in_inc; - fuint16 out_alt; - fint16 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: - fuint16 in = GET_LE16( instr + 0 ); - fuint16 out = GET_LE16( instr + 2 ); - int count = GET_LE16( instr + 4 ); - if ( !count ) - count = 0x10000; - pc += 6; - WRITE_LOW( 0x100 | (sp - 1), y ); - WRITE_LOW( 0x100 | (sp - 2), a ); - WRITE_LOW( 0x100 | (sp - 3), x ); - FLUSH_TIME(); - do - { - // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O - fuint8 t = READ( in ); - in += in_inc; - in &= 0xFFFF; - s.time += 6; - if ( in_alt ) - in_inc = -in_inc; - WRITE( out, t ); - out += out_inc; - out &= 0xFFFF; - if ( out_alt ) - out_inc = -out_inc; - } - while ( --count ); - CACHE_TIME(); - goto loop; - } - -// Illegal - - default: - assert( (unsigned) opcode <= 0xFF ); - dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); - illegal_encountered = true; - goto loop; - } - assert( false ); - - int result_; -handle_brk: - pc++; - result_ = 6; - -interrupt: - { - s_time += 7; - - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); - - sp = (sp - 3) | 0x100; - fuint8 temp; - CALC_STATUS( temp ); - if ( result_ == 6 ) - temp |= st_b; - WRITE_LOW( sp, temp ); - - status &= ~st_d; - status |= st_i; - this->r.status = status; // update externally-visible I flag - - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - goto loop; - } - -idle_done: - s_time = 0; -out_of_time: - pc--; - FLUSH_TIME(); - CPU_DONE( this, TIME, result_ ); - CACHE_TIME(); - if ( result_ > 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - - s.time = s_time; - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return illegal_encountered; -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h deleted file mode 100644 index 437d0908..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h +++ /dev/null @@ -1,125 +0,0 @@ -// PC Engine CPU emulator for use with HES music files - -// Game_Music_Emu 0.5.2 -#ifndef HES_CPU_H -#define HES_CPU_H - -#include "blargg_common.h" -#include - -typedef blargg_long hes_time_t; // clock cycle count -typedef unsigned hes_addr_t; // 16-bit address -enum { future_hes_time = LONG_MAX / 2 + 1 }; - -class Hes_Cpu { -public: - typedef BOOST::uint8_t uint8_t; - - void reset(); - - enum { page_size = 0x2000 }; - enum { page_shift = 13 }; - enum { page_count = 8 }; - void set_mmr( int reg, int bank ); - - uint8_t const* get_code( hes_addr_t ); - - uint8_t ram [page_size]; - - // not kept updated during a call to run() - struct registers_t { - BOOST::uint16_t pc; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - }; - registers_t r; - - // page mapping registers - uint8_t mmr [page_count + 1]; - - // Set end_time and run CPU from current time. Returns true if any illegal - // instructions were encountered. - bool run( hes_time_t end_time ); - - // Time of beginning of next instruction to be executed - hes_time_t time() const { return state->time + state->base; } - void set_time( hes_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } - - hes_time_t irq_time() const { return irq_time_; } - void set_irq_time( hes_time_t ); - - hes_time_t end_time() const { return end_time_; } - void set_end_time( hes_time_t ); - - void end_frame( hes_time_t ); - - // Attempt to execute instruction here results in CPU advancing time to - // lesser of irq_time() and end_time() (or end_time() if IRQs are - // disabled) - enum { idle_addr = 0x1FFF }; - - // Can read this many bytes past end of a page - enum { cpu_padding = 8 }; - -public: - Hes_Cpu() { state = &state_; } - enum { irq_inhibit = 0x04 }; -private: - // noncopyable - Hes_Cpu( const Hes_Cpu& ); - Hes_Cpu& operator = ( const Hes_Cpu& ); - - struct state_t { - uint8_t const* code_map [page_count + 1]; - hes_time_t base; - blargg_long time; - }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - hes_time_t irq_time_; - hes_time_t end_time_; - - void set_code_page( int, void const* ); - inline int update_end_time( hes_time_t end, hes_time_t irq ); -}; - -inline BOOST::uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) -{ - return state->code_map [addr >> page_shift] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; -} - -inline int Hes_Cpu::update_end_time( hes_time_t t, hes_time_t irq ) -{ - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - int delta = state->base - t; - state->base = t; - return delta; -} - -inline void Hes_Cpu::set_irq_time( hes_time_t t ) -{ - state->time += update_end_time( end_time_, (irq_time_ = t) ); -} - -inline void Hes_Cpu::set_end_time( hes_time_t t ) -{ - state->time += update_end_time( (end_time_ = t), irq_time_ ); -} - -inline void Hes_Cpu::end_frame( hes_time_t t ) -{ - assert( state == &state_ ); - state_.base -= t; - if ( irq_time_ < future_hes_time ) irq_time_ -= t; - if ( end_time_ < future_hes_time ) end_time_ -= t; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp deleted file mode 100644 index fafb2666..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Hes_Emu.h" - -#include "blargg_endian.h" -#include - -/* 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 timer_mask = 0x04; -int const vdp_mask = 0x02; -int const i_flag_mask = 0x04; -int const unmapped = 0xFF; - -long const period_60hz = 262 * 455L; // scanlines * clocks per scanline - -Hes_Emu::Hes_Emu() -{ - timer.raw_load = 0; - set_type( gme_hes_type ); - - static const char* const names [Hes_Apu::osc_count] = { - "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2" - }; - set_voice_names( names ); - - static int const types [Hes_Apu::osc_count] = { - wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, - mixed_type | 0, mixed_type | 1 - }; - set_voice_types( types ); - set_silence_lookahead( 6 ); - set_gain( 1.11 ); -} - -Hes_Emu::~Hes_Emu() { } - -void Hes_Emu::unload() -{ - rom.clear(); - Music_Emu::unload(); -} - -// Track info - -static byte const* copy_field( byte const* in, char* out ) -{ - if ( in ) - { - int len = 0x20; - if ( in [0x1F] && !in [0x2F] ) - len = 0x30; // fields are sometimes 16 bytes longer (ugh) - - // since text fields are where any data could be, detect non-text - // and fields with data after zero byte terminator - - int i = 0; - for ( i = 0; i < len && in [i]; i++ ) - if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text - return 0; // non-ASCII found - - for ( ; i < len; i++ ) - if ( in [i] ) - return 0; // data after terminator - - Gme_File::copy_field_( out, (char const*) in, len ); - in += len; - } - return in; -} - -static void copy_hes_fields( byte const* in, track_info_t* out ) -{ - if ( *in >= ' ' ) - { - in = copy_field( in, out->game ); - in = copy_field( in, out->author ); - in = copy_field( in, out->copyright ); - } -} - -blargg_err_t Hes_Emu::track_info_( track_info_t* out, int ) const -{ - copy_hes_fields( rom.begin() + 0x20, out ); - return 0; -} - -static blargg_err_t check_hes_header( void const* header ) -{ - if ( memcmp( header, "HESM", 4 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Hes_File : Gme_Info_ -{ - struct header_t { - char header [Hes_Emu::header_size]; - char unused [0x20]; - byte fields [0x30 * 3]; - } h; - - Hes_File() { set_type( gme_hes_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 ); - blargg_err_t err = in.read( &h, sizeof h ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - return check_hes_header( &h ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_hes_fields( h.fields, out ); - return 0; - } -}; - -static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } -static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } - -gme_type_t_ const gme_hes_type [1] = { "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }; - -// Setup - -blargg_err_t Hes_Emu::load_( Data_Reader& in ) -{ - assert( offsetof (header_t,unused [4]) == header_size ); - RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) ); - - RETURN_ERR( check_hes_header( header_.tag ) ); - - 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. - - long addr = get_le32( header_.addr ); - long size = get_le32( header_.size ); - long const rom_max = 0x100000; - if ( addr & ~(rom_max - 1) ) - { - set_warning( "Invalid address" ); - addr &= rom_max - 1; - } - if ( (unsigned long) (addr + size) > (unsigned long) 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 ); - - set_voice_count( apu.osc_count ); - - apu.volume( gain() ); - - return setup_buffer( 7159091 ); -} - -void Hes_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -void Hes_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - apu.osc_output( i, center, left, right ); -} - -// Emulation - -void Hes_Emu::recalc_timer_load() -{ - timer.load = timer.raw_load * timer_base + 1; -} - -void Hes_Emu::set_tempo_( double t ) -{ - play_period = hes_time_t (period_60hz / t); - timer_base = int (1024 / t); - recalc_timer_load(); -} - -blargg_err_t Hes_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( ram, 0, sizeof ram ); // some HES music relies on zero fill - memset( sgx, 0, sizeof sgx ); - - apu.reset(); - cpu::reset(); - - for ( unsigned i = 0; i < sizeof header_.banks; i++ ) - set_mmr( i, header_.banks [i] ); - set_mmr( page_count, 0xFF ); // unmapped beyond end of address space - - irq.disables = timer_mask | vdp_mask; - irq.timer = future_hes_time; - irq.vdp = future_hes_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; - r.sp = 0xFD; - r.pc = get_le16( header_.init_addr ); - r.a = track; - - recalc_timer_load(); - last_frame_hook = 0; - - return 0; -} - -// Hardware - -void Hes_Emu::cpu_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( 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_Emu::cpu_write_( hes_addr_t addr, int data ) -{ - if ( unsigned (addr - apu.start_addr) <= apu.end_addr - apu.start_addr ) - { - GME_APU_HOOK( this, addr - apu.start_addr, data ); - // avoid going way past end when a long block xfer is writing to I/O space - hes_time_t t = min( time(), end_time() + 8 ); - apu.write_data( t, addr, data ); - return; - } - - hes_time_t time = this->time(); - switch ( addr ) - { - case 0x0000: - case 0x0002: - case 0x0003: - cpu_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_Emu::cpu_read_( hes_addr_t addr ) -{ - hes_time_t time = this->time(); - addr &= page_size - 1; - switch ( addr ) - { - case 0x0000: - if ( irq.vdp > time ) - return 0; - irq.vdp = future_hes_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; - } - - #ifndef NDEBUG - case 0x1000: // I/O port - case 0x180C: // CD-ROM - case 0x180D: - break; - - default: - dprintf( "unmapped read $%04X\n", addr ); - #endif - } - - return unmapped; -} - -// see hes_cpu_io.h for core read/write functions - -// Emulation - -void Hes_Emu::run_until( hes_time_t present ) -{ - while ( vdp.next_vbl < present ) - vdp.next_vbl += play_period; - - hes_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_Emu::irq_changed() -{ - hes_time_t present = time(); - - if ( irq.timer > present ) - { - irq.timer = future_hes_time; - if ( timer.enabled && !timer.fired ) - irq.timer = present + timer.count; - } - - if ( irq.vdp > present ) - { - irq.vdp = future_hes_time; - if ( vdp.control & 0x08 ) - irq.vdp = vdp.next_vbl; - } - - hes_time_t time = future_hes_time; - if ( !(irq.disables & timer_mask) ) time = irq.timer; - if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); - - set_irq_time( time ); -} - -int Hes_Emu::cpu_done() -{ - check( time() >= end_time() || - (!(r.status & i_flag_mask) && time() >= irq_time()) ); - - if ( !(r.status & i_flag_mask) ) - { - hes_time_t present = time(); - - if ( irq.timer <= present && !(irq.disables & timer_mask) ) - { - timer.fired = true; - irq.timer = future_hes_time; - irq_changed(); // overkill, but not worth writing custom code - #if GME_FRAME_HOOK_DEFINED - { - unsigned const threshold = period_60hz / 30; - unsigned long elapsed = present - last_frame_hook; - if ( elapsed - period_60hz + threshold / 2 < threshold ) - { - last_frame_hook = present; - GME_FRAME_HOOK( this ); - } - } - #endif - return 0x0A; - } - - if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) - { - // work around for bugs with music not acknowledging VDP - //run_until( present ); - //irq.vdp = future_hes_time; - //irq_changed(); - #if GME_FRAME_HOOK_DEFINED - last_frame_hook = present; - GME_FRAME_HOOK( this ); - #endif - return 0x08; - } - } - return 0; -} - -static void adjust_time( blargg_long& time, hes_time_t delta ) -{ - if ( time < future_hes_time ) - { - time -= delta; - if ( time < 0 ) - time = 0; - } -} - -blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) -{ - blip_time_t const duration = duration_; // cache - - if ( cpu::run( duration ) ) - set_warning( "Emulation error (illegal instruction)" ); - - check( 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; - #if GME_FRAME_HOOK_DEFINED - last_frame_hook -= duration; - #endif - cpu::end_frame( duration ); - ::adjust_time( irq.timer, duration ); - ::adjust_time( irq.vdp, duration ); - apu.end_frame( duration ); - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h deleted file mode 100644 index 9951eb6a..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h +++ /dev/null @@ -1,94 +0,0 @@ -// TurboGrafx-16/PC Engine HES music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef HES_EMU_H -#define HES_EMU_H - -#include "Classic_Emu.h" -#include "Hes_Apu.h" -#include "Hes_Cpu.h" - -class Hes_Emu : private Hes_Cpu, public Classic_Emu { - typedef Hes_Cpu cpu; -public: - // HES file header - enum { header_size = 0x20 }; - struct header_t - { - byte tag [4]; - byte vers; - byte first_track; - byte init_addr [2]; - byte banks [8]; - byte data_tag [4]; - byte size [4]; - byte addr [4]; - byte unused [4]; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_hes_type; } - -public: - Hes_Emu(); - ~Hes_Emu(); -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); -public: private: friend class Hes_Cpu; - byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space - - int cpu_read_( hes_addr_t ); - int cpu_read( hes_addr_t ); - void cpu_write_( hes_addr_t, int data ); - void cpu_write( hes_addr_t, int ); - void cpu_write_vdp( int addr, int data ); - byte const* cpu_set_mmr( int page, int bank ); - int cpu_done(); -private: - Rom_Data rom; - header_t header_; - hes_time_t play_period; - hes_time_t last_frame_hook; - int timer_base; - - struct { - hes_time_t last_time; - blargg_long count; - blargg_long load; - int raw_load; - byte enabled; - byte fired; - } timer; - - struct { - hes_time_t next_vbl; - byte latch; - byte control; - } vdp; - - struct { - hes_time_t timer; - hes_time_t vdp; - byte disables; - } irq; - - void recalc_timer_load(); - - // large items - Hes_Apu apu; - byte sgx [3 * page_size + cpu_padding]; - - void irq_changed(); - void run_until( hes_time_t ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp deleted file mode 100644 index 37c4a724..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp +++ /dev/null @@ -1,1706 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -/* -Last validated with zexall 2006.11.14 2:19 PM -* Doesn't implement the R register or immediate interrupt after EI. -* Address wrap-around isn't completely correct, but is prevented from crashing emulator. -*/ - -#include "Kss_Cpu.h" - -#include "blargg_endian.h" -#include - -//#include "z80_cpu_log.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 */ - -#define SYNC_TIME() (void) (s.time = s_time) -#define RELOAD_TIME() (void) (s_time = s.time) - -// Callbacks to emulator - -#define CPU_OUT( cpu, addr, data, time )\ - kss_cpu_out( this, time, addr, data ) - -#define CPU_IN( cpu, addr, time )\ - kss_cpu_in( this, time, addr ) - -#define CPU_WRITE( cpu, addr, data, time )\ - (SYNC_TIME(), kss_cpu_write( this, addr, data )) - -#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; - -#define SZ28P( n ) szpc [n] -#define SZ28PC( n ) szpc [n] -#define SZ28C( n ) (szpc [n] & ~P04) -#define SZ28( n ) SZ28C( n ) - -#define SET_R( n ) (void) (r.r = n) -#define GET_R() (r.r) - -Kss_Cpu::Kss_Cpu() -{ - state = &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 Kss_Cpu::set_page( int i, void* write, void const* read ) -{ - blargg_long offset = KSS_CPU_PAGE_OFFSET( i * (blargg_long) page_size ); - state->write [i] = (byte *) write - offset; - state->read [i] = (byte const*) read - offset; -} - -void Kss_Cpu::reset( void* unmapped_write, void const* unmapped_read ) -{ - check( state == &state_ ); - state = &state_; - state_.time = 0; - 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 Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ) -{ - // address range must begin and end on page boundaries - require( addr % page_size == 0 ); - require( size % page_size == 0 ); - - unsigned first_page = addr / page_size; - for ( unsigned i = size / page_size; i--; ) - { - blargg_long offset = i * (blargg_long) page_size; - set_page( first_page + i, (byte*) write + offset, (byte const*) read + offset ); - } -} - -#define TIME (s_time + s.base) -#define RW_MEM( addr, rw ) (s.rw [(addr) >> page_shift] [KSS_CPU_PAGE_OFFSET( addr )]) -#define READ_PROG( addr ) RW_MEM( addr, read ) -#define READ( addr ) READ_PROG( addr ) -//#define WRITE( addr, data ) (void) (RW_MEM( addr, write ) = data) -#define WRITE( addr, data ) CPU_WRITE( this, addr, data, TIME ) -#define READ_WORD( addr ) GET_LE16( &READ( addr ) ) -#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data ) -#define IN( addr ) CPU_IN( this, addr, TIME ) -#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) - -#if BLARGG_BIG_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [n]) -#elif BLARGG_LITTLE_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) -#else - #error "Byte order of CPU must be known" -#endif - -//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) - -// help compiler see that it can just adjust stack offset, saving an extra instruction -#define R16( n, shift, offset )\ - (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) - -#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e -#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f -#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g -#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h - -// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 -static byte const ed_dd_timing [0x100] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, -0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, -0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, -0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -bool Kss_Cpu::run( cpu_time_t end_time ) -{ - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - bool warning = false; - - typedef BOOST::int8_t int8_t; - - union { - regs_t rg; - pairs_t rp; - uint8_t r8_ [8]; // indexed - uint16_t r16_ [4]; - }; - rg = this->r.b; - - cpu_time_t s_time = s.time; - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 iy = r.iy; - int flags = r.b.flags; - - goto loop; -jr_not_taken: - s_time -= 5; - goto loop; -call_not_taken: - s_time -= 7; -jp_not_taken: - pc += 2; -loop: - - check( (unsigned long) pc < 0x10000 ); - check( (unsigned long) sp < 0x10000 ); - check( (unsigned) flags < 0x100 ); - check( (unsigned) ix < 0x10000 ); - check( (unsigned) iy < 0x10000 ); - - uint8_t const* instr = s.read [pc >> page_shift]; -#define GET_ADDR() GET_LE16( instr ) - - fuint8 opcode; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - opcode = instr [pc]; - pc++; - instr += pc; - #else - instr += KSS_CPU_PAGE_OFFSET( pc ); - opcode = *instr++; - pc++; - #endif - - static byte const base_timing [0x100] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 - 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 - 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 - 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 - 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B - 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C - 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D - 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E - 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F - }; - - fuint16 data; - data = base_timing [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = READ_PROG( pc ); - - #ifdef Z80_CPU_LOG_H - //log_opcode( opcode, READ_PROG( pc ) ); - z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); - z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), - READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Common - - case 0x00: // NOP - CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. - goto loop; - - case 0x08:{// EX AF,AF' - int temp = r.alt.b.a; - r.alt.b.a = rg.a; - rg.a = temp; - - temp = r.alt.b.flags; - r.alt.b.flags = flags; - flags = temp; - goto loop; - } - - case 0xD3: // OUT (imm),A - pc++; - OUT( data + rg.a * 0x100, rg.a ); - goto loop; - - case 0x2E: // LD L,imm - pc++; - rg.l = data; - goto loop; - - case 0x3E: // LD A,imm - pc++; - rg.a = data; - goto loop; - - case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rg.a = READ( addr ); - goto loop; - } - -// Conditional - -#define ZERO (flags & Z40) -#define CARRY (flags & C01) -#define EVEN (flags & P04) -#define MINUS (flags & S80) - -// JR -// TODO: more efficient way to handle negative branch that wraps PC around -#define JR( cond ) {\ - int offset = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) )\ - goto jr_not_taken;\ - pc = uint16_t (pc + offset);\ - goto loop;\ -} - - case 0x20: JR( !ZERO ) // JR NZ,disp - case 0x28: JR( ZERO ) // JR Z,disp - case 0x30: JR( !CARRY ) // JR NC,disp - case 0x38: JR( CARRY ) // JR C,disp - case 0x18: JR( true ) // JR disp - - case 0x10:{// DJNZ disp - int temp = rg.b - 1; - rg.b = temp; - JR( temp ) - } - -// JP -#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; - - case 0xC2: JP( !ZERO ) // JP NZ,addr - case 0xCA: JP( ZERO ) // JP Z,addr - case 0xD2: JP( !CARRY ) // JP NC,addr - case 0xDA: JP( CARRY ) // JP C,addr - case 0xE2: JP( !EVEN ) // JP PO,addr - case 0xEA: JP( EVEN ) // JP PE,addr - case 0xF2: JP( !MINUS ) // JP P,addr - case 0xFA: JP( MINUS ) // JP M,addr - - case 0xC3: // JP addr - pc = GET_ADDR(); - goto loop; - - case 0xE9: // JP HL - pc = rp.hl; - goto loop; - -// RET -#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; - - case 0xC0: RET( !ZERO ) // RET NZ - case 0xC8: RET( ZERO ) // RET Z - case 0xD0: RET( !CARRY ) // RET NC - case 0xD8: RET( CARRY ) // RET C - case 0xE0: RET( !EVEN ) // RET PO - case 0xE8: RET( EVEN ) // RET PE - case 0xF0: RET( !MINUS ) // RET P - case 0xF8: RET( MINUS ) // RET M - - case 0xC9: // RET - ret_taken: - pc = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// CALL -#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; - - case 0xC4: CALL( !ZERO ) // CALL NZ,addr - case 0xCC: CALL( ZERO ) // CALL Z,addr - case 0xD4: CALL( !CARRY ) // CALL NC,addr - case 0xDC: CALL( CARRY ) // CALL C,addr - case 0xE4: CALL( !EVEN ) // CALL PO,addr - case 0xEC: CALL( EVEN ) // CALL PE,addr - case 0xF4: CALL( !MINUS ) // CALL P,addr - case 0xFC: CALL( MINUS ) // CALL M,addr - - case 0xCD:{// CALL addr - call_taken: - fuint16 addr = pc + 2; - pc = GET_ADDR(); - sp = uint16_t (sp - 2); - WRITE_WORD( sp, addr ); - goto loop; - } - - case 0xFF: // RST - if ( pc > idle_addr ) - goto hit_idle_addr; - CASE7( C7, CF, D7, DF, E7, EF, F7 ): - data = pc; - pc = opcode & 0x38; - goto push_data; - -// PUSH/POP - case 0xF5: // PUSH AF - data = rg.a * 0x100u + flags; - goto push_data; - - case 0xC5: // PUSH BC - case 0xD5: // PUSH DE - case 0xE5: // PUSH HL - data = R16( opcode, 4, 0xC5 ); - push_data: - sp = uint16_t (sp - 2); - WRITE_WORD( sp, data ); - goto loop; - - case 0xF1: // POP AF - flags = READ( sp ); - rg.a = READ( sp + 1 ); - sp = uint16_t (sp + 2); - goto loop; - - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL - R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// ADC/ADD/SBC/SUB - case 0x96: // SUB (HL) - case 0x86: // ADD (HL) - flags &= ~C01; - case 0x9E: // SBC (HL) - case 0x8E: // ADC (HL) - data = READ( rp.hl ); - goto adc_data; - - case 0xD6: // SUB A,imm - case 0xC6: // ADD imm - flags &= ~C01; - case 0xDE: // SBC A,imm - case 0xCE: // ADC imm - pc++; - goto adc_data; - - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r - flags &= ~C01; - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r - data = R8( opcode & 7, 0 ); - adc_data: { - int result = data + (flags & C01); - data ^= rg.a; - flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes - if ( flags ) - result = -result; - result += rg.a; - data ^= result; - flags |=(data & H10) | - ((data - -0x80) >> 6 & V04) | - SZ28C( result & 0x1FF ); - rg.a = result; - goto loop; - } - -// CP - case 0xBE: // CP (HL) - data = READ( rp.hl ); - goto cp_data; - - case 0xFE: // CP imm - pc++; - goto cp_data; - - CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r - data = R8( opcode, 0xB8 ); - cp_data: { - int result = rg.a - data; - flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); - data ^= rg.a; - flags |=(((result ^ rg.a) & data) >> 5 & V04) | - (((data & H10) ^ result) & (S80 | H10)); - if ( (uint8_t) result ) - goto loop; - flags |= Z40; - goto loop; - } - -// ADD HL,rp - - case 0x39: // ADD HL,SP - data = sp; - goto add_hl_data; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - data = R16( opcode, 4, 0x09 ); - add_hl_data: { - blargg_ulong sum = rp.hl + data; - data ^= rp.hl; - rp.hl = sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((data ^ sum) >> 8 & H10); - goto loop; - } - - case 0x27:{// DAA - int a = rg.a; - if ( a > 0x99 ) - flags |= C01; - - int adjust = 0x60 & -(flags & C01); - - if ( flags & H10 || (a & 0x0F) > 9 ) - adjust |= 0x06; - - if ( flags & N02 ) - adjust = -adjust; - a += adjust; - - flags = (flags & (C01 | N02)) | - ((rg.a ^ a) & H10) | - SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - /* - case 0x27:{// DAA - // more optimized, but probably not worth the obscurity - int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags - int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 - - if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 - adjust |= 0x06; - - if ( f & N02 ) - adjust = -adjust; - int a = rg.a + adjust; - - flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - */ - -// INC/DEC - case 0x34: // INC (HL) - data = READ( rp.hl ) + 1; - WRITE( rp.hl, data ); - goto inc_set_flags; - - CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r - data = ++R8( opcode >> 3, 0 ); - inc_set_flags: - flags = (flags & C01) | - (((data & 0x0F) - 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x80 ) - goto loop; - flags |= V04; - goto loop; - - case 0x35: // DEC (HL) - data = READ( rp.hl ) - 1; - WRITE( rp.hl, data ); - goto dec_set_flags; - - CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r - data = --R8( opcode >> 3, 0 ); - dec_set_flags: - flags = (flags & C01) | N02 | - (((data & 0x0F) + 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x7F ) - goto loop; - flags |= V04; - goto loop; - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - R16( opcode, 4, 0x03 )++; - goto loop; - - case 0x33: // INC SP - sp = uint16_t (sp + 1); - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - R16( opcode, 4, 0x0B )--; - goto loop; - - case 0x3B: // DEC SP - sp = uint16_t (sp - 1); - goto loop; - -// AND - case 0xA6: // AND (HL) - data = READ( rp.hl ); - goto and_data; - - case 0xE6: // AND imm - pc++; - goto and_data; - - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r - data = R8( opcode, 0xA0 ); - and_data: - rg.a &= data; - flags = SZ28P( rg.a ) | H10; - goto loop; - -// OR - case 0xB6: // OR (HL) - data = READ( rp.hl ); - goto or_data; - - case 0xF6: // OR imm - pc++; - goto or_data; - - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r - data = R8( opcode, 0xB0 ); - or_data: - rg.a |= data; - flags = SZ28P( rg.a ); - goto loop; - -// XOR - case 0xAE: // XOR (HL) - data = READ( rp.hl ); - goto xor_data; - - case 0xEE: // XOR imm - pc++; - goto xor_data; - - CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r - data = R8( opcode, 0xA8 ); - xor_data: - rg.a ^= data; - flags = SZ28P( rg.a ); - goto loop; - -// LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r - WRITE( rp.hl, R8( opcode, 0x70 ) ); - goto loop; - - CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r - CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r - CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r - CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r - CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r - CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r - CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r - R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); - goto loop; - - CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm - R8( opcode >> 3, 0 ) = data; - pc++; - goto loop; - - case 0x36: // LD (HL),imm - pc++; - WRITE( rp.hl, data ); - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) - R8( opcode >> 3, 8 ) = READ( rp.hl ); - goto loop; - - case 0x01: // LD rp,imm - case 0x11: - case 0x21: - R16( opcode, 4, 0x01 ) = GET_ADDR(); - pc += 2; - goto loop; - - case 0x31: // LD sp,imm - sp = GET_ADDR(); - pc += 2; - goto loop; - - case 0x2A:{// LD HL,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rp.hl = READ_WORD( addr ); - goto loop; - } - - case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE( addr, rg.a ); - goto loop; - } - - case 0x22:{// LD (addr),HL - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, rp.hl ); - goto loop; - } - - case 0x02: // LD (BC),A - case 0x12: // LD (DE),A - WRITE( R16( opcode, 4, 0x02 ), rg.a ); - goto loop; - - case 0x0A: // LD A,(BC) - case 0x1A: // LD A,(DE) - rg.a = READ( R16( opcode, 4, 0x0A ) ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - -// Rotate - - case 0x07:{// RLCA - fuint16 temp = rg.a; - temp = (temp << 1) | (temp >> 7); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08 | C01)); - rg.a = temp; - goto loop; - } - - case 0x0F:{// RRCA - fuint16 temp = rg.a; - flags = (flags & (S80 | Z40 | P04)) | - (temp & C01); - temp = (temp << 7) | (temp >> 1); - flags |= temp & (F20 | F08); - rg.a = temp; - goto loop; - } - - case 0x17:{// RLA - blargg_ulong temp = (rg.a << 1) | (flags & C01); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (temp >> 8); - rg.a = temp; - goto loop; - } - - case 0x1F:{// RRA - fuint16 temp = (flags << 7) | (rg.a >> 1); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (rg.a & C01); - rg.a = temp; - goto loop; - } - -// Misc - case 0x2F:{// CPL - fuint16 temp = ~rg.a; - flags = (flags & (S80 | Z40 | P04 | C01)) | - (temp & (F20 | F08)) | - (H10 | N02); - rg.a = temp; - goto loop; - } - - case 0x3F:{// CCF - flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | - (flags << 4 & H10) | - (rg.a & (F20 | F08)); - goto loop; - } - - case 0x37: // SCF - flags = (flags & (S80 | Z40 | P04)) | C01 | - (rg.a & (F20 | F08)); - goto loop; - - case 0xDB: // IN A,(imm) - pc++; - rg.a = IN( data + rg.a * 0x100 ); - goto loop; - - case 0xE3:{// EX (SP),HL - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, rp.hl ); - rp.hl = temp; - goto loop; - } - - case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; - rp.hl = rp.de; - rp.de = temp; - goto loop; - } - - case 0xD9:{// EXX DE,HL - fuint16 temp = r.alt.w.bc; - r.alt.w.bc = rp.bc; - rp.bc = temp; - - temp = r.alt.w.de; - r.alt.w.de = rp.de; - rp.de = temp; - - temp = r.alt.w.hl; - r.alt.w.hl = rp.hl; - rp.hl = temp; - goto loop; - } - - case 0xF3: // DI - r.iff1 = 0; - r.iff2 = 0; - goto loop; - - case 0xFB: // EI - r.iff1 = 1; - r.iff2 = 1; - // TODO: delayed effect - goto loop; - - case 0x76: // HALT - goto halt; - -//////////////////////////////////////// CB prefix - { - case 0xCB: - unsigned data2; - data2 = instr [1]; - pc++; - switch ( data ) - { - - // Rotate left - - #define RLC( read, write ) {\ - fuint8 result = read;\ - result = uint8_t (result << 1) | (result >> 7);\ - flags = SZ28P( result ) | (result & C01);\ - write;\ - goto loop;\ - } - - case 0x06: // RLC (HL) - s_time += 7; - data = rp.hl; - rlc_data_addr: - RLC( READ( data ), WRITE( data, result ) ) - - CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r - uint8_t& reg = R8( data, 0 ); - RLC( reg, reg = result ) - } - - #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x16: // RL (HL) - s_time += 7; - data = rp.hl; - rl_data_addr: - RL( READ( data ), WRITE( data, result ) ) - - CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r - uint8_t& reg = R8( data, 0x10 ); - RL( reg, reg = result ) - } - - #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x26: // SLA (HL) - s_time += 7; - data = rp.hl; - sla_data_addr: - SLA( READ( data ), 0, WRITE( data, result ) ) - - CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r - uint8_t& reg = R8( data, 0x20 ); - SLA( reg, 0, reg = result ) - } - - case 0x36: // SLL (HL) - s_time += 7; - data = rp.hl; - sll_data_addr: - SLA( READ( data ), 1, WRITE( data, result ) ) - - CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r - uint8_t& reg = R8( data, 0x30 ); - SLA( reg, 1, reg = result ) - } - - // Rotate right - - #define RRC( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = uint8_t (result << 7) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x0E: // RRC (HL) - s_time += 7; - data = rp.hl; - rrc_data_addr: - RRC( READ( data ), WRITE( data, result ) ) - - CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r - uint8_t& reg = R8( data, 0x08 ); - RRC( reg, reg = result ) - } - - #define RR( read, write ) {\ - fuint8 result = read;\ - fuint8 temp = result & C01;\ - result = uint8_t (flags << 7) | (result >> 1);\ - flags = SZ28P( result ) | temp;\ - write;\ - goto loop;\ - } - - case 0x1E: // RR (HL) - s_time += 7; - data = rp.hl; - rr_data_addr: - RR( READ( data ), WRITE( data, result ) ) - - CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r - uint8_t& reg = R8( data, 0x18 ); - RR( reg, reg = result ) - } - - #define SRA( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = (result & 0x80) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x2E: // SRA (HL) - data = rp.hl; - s_time += 7; - sra_data_addr: - SRA( READ( data ), WRITE( data, result ) ) - - CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r - uint8_t& reg = R8( data, 0x28 ); - SRA( reg, reg = result ) - } - - #define SRL( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result >>= 1;\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x3E: // SRL (HL) - s_time += 7; - data = rp.hl; - srl_data_addr: - SRL( READ( data ), WRITE( data, result ) ) - - CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r - uint8_t& reg = R8( data, 0x38 ); - SRL( reg, reg = result ) - } - - // BIT - { - unsigned temp; - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) - s_time += 4; - temp = READ( rp.hl ); - flags &= C01; - goto bit_temp; - CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r - CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r - CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r - CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r - CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r - CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r - CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r - temp = R8( data & 7, 0 ); - flags = (flags & C01) | (temp & (F20 | F08)); - bit_temp: - int masked = temp & 1 << (data >> 3 & 7); - flags |=(masked & S80) | H10 | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - // SET/RES - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) - s_time += 7; - int temp = READ( rp.hl ); - int bit = 1 << (data >> 3 & 7); - temp |= bit; // SET - if ( !(data & 0x40) ) - temp ^= bit; // RES - WRITE( rp.hl, temp ); - goto loop; - } - - CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r - CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r - CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r - CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r - CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r - CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r - CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r - CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r - R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); - goto loop; - - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r - CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r - CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r - R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); - goto loop; - } - assert( false ); - } - -#undef GET_ADDR -#define GET_ADDR() GET_LE16( instr + 1 ) - -//////////////////////////////////////// ED prefix - { - case 0xED: - pc++; - s_time += ed_dd_timing [data] >> 4; - switch ( data ) - { - { - blargg_ulong temp; - case 0x72: // SBC HL,SP - case 0x7A: // ADC HL,SP - temp = sp; - if ( 0 ) - case 0x42: // SBC HL,BC - case 0x52: // SBC HL,DE - case 0x62: // SBC HL,HL - case 0x4A: // ADC HL,BC - case 0x5A: // ADC HL,DE - case 0x6A: // ADC HL,HL - temp = R16( data >> 3 & 6, 1, 0 ); - blargg_ulong sum = temp + (flags & C01); - flags = ~data >> 2 & N02; - if ( flags ) - sum = -sum; - sum += rp.hl; - temp ^= rp.hl; - temp ^= sum; - flags |=(sum >> 16 & C01) | - (temp >> 8 & H10) | - (sum >> 8 & (S80 | F20 | F08)) | - ((temp - -0x8000) >> 14 & V04); - rp.hl = sum; - if ( (uint16_t) sum ) - goto loop; - flags |= Z40; - goto loop; - } - - CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) - int temp = IN( rp.bc ); - R8( data >> 3, 8 ) = temp; - flags = (flags & C01) | SZ28P( temp ); - goto loop; - } - - case 0x71: // OUT (C),0 - rg.flags = 0; - CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r - OUT( rp.bc, R8( data >> 3, 8 ) ); - goto loop; - - { - unsigned temp; - case 0x73: // LD (ADDR),SP - temp = sp; - if ( 0 ) - case 0x43: // LD (ADDR),BC - case 0x53: // LD (ADDR),DE - temp = R16( data, 4, 0x43 ); - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, temp ); - goto loop; - } - - case 0x4B: // LD BC,(ADDR) - case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - R16( data, 4, 0x4B ) = READ_WORD( addr ); - goto loop; - } - - case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - sp = READ_WORD( addr ); - goto loop; - } - - case 0x67:{// RRD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); - temp = (rg.a & 0xF0) | (temp & 0x0F); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - case 0x6F:{// RLD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); - temp = (rg.a & 0xF0) | (temp >> 4); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG - opcode = 0x10; // flag to do SBC instead of ADC - flags &= ~C01; - data = rg.a; - rg.a = 0; - goto adc_data; - - { - int inc; - case 0xA9: // CPD - case 0xB9: // CPDR - inc = -1; - if ( 0 ) - case 0xA1: // CPI - case 0xB1: // CPIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int result = rg.a - temp; - flags = (flags & C01) | N02 | - ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); - - if ( !(uint8_t) result ) flags |= Z40; - result -= (flags & H10) >> 4; - flags |= result & F08; - flags |= result << 4 & F20; - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( flags & Z40 || data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xA8: // LDD - case 0xB8: // LDDR - inc = -1; - if ( 0 ) - case 0xA0: // LDI - case 0xB0: // LDIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - addr = rp.de; - rp.de = addr + inc; - WRITE( addr, temp ); - - temp += rg.a; - flags = (flags & (S80 | Z40 | C01)) | - (temp & F08) | (temp << 4 & F20); - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xAB: // OUTD - case 0xBB: // OTDR - inc = -1; - if ( 0 ) - case 0xA3: // OUTI - case 0xB3: // OTIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - OUT( rp.bc, temp ); - goto loop; - } - - { - int inc; - case 0xAA: // IND - case 0xBA: // INDR - inc = -1; - if ( 0 ) - case 0xA2: // INI - case 0xB2: // INIR - inc = +1; - - fuint16 addr = rp.hl; - rp.hl = addr + inc; - - int temp = IN( rp.bc ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - WRITE( addr, temp ); - goto loop; - } - - case 0x47: // LD I,A - r.i = rg.a; - goto loop; - - case 0x4F: // LD R,A - SET_R( rg.a ); - dprintf( "LD R,A not supported\n" ); - warning = true; - goto loop; - - case 0x57: // LD A,I - rg.a = r.i; - goto ld_ai_common; - - case 0x5F: // LD A,R - rg.a = GET_R(); - dprintf( "LD A,R not supported\n" ); - warning = true; - ld_ai_common: - flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); - goto loop; - - CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN - r.iff1 = r.iff2; - goto ret_taken; - - case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 - r.im = 0; - goto loop; - - case 0x56: case 0x76: // IM 1 - r.im = 1; - goto loop; - - case 0x5E: case 0x7E: // IM 2 - r.im = 2; - goto loop; - - default: - dprintf( "Opcode $ED $%02X not supported\n", data ); - warning = true; - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// DD/FD prefix - { - fuint16 ixy; - case 0xDD: - ixy = ix; - goto ix_prefix; - case 0xFD: - ixy = iy; - ix_prefix: - pc++; - unsigned data2 = READ_PROG( pc ); - s_time += ed_dd_timing [data] & 0x0F; - switch ( data ) - { - // TODO: more efficient way of avoid negative address - // TODO: avoid using this as argument to READ() since it is evaluated twice - #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) - - #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; - - // ADD/ADC/SUB/SBC - - case 0x96: // SUB (IXY+disp) - case 0x86: // ADD (IXY+disp) - flags &= ~C01; - case 0x9E: // SBC (IXY+disp) - case 0x8E: // ADC (IXY+disp) - pc++; - opcode = data; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto adc_data; - - case 0x94: // SUB HXY - case 0x84: // ADD HXY - flags &= ~C01; - case 0x9C: // SBC HXY - case 0x8C: // ADC HXY - opcode = data; - data = ixy >> 8; - goto adc_data; - - case 0x95: // SUB LXY - case 0x85: // ADD LXY - flags &= ~C01; - case 0x9D: // SBC LXY - case 0x8D: // ADC LXY - opcode = data; - data = (uint8_t) ixy; - goto adc_data; - - { - unsigned temp; - case 0x39: // ADD IXY,SP - temp = sp; - goto add_ixy_data; - - case 0x29: // ADD IXY,HL - temp = ixy; - goto add_ixy_data; - - case 0x09: // ADD IXY,BC - case 0x19: // ADD IXY,DE - temp = R16( data, 4, 0x09 ); - add_ixy_data: { - blargg_ulong sum = ixy + temp; - temp ^= ixy; - ixy = (uint16_t) sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((temp ^ sum) >> 8 & H10); - goto set_ixy; - } - } - - // AND - case 0xA6: // AND (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto and_data; - - case 0xA4: // AND HXY - data = ixy >> 8; - goto and_data; - - case 0xA5: // AND LXY - data = (uint8_t) ixy; - goto and_data; - - // OR - case 0xB6: // OR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto or_data; - - case 0xB4: // OR HXY - data = ixy >> 8; - goto or_data; - - case 0xB5: // OR LXY - data = (uint8_t) ixy; - goto or_data; - - // XOR - case 0xAE: // XOR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto xor_data; - - case 0xAC: // XOR HXY - data = ixy >> 8; - goto xor_data; - - case 0xAD: // XOR LXY - data = (uint8_t) ixy; - goto xor_data; - - // CP - case 0xBE: // CP (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto cp_data; - - case 0xBC: // CP HXY - data = ixy >> 8; - goto cp_data; - - case 0xBD: // CP LXY - data = (uint8_t) ixy; - goto cp_data; - - // LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r - data = R8( data, 0x70 ); - if ( 0 ) - case 0x36: // LD (IXY+disp),imm - pc++, data = READ_PROG( pc ); - pc++; - WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); - goto loop; - - CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY - R8( data >> 3, 8 ) = ixy >> 8; - goto loop; - - case 0x64: // LD HXY,HXY - case 0x6D: // LD LXY,LXY - goto loop; - - CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY - R8( data >> 3, 8 ) = ixy; - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) - pc++; - R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto loop; - - case 0x26: // LD HXY,imm - pc++; - goto ld_hxy_data; - - case 0x65: // LD HXY,LXY - data2 = (uint8_t) ixy; - goto ld_hxy_data; - - CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r - data2 = R8( data, 0x60 ); - ld_hxy_data: - ixy = (uint8_t) ixy | (data2 << 8); - goto set_ixy; - - case 0x2E: // LD LXY,imm - pc++; - goto ld_lxy_data; - - case 0x6C: // LD LXY,HXY - data2 = ixy >> 8; - goto ld_lxy_data; - - CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r - data2 = R8( data, 0x68 ); - ld_lxy_data: - ixy = (ixy & 0xFF00) | data2; - set_ixy: - if ( opcode == 0xDD ) - { - ix = ixy; - goto loop; - } - iy = ixy; - goto loop; - - case 0xF9: // LD SP,IXY - sp = ixy; - goto loop; - - case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, ixy ); - goto loop; - } - - case 0x21: // LD IXY,imm - ixy = GET_ADDR(); - pc += 2; - goto set_ixy; - - case 0x2A:{// LD IXY,(addr) - fuint16 addr = GET_ADDR(); - ixy = READ_WORD( addr ); - pc += 2; - goto set_ixy; - } - - // DD/FD CB prefix - case 0xCB: { - data = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data2 = READ_PROG( pc ); - pc++; - switch ( data2 ) - { - case 0x06: goto rlc_data_addr; // RLC (IXY) - case 0x16: goto rl_data_addr; // RL (IXY) - case 0x26: goto sla_data_addr; // SLA (IXY) - case 0x36: goto sll_data_addr; // SLL (IXY) - case 0x0E: goto rrc_data_addr; // RRC (IXY) - case 0x1E: goto rr_data_addr; // RR (IXY) - case 0x2E: goto sra_data_addr; // SRA (IXY) - case 0x3E: goto srl_data_addr; // SRL (IXY) - - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) - fuint8 temp = READ( data ); - int masked = temp & 1 << (data2 >> 3 & 7); - flags = (flags & C01) | H10 | - (masked & S80) | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) - int temp = READ( data ); - int bit = 1 << (data2 >> 3 & 7); - temp |= bit; // SET - if ( !(data2 & 0x40) ) - temp ^= bit; // RES - WRITE( data, temp ); - goto loop; - } - - default: - dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); - warning = true; - goto loop; - } - assert( false ); - } - - // INC/DEC - case 0x23: // INC IXY - ixy = uint16_t (ixy + 1); - goto set_ixy; - - case 0x2B: // DEC IXY - ixy = uint16_t (ixy - 1); - goto set_ixy; - - case 0x34: // INC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) + 1; - WRITE( ixy, data ); - goto inc_set_flags; - - case 0x35: // DEC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) - 1; - WRITE( ixy, data ); - goto dec_set_flags; - - case 0x24: // INC HXY - ixy = uint16_t (ixy + 0x100); - data = ixy >> 8; - goto inc_xy_common; - - case 0x2C: // INC LXY - data = uint8_t (ixy + 1); - ixy = (ixy & 0xFF00) | data; - inc_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto inc_set_flags; - } - iy = ixy; - goto inc_set_flags; - - case 0x25: // DEC HXY - ixy = uint16_t (ixy - 0x100); - data = ixy >> 8; - goto dec_xy_common; - - case 0x2D: // DEC LXY - data = uint8_t (ixy - 1); - ixy = (ixy & 0xFF00) | data; - dec_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto dec_set_flags; - } - iy = ixy; - goto dec_set_flags; - - // PUSH/POP - case 0xE5: // PUSH IXY - data = ixy; - goto push_data; - - case 0xE1:{// POP IXY - ixy = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto set_ixy; - } - - // Misc - - case 0xE9: // JP (IXY) - pc = ixy; - goto loop; - - case 0xE3:{// EX (SP),IXY - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, ixy ); - ixy = temp; - goto set_ixy; - } - - default: - dprintf( "Unnecessary DD/FD prefix encountered\n" ); - warning = true; - pc--; - goto loop; - } - assert( false ); - } - - } - dprintf( "Unhandled main opcode: $%02X\n", opcode ); - assert( false ); - -hit_idle_addr: - s_time -= 11; - goto out_of_time; -halt: - s_time &= 3; // increment by multiple of 4 -out_of_time: - pc--; - - s.time = s_time; - rg.flags = flags; - r.ix = ix; - r.iy = iy; - r.sp = sp; - r.pc = pc; - this->r.b = rg; - this->state_ = s; - this->state = &this->state_; - - return warning; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h deleted file mode 100644 index 6297d100..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h +++ /dev/null @@ -1,124 +0,0 @@ -// Z80 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef KSS_CPU_H -#define KSS_CPU_H - -#include "blargg_endian.h" - -typedef blargg_long cpu_time_t; - -// must be defined by caller -void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); -int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); -void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); - -class Kss_Cpu { -public: - typedef BOOST::uint8_t uint8_t; - - // Clear registers and map all pages to unmapped - void reset( void* unmapped_write, void const* unmapped_read ); - - // Map memory. Start and size must be multiple of page_size. - enum { page_size = 0x2000 }; - void map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ); - - // Map address to page - uint8_t* write( unsigned addr ); - uint8_t const* read( unsigned addr ); - - // Run until specified time is reached. Returns true if suspicious/unsupported - // instruction was encountered at any point during run. - bool run( cpu_time_t end_time ); - - // Time of beginning of next instruction - cpu_time_t time() const { return state->time + state->base; } - - // Alter current time. Not supported during run() call. - void set_time( cpu_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } - - typedef BOOST::uint16_t uint16_t; - - #if BLARGG_BIG_ENDIAN - struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; - #else - struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; - #endif - BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 ); - - struct pairs_t { uint16_t bc, de, hl, fa; }; - - // Registers are not updated until run() returns - struct registers_t { - uint16_t pc; - uint16_t sp; - uint16_t ix; - uint16_t iy; - union { - regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a - pairs_t w; // w.bc, w.de, w.hl. w.fa - }; - union { - regs_t b; - pairs_t w; - } alt; - uint8_t iff1; - uint8_t iff2; - uint8_t r; - uint8_t i; - uint8_t im; - }; - //registers_t r; (below for efficiency) - - enum { idle_addr = 0xFFFF }; - - // can read this far past end of a page - enum { cpu_padding = 0x100 }; - -public: - Kss_Cpu(); - enum { page_shift = 13 }; - enum { page_count = 0x10000 >> page_shift }; -private: - uint8_t szpc [0x200]; - cpu_time_t end_time_; - struct state_t { - uint8_t const* read [page_count + 1]; - uint8_t * write [page_count + 1]; - cpu_time_t base; - cpu_time_t time; - }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - void set_end_time( cpu_time_t t ); - void set_page( int i, void* write, void const* read ); -public: - registers_t r; -}; - -#if BLARGG_NONPORTABLE - #define KSS_CPU_PAGE_OFFSET( addr ) (addr) -#else - #define KSS_CPU_PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -inline BOOST::uint8_t* Kss_Cpu::write( unsigned addr ) -{ - return state->write [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); -} - -inline BOOST::uint8_t const* Kss_Cpu::read( unsigned addr ) -{ - return state->read [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); -} - -inline void Kss_Cpu::set_end_time( cpu_time_t t ) -{ - cpu_time_t delta = state->base - t; - state->base = t; - state->time += delta; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp deleted file mode 100644 index 1b26ef0b..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp +++ /dev/null @@ -1,414 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Kss_Emu.h" - -#include "blargg_endian.h" -#include - -/* 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" - -long const clock_rate = 3579545; -int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; - -Kss_Emu::Kss_Emu() -{ - sn = 0; - set_type( gme_kss_type ); - set_silence_lookahead( 6 ); - 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 | 0, wave_type | 1, wave_type | 2, - wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 - }; - set_voice_types( types ); - - memset( unmapped_read, 0xFF, sizeof unmapped_read ); -} - -Kss_Emu::~Kss_Emu() { unload(); } - -void Kss_Emu::unload() -{ - delete sn; - sn = 0; - Classic_Emu::unload(); -} - -// Track info - -static void copy_kss_fields( Kss_Emu::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"; - } - Gme_File::copy_field_( out->system, system ); -} - -blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const -{ - copy_kss_fields( header_, out ); - return 0; -} - -static blargg_err_t check_kss_header( void const* header ) -{ - if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Kss_File : Gme_Info_ -{ - Kss_Emu::header_t header_; - - Kss_File() { set_type( gme_kss_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - return check_kss_header( &header_ ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_kss_fields( header_, out ); - return 0; - } -}; - -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::update_gain() -{ - double g = gain() * 1.4; - if ( scc_accessed ) - g *= 1.5; - ay.volume( g ); - scc.volume( g ); - if ( sn ) - sn->volume( g ); -} - -blargg_err_t Kss_Emu::load_( Data_Reader& in ) -{ - memset( &header_, 0, sizeof header_ ); - assert( offsetof (header_t,device_flags) == header_size - 1 ); - assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); - RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) ); - - RETURN_ERR( check_kss_header( header_.tag ) ); - - 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 - { - ext_header_t& ext = header_; - memcpy( &ext, rom.begin(), min( (int) ext_header_size, (int) header_.extra_header ) ); - if ( header_.extra_header > 0x10 ) - set_warning( "Unknown data in header" ); - } - - if ( header_.device_flags & 0x09 ) - set_warning( "FM sound not supported" ); - - scc_enabled = 0xC000; - if ( header_.device_flags & 0x04 ) - scc_enabled = 0; - - if ( header_.device_flags & 0x02 && !sn ) - CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) ); - - set_voice_count( osc_count ); - - return setup_buffer( ::clock_rate ); -} - -void Kss_Emu::update_eq( blip_eq_t const& eq ) -{ - ay.treble_eq( eq ); - scc.treble_eq( eq ); - if ( sn ) - sn->treble_eq( eq ); -} - -void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - int i2 = i - ay.osc_count; - if ( i2 >= 0 ) - scc.osc_output( i2, center ); - else - ay.osc_output( i, center ); - if ( sn && i < sn->osc_count ) - sn->osc_output( i, center, left, right ); -} - -// Emulation - -void Kss_Emu::set_tempo_( double t ) -{ - blip_time_t period = - (header_.device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); - play_period = blip_time_t (period / t); -} - -blargg_err_t Kss_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( 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 - unsigned load_addr = get_le16( header_.load_addr ); - long orig_load_size = get_le16( header_.load_size ); - long load_size = min( orig_load_size, rom.file_size() ); - load_size = min( load_size, long (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 - blargg_long 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 ); - - ay.reset(); - scc.reset(); - if ( sn ) - sn->reset(); - r.sp = 0xF380; - ram [--r.sp] = idle_addr >> 8; - ram [--r.sp] = idle_addr & 0xFF; - r.b.a = track; - r.pc = get_le16( header_.init_addr ); - next_play = play_period; - scc_accessed = false; - gain_updated = false; - update_gain(); - ay_latch = 0; - - return 0; -} - -void Kss_Emu::set_bank( int logical, int physical ) -{ - unsigned const bank_size = this->bank_size(); - - unsigned 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 - { - long phys = physical * (blargg_long) bank_size; - for ( unsigned offset = 0; offset < bank_size; offset += page_size ) - cpu::map_mem( addr + offset, page_size, - unmapped_write, rom.at_addr( phys + offset ) ); - } -} - -void Kss_Emu::cpu_write( unsigned addr, int data ) -{ - data &= 0xFF; - switch ( addr ) - { - case 0x9000: - set_bank( 0, data ); - return; - - case 0xB000: - set_bank( 1, data ); - return; - } - - int scc_addr = (addr & 0xDFFF) ^ 0x9800; - if ( scc_addr < scc.reg_count ) - { - scc_accessed = true; - scc.write( time(), scc_addr, data ); - return; - } - - dprintf( "LD ($%04X),$%02X\n", addr, data ); -} - -void kss_cpu_write( Kss_Cpu* cpu, unsigned addr, int data ) -{ - *cpu->write( addr ) = data; - if ( (addr & STATIC_CAST(Kss_Emu&,*cpu).scc_enabled) == 0x8000 ) - STATIC_CAST(Kss_Emu&,*cpu).cpu_write( addr, data ); -} - -void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) -{ - data &= 0xFF; - Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); - switch ( addr & 0xFF ) - { - case 0xA0: - emu.ay_latch = data & 0x0F; - return; - - case 0xA1: - GME_APU_HOOK( &emu, emu.ay_latch, data ); - emu.ay.write( time, emu.ay_latch, data ); - return; - - case 0x06: - if ( emu.sn && (emu.header_.device_flags & 0x04) ) - { - emu.sn->write_ggstereo( time, data ); - return; - } - break; - - case 0x7E: - case 0x7F: - if ( emu.sn ) - { - GME_APU_HOOK( &emu, 16, data ); - emu.sn->write_data( time, data ); - return; - } - break; - - case 0xFE: - emu.set_bank( 0, data ); - return; - - #ifndef NDEBUG - case 0xF1: // FM data - if ( data ) - break; // trap non-zero data - case 0xF0: // FM addr - case 0xA8: // PPI - return; - #endif - } - - dprintf( "OUT $%04X,$%02X\n", addr, data ); -} - -int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr ) -{ - //Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); - //switch ( addr & 0xFF ) - //{ - //} - - dprintf( "IN $%04X\n", addr ); - return 0; -} - -// Emulation - -blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) -{ - while ( time() < duration ) - { - blip_time_t end = min( duration, next_play ); - cpu::run( min( duration, next_play ) ); - if ( r.pc == idle_addr ) - set_time( end ); - - if ( time() >= next_play ) - { - next_play += play_period; - if ( r.pc == idle_addr ) - { - if ( !gain_updated ) - { - gain_updated = true; - if ( scc_accessed ) - update_gain(); - } - - ram [--r.sp] = idle_addr >> 8; - ram [--r.sp] = idle_addr & 0xFF; - r.pc = get_le16( header_.play_addr ); - GME_FRAME_HOOK( this ); - } - } - } - - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - adjust_time( -duration ); - ay.end_frame( duration ); - scc.end_frame( duration ); - if ( sn ) - sn->end_frame( duration ); - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h deleted file mode 100644 index 4d8463ab..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h +++ /dev/null @@ -1,96 +0,0 @@ -// MSX computer KSS music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef KSS_EMU_H -#define KSS_EMU_H - -#include "Classic_Emu.h" -#include "Kss_Scc_Apu.h" -#include "Kss_Cpu.h" -#include "Sms_Apu.h" -#include "Ay_Apu.h" - -class Kss_Emu : private Kss_Cpu, public Classic_Emu { - typedef Kss_Cpu cpu; -public: - // KSS file header - enum { header_size = 0x10 }; - struct header_t - { - 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; - }; - - enum { ext_header_size = 0x10 }; - struct ext_header_t - { - byte data_size [4]; - byte unused [4]; - byte first_track [2]; - byte last_tack [2]; - byte psg_vol; - byte scc_vol; - byte msx_music_vol; - byte msx_audio_vol; - }; - - struct composite_header_t : header_t, ext_header_t { }; - - // Header for currently loaded file - composite_header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_kss_type; } -public: - Kss_Emu(); - ~Kss_Emu(); -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); -private: - Rom_Data rom; - composite_header_t header_; - - bool scc_accessed; - bool gain_updated; - void update_gain(); - - unsigned scc_enabled; // 0 or 0xC000 - byte const* bank_data; - int bank_count; - void set_bank( int logical, int physical ); - blargg_long bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); } - - blip_time_t play_period; - blip_time_t next_play; - int ay_latch; - - friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); - friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); - void cpu_write( unsigned addr, int data ); - friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); - - // large items - enum { mem_size = 0x10000 }; - byte ram [mem_size + cpu_padding]; - - Ay_Apu ay; - Scc_Apu scc; - Sms_Apu* sn; - byte unmapped_read [0x100]; - byte unmapped_write [page_size]; -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp deleted file mode 100644 index 1660ac3d..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Kss_Scc_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" - -// Tones above this frequency are treated as disabled tone at half volume. -// Power of two is more efficient (avoids division). -unsigned const inaudible_freq = 16384; - -int const wave_size = 0x20; - -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; - output->set_modified(); - - blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 + - regs [0x80 + index * 2] + 1; - int volume = 0; - if ( regs [0x8F] & (1 << index) ) - { - blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() + - inaudible_freq * 32) / (inaudible_freq * 16); - if ( period > inaudible_period ) - volume = (regs [0x8A + 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 wave - { - int amp = wave [osc.phase] * volume; - int delta = amp - osc.last_amp; - if ( delta ) - { - osc.last_amp = amp; - synth.offset( last_time, delta, output ); - } - } - - blip_time_t time = last_time + osc.delay; - if ( time < end_time ) - { - if ( !volume ) - { - // maintain phase - blargg_long count = (end_time - time + period - 1) / period; - osc.phase = (osc.phase + count) & (wave_size - 1); - time += count * period; - } - else - { - - int phase = osc.phase; - int last_wave = wave [phase]; - phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop - - do - { - int amp = wave [phase]; - phase = (phase + 1) & (wave_size - 1); - int delta = amp - last_wave; - if ( delta ) - { - last_wave = amp; - synth.offset( time, delta * volume, output ); - } - time += period; - } - while ( time < end_time ); - - osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance - osc.last_amp = wave [phase] * volume; - } - } - osc.delay = time - end_time; - } - last_time = end_time; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h deleted file mode 100644 index 03a6a108..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h +++ /dev/null @@ -1,106 +0,0 @@ -// Konami SCC sound chip emulator - -// Game_Music_Emu 0.5.2 -#ifndef KSS_SCC_APU_H -#define KSS_SCC_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" -#include - -class Scc_Apu { -public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Reset sound chip - void reset(); - - // Write to register at specified time - enum { reg_count = 0x90 }; - void write( blip_time_t time, int reg, int data ); - - // Run sound to specified time, end current time frame, then start 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( blip_time_t length ); - -// Additional features - - // Set sound output of specific oscillator to buffer, where index is - // 0 to 4. If buffer is NULL, the specified oscillator is muted. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Set treble equalization (see documentation) - void treble_eq( blip_eq_t const& ); - -public: - Scc_Apu(); -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 synth; - - void run_until( blip_time_t ); -}; - -inline void Scc_Apu::volume( double v ) { synth.volume( 0.43 / osc_count / amp_range * v ); } - -inline void Scc_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - -inline void Scc_Apu::osc_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 ); - run_until( time ); - 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 ); -} - -inline void Scc_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - oscs [i].output = buf; -} - -inline Scc_Apu::Scc_Apu() -{ - output( 0 ); -} - -inline void Scc_Apu::reset() -{ - last_time = 0; - - for ( int i = 0; i < osc_count; i++ ) - memset( &oscs [i], 0, offsetof (osc_t,output) ); - - memset( regs, 0, sizeof regs ); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp b/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp deleted file mode 100644 index 0a1475db..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp +++ /dev/null @@ -1,426 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "M3u_Playlist.h" -#include "Music_Emu.h" - -#include - -/* 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" - -// gme functions defined here to avoid linking in m3u code unless it's used - -blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) -{ - require( raw_track_count_ ); // file must be loaded first - - if ( !err ) - { - if ( playlist.size() ) - track_count_ = playlist.size(); - - int line = playlist.first_error(); - if ( line ) - { - // avoid using bloated printf() - char* out = &playlist_warning [sizeof playlist_warning]; - *--out = 0; - do { - *--out = line % 10 + '0'; - } while ( (line /= 10) > 0 ); - - static const char str [] = "Problem in m3u at line "; - out -= sizeof str - 1; - memcpy( out, str, sizeof str - 1 ); - set_warning( out ); - } - } - return err; -} - -blargg_err_t Gme_File::load_m3u( const char* path ) { return load_m3u_( playlist.load( path ) ); } - -blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } - -gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } - -gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) -{ - Mem_File_Reader in( data, size ); - return me->load_m3u( in ); -} - - - -static char* skip_white( char* in ) -{ - while ( *in == ' ' ) - in++; - return in; -} - -inline unsigned from_dec( unsigned n ) { return n - '0'; } - -static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) -{ - entry.file = in; - entry.type = ""; - char* out = in; - while ( 1 ) - { - int c = *in; - if ( !c ) break; - in++; - - if ( c == ',' ) // commas in filename - { - char* p = skip_white( in ); - if ( *p == '$' || from_dec( *p ) <= 9 ) - { - in = p; - break; - } - } - - if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix - { - entry.type = ++in; - while ( (c = *in) != 0 && c != ',' ) - in++; - if ( c == ',' ) - { - *in++ = 0; // terminate type - in = skip_white( in ); - } - break; - } - - if ( c == '\\' ) // \ prefix for special characters - { - c = *in; - if ( !c ) break; - in++; - } - *out++ = (char) c; - } - *out = 0; // terminate string - return in; -} - -static char* next_field( char* in, int* result ) -{ - while ( 1 ) - { - in = skip_white( in ); - - if ( !*in ) - break; - - if ( *in == ',' ) - { - in++; - break; - } - - *result = 1; - in++; - } - return skip_white( in ); -} - -static char* parse_int_( char* in, int* out ) -{ - int n = 0; - while ( 1 ) - { - unsigned d = from_dec( *in ); - if ( d > 9 ) - break; - in++; - n = n * 10 + d; - *out = n; - } - return in; -} - -static char* parse_int( char* in, int* out, int* result ) -{ - return next_field( parse_int_( in, out ), result ); -} - -// Returns 16 or greater if not hex -inline int from_hex_char( int h ) -{ - h -= 0x30; - if ( (unsigned) h > 9 ) - h = ((h - 0x11) & 0xDF) + 10; - return h; -} - -static char* parse_track( char* in, M3u_Playlist::entry_t& entry, int* result ) -{ - if ( *in == '$' ) - { - in++; - int n = 0; - while ( 1 ) - { - int h = from_hex_char( *in ); - if ( h > 15 ) - break; - in++; - n = n * 16 + h; - entry.track = n; - } - } - else - { - in = parse_int_( in, &entry.track ); - if ( entry.track >= 0 ) - entry.decimal_track = 1; - } - return next_field( in, result ); -} - -static char* parse_time_( char* in, int* out ) -{ - *out = -1; - int n = -1; - in = parse_int_( in, &n ); - if ( n >= 0 ) - { - *out = n; - if ( *in == ':' ) - { - n = -1; - in = parse_int_( in + 1, &n ); - if ( n >= 0 ) - *out = *out * 60 + n; - } - } - return in; -} - -static char* parse_time( char* in, int* out, int* result ) -{ - return next_field( parse_time_( in, out ), result ); -} - -static char* parse_name( char* in ) -{ - char* out = in; - while ( 1 ) - { - int c = *in; - if ( !c ) break; - in++; - - if ( c == ',' ) // commas in string - { - char* p = skip_white( in ); - if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 ) - { - in = p; - break; - } - } - - if ( c == '\\' ) // \ prefix for special characters - { - c = *in; - if ( !c ) break; - in++; - } - *out++ = (char) c; - } - *out = 0; // terminate string - return in; -} - -static int parse_line( char* in, M3u_Playlist::entry_t& entry ) -{ - int result = 0; - - // file - entry.file = in; - entry.type = ""; - in = parse_filename( in, entry ); - - // track - entry.track = -1; - entry.decimal_track = 0; - in = parse_track( in, entry, &result ); - - // name - entry.name = in; - in = parse_name( in ); - - // time - entry.length = -1; - in = parse_time( in, &entry.length, &result ); - - // loop - entry.intro = -1; - entry.loop = -1; - if ( *in == '-' ) - { - entry.loop = entry.length; - in++; - } - else - { - in = parse_time_( in, &entry.loop ); - if ( entry.loop >= 0 ) - { - entry.intro = 0; - if ( *in == '-' ) // trailing '-' means that intro length was specified - { - in++; - entry.intro = entry.loop; - entry.loop = entry.length - entry.intro; - } - } - } - in = next_field( in, &result ); - - // fade - entry.fade = -1; - in = parse_time( in, &entry.fade, &result ); - - // repeat - entry.repeat = -1; - in = parse_int( in, &entry.repeat, &result ); - - return result; -} - -static void parse_comment( char* in, M3u_Playlist::info_t& info, bool first ) -{ - in = skip_white( in + 1 ); - const char* field = in; - while ( *in && *in != ':' ) - in++; - - if ( *in == ':' ) - { - const char* text = skip_white( in + 1 ); - if ( *text ) - { - *in = 0; - if ( !strcmp( "Composer", field ) ) info.composer = text; - else if ( !strcmp( "Engineer", field ) ) info.engineer = text; - else if ( !strcmp( "Ripping" , field ) ) info.ripping = text; - else if ( !strcmp( "Tagging" , field ) ) info.tagging = text; - else - text = 0; - if ( text ) - return; - *in = ':'; - } - } - - if ( first ) - info.title = field; -} - -blargg_err_t M3u_Playlist::parse_() -{ - info_.title = ""; - info_.composer = ""; - info_.engineer = ""; - info_.ripping = ""; - info_.tagging = ""; - - int const CR = 13; - int const LF = 10; - - data.end() [-1] = LF; // terminate input - - first_error_ = 0; - bool first_comment = true; - int line = 0; - int count = 0; - char* in = data.begin(); - while ( in < data.end() ) - { - // find end of line and terminate it - line++; - char* begin = in; - while ( *in != CR && *in != LF ) - { - if ( !*in ) - return "Not an m3u playlist"; - in++; - } - if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line - *in++ = 0; - *in++ = 0; - - // parse line - if ( *begin == '#' ) - { - parse_comment( begin, info_, first_comment ); - first_comment = false; - } - else if ( *begin ) - { - if ( (int) entries.size() <= count ) - RETURN_ERR( entries.resize( count * 2 + 64 ) ); - - if ( !parse_line( begin, entries [count] ) ) - count++; - else if ( !first_error_ ) - first_error_ = line; - first_comment = false; - } - } - if ( count <= 0 ) - return "Not an m3u playlist"; - - if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) ) - info_.title = ""; - - return entries.resize( count ); -} - -blargg_err_t M3u_Playlist::parse() -{ - blargg_err_t err = parse_(); - if ( err ) - { - entries.clear(); - data.clear(); - } - return err; -} - -blargg_err_t M3u_Playlist::load( Data_Reader& in ) -{ - RETURN_ERR( data.resize( in.remain() + 1 ) ); - RETURN_ERR( in.read( data.begin(), data.size() - 1 ) ); - return parse(); -} - -blargg_err_t M3u_Playlist::load( const char* path ) -{ - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - return load( in ); -} - -blargg_err_t M3u_Playlist::load( void const* in, long size ) -{ - RETURN_ERR( data.resize( size + 1 ) ); - memcpy( data.begin(), in, size ); - return parse(); -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h b/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h deleted file mode 100644 index eda0dc89..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h +++ /dev/null @@ -1,67 +0,0 @@ -// M3U playlist file parser, with support for subtrack information - -// Game_Music_Emu 0.5.2 -#ifndef M3U_PLAYLIST_H -#define M3U_PLAYLIST_H - -#include "blargg_common.h" -#include "Data_Reader.h" - -class M3u_Playlist { -public: - // Load playlist data - blargg_err_t load( const char* path ); - blargg_err_t load( Data_Reader& in ); - blargg_err_t load( void const* data, long size ); - - // Line number of first parse error, 0 if no error. Any lines with parse - // errors are ignored. - int first_error() const { return first_error_; } - - struct info_t - { - const char* title; - const char* composer; - const char* engineer; - const char* ripping; - const char* tagging; - }; - info_t const& info() const { return info_; } - - struct entry_t - { - const char* file; // filename without stupid ::TYPE suffix - const char* type; // if filename has ::TYPE suffix, this will be "TYPE". "" if none. - const char* name; - bool decimal_track; // true if track was specified in hex - // integers are -1 if not present - int track; // 1-based - int length; // seconds - int intro; - int loop; - int fade; - int repeat; // count - }; - entry_t const& operator [] ( int i ) const { return entries [i]; } - int size() const { return entries.size(); } - - void clear(); - -private: - blargg_vector entries; - blargg_vector data; - int first_error_; - info_t info_; - - blargg_err_t parse(); - blargg_err_t parse_(); -}; - -inline void M3u_Playlist::clear() -{ - first_error_ = 0; - entries.clear(); - data.clear(); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Makefile.am b/gme/Game_Music_Emu-0.5.2/gme/Makefile.am deleted file mode 100644 index 75d377cd..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Makefile.am +++ /dev/null @@ -1,64 +0,0 @@ -noinst_LIBRARIES = libgme.a -libgme_a_SOURCES = Ay_Apu.cpp Gb_Apu.cpp Hes_Emu.cpp Nes_Fme7_Apu.cpp Sms_Apu.cpp\ -Ay_Cpu.cpp Gb_Cpu.cpp Kss_Cpu.cpp Nes_Namco_Apu.cpp Snes_Spc.cpp\ -Ay_Emu.cpp Gb_Oscs.cpp Kss_Emu.cpp Nes_Oscs.cpp Spc_Cpu.cpp\ -Blip_Buffer.cpp Gbs_Emu.cpp Kss_Scc_Apu.cpp Nes_Vrc6_Apu.cpp Spc_Dsp.cpp\ -Classic_Emu.cpp gme.cpp M3u_Playlist.cpp Nsfe_Emu.cpp Spc_Emu.cpp\ -Data_Reader.cpp Gme_File.cpp Multi_Buffer.cpp Nsf_Emu.cpp Vgm_Emu.cpp\ -Dual_Resampler.cpp Gym_Emu.cpp Music_Emu.cpp Sap_Apu.cpp Vgm_Emu_Impl.cpp\ -Effects_Buffer.cpp Hes_Apu.cpp Nes_Apu.cpp Sap_Cpu.cpp Ym2413_Emu.cpp\ -Fir_Resampler.cpp Hes_Cpu.cpp Nes_Cpu.cpp Sap_Emu.cpp Ym2612_Emu.cpp\ -Ay_Apu.h\ -Ay_Cpu.h\ -Ay_Emu.h\ -blargg_common.h\ -blargg_config.h\ -blargg_endian.h\ -blargg_source.h\ -Blip_Buffer.h\ -Classic_Emu.h\ -Data_Reader.h\ -Dual_Resampler.h\ -Effects_Buffer.h\ -Fir_Resampler.h\ -Gb_Apu.h\ -Gb_Cpu.h\ -gb_cpu_io.h\ -Gb_Oscs.h\ -Gbs_Emu.h\ -Gme_File.h\ -gme.h\ -Gym_Emu.h\ -Hes_Apu.h\ -Hes_Cpu.h\ -hes_cpu_io.h\ -Hes_Emu.h\ -Kss_Cpu.h\ -Kss_Emu.h\ -Kss_Scc_Apu.h\ -M3u_Playlist.h\ -Multi_Buffer.h\ -Music_Emu.h\ -Nes_Apu.h\ -Nes_Cpu.h\ -nes_cpu_io.h\ -Nes_Fme7_Apu.h\ -Nes_Namco_Apu.h\ -Nes_Oscs.h\ -Nes_Vrc6_Apu.h\ -Nsfe_Emu.h\ -Nsf_Emu.h\ -Sap_Apu.h\ -Sap_Cpu.h\ -sap_cpu_io.h\ -Sap_Emu.h\ -Sms_Apu.h\ -Sms_Oscs.h\ -Snes_Spc.h\ -Spc_Cpu.h\ -Spc_Dsp.h\ -Spc_Emu.h\ -Vgm_Emu.h\ -Vgm_Emu_Impl.h\ -Ym2413_Emu.h\ -Ym2612_Emu.h diff --git a/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp b/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp deleted file mode 100644 index ecd8f8ad..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ - -#include "Multi_Buffer.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" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) -{ - length_ = 0; - sample_rate_ = 0; - channels_changed_count_ = 1; -} - -blargg_err_t Multi_Buffer::set_channel_count( int ) { return 0; } - -// Silent_Buffer - -Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse -{ - // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? - chan.left = 0; - chan.center = 0; - chan.right = 0; -} - -// Mono_Buffer - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ - chan.center = &buf; - chan.left = &buf; - chan.right = &buf; -} - -Mono_Buffer::~Mono_Buffer() { } - -blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) -{ - RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} - -// Stereo_Buffer - -Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) -{ - chan.center = &bufs [0]; - chan.left = &bufs [1]; - chan.right = &bufs [2]; -} - -Stereo_Buffer::~Stereo_Buffer() { } - -blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) -{ - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Stereo_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Stereo_Buffer::bass_freq( int bass ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( bass ); -} - -void Stereo_Buffer::clear() -{ - stereo_added = 0; - was_stereo = false; - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -void Stereo_Buffer::end_frame( blip_time_t clock_count ) -{ - stereo_added = 0; - for ( unsigned i = 0; i < buf_count; i++ ) - { - stereo_added |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); - } -} - -long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) -{ - require( !(count & 1) ); // count must be even - count = (unsigned) count / 2; - - long avail = bufs [0].samples_avail(); - if ( count > avail ) - count = avail; - if ( count ) - { - int bufs_used = stereo_added | was_stereo; - //dprintf( "%X\n", bufs_used ); - if ( bufs_used <= 1 ) - { - mix_mono( out, count ); - bufs [0].remove_samples( count ); - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); - } - else if ( bufs_used & 1 ) - { - mix_stereo( out, count ); - bufs [0].remove_samples( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - else - { - mix_stereo_no_center( out, count ); - bufs [0].remove_silence( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - - // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) - { - was_stereo = stereo_added; - stereo_added = 0; - } - } - - return count * 2; -} - -void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [1] ); - BLIP_READER_BEGIN( left, bufs [1] ); - BLIP_READER_BEGIN( right, bufs [2] ); - BLIP_READER_BEGIN( center, bufs [0] ); - - for ( ; count; --count ) - { - int c = BLIP_READER_READ( center ); - blargg_long l = c + BLIP_READER_READ( left ); - blargg_long r = c + BLIP_READER_READ( right ); - if ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - BLIP_READER_NEXT( center, bass ); - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - BLIP_READER_NEXT( left, bass ); - BLIP_READER_NEXT( right, bass ); - - out [0] = l; - out [1] = r; - out += 2; - } - - BLIP_READER_END( center, bufs [0] ); - BLIP_READER_END( right, bufs [2] ); - BLIP_READER_END( left, bufs [1] ); -} - -void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [1] ); - BLIP_READER_BEGIN( left, bufs [1] ); - BLIP_READER_BEGIN( right, bufs [2] ); - - for ( ; count; --count ) - { - blargg_long l = BLIP_READER_READ( left ); - if ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - blargg_long r = BLIP_READER_READ( right ); - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - BLIP_READER_NEXT( left, bass ); - BLIP_READER_NEXT( right, bass ); - - out [0] = l; - out [1] = r; - out += 2; - } - - BLIP_READER_END( right, bufs [2] ); - BLIP_READER_END( left, bufs [1] ); -} - -void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [0] ); - BLIP_READER_BEGIN( center, bufs [0] ); - - for ( ; count; --count ) - { - blargg_long s = BLIP_READER_READ( center ); - if ( (BOOST::int16_t) s != s ) - s = 0x7FFF - (s >> 24); - - BLIP_READER_NEXT( center, bass ); - out [0] = s; - out [1] = s; - out += 2; - } - - BLIP_READER_END( center, bufs [0] ); -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h b/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h deleted file mode 100644 index a39cca1a..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h +++ /dev/null @@ -1,156 +0,0 @@ -// Multi-channel sound buffer interface, and basic mono and stereo buffers - -// Blip_Buffer 0.4.1 -#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: - Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } - - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); - - // Get indexed channel, from 0 to channel count - 1 - struct channel_t { - Blip_Buffer* center; - Blip_Buffer* left; - Blip_Buffer* right; - }; - enum { type_index_mask = 0xFF }; - enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; - virtual channel_t channel( int index, int type ) = 0; - - // See Blip_Buffer.h - virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; - virtual void clock_rate( long ) = 0; - virtual void bass_freq( int ) = 0; - virtual void clear() = 0; - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // See Blip_Buffer.h - virtual void end_frame( blip_time_t ) = 0; - - // 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 long read_samples( blip_sample_t*, long ) = 0; - virtual long samples_avail() const = 0; - -public: - BLARGG_DISABLE_NOTHROW -protected: - void channels_changed() { channels_changed_count_++; } -private: - // noncopyable - Multi_Buffer( const Multi_Buffer& ); - Multi_Buffer& operator = ( const Multi_Buffer& ); - - unsigned channels_changed_count_; - long sample_rate_; - int length_; - int const samples_per_frame_; -}; - -// Uses a single buffer and outputs mono samples. -class Mono_Buffer : public Multi_Buffer { - Blip_Buffer buf; - channel_t chan; -public: - // Buffer used for all channels - Blip_Buffer* center() { return &buf; } - -public: - Mono_Buffer(); - ~Mono_Buffer(); - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long rate ) { buf.clock_rate( rate ); } - void bass_freq( int freq ) { buf.bass_freq( freq ); } - void clear() { buf.clear(); } - long samples_avail() const { return buf.samples_avail(); } - long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - channel_t channel( int, int ) { return chan; } - void end_frame( blip_time_t t ) { buf.end_frame( t ); } -}; - -// 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 [0]; } - Blip_Buffer* left() { return &bufs [1]; } - Blip_Buffer* right() { return &bufs [2]; } - -public: - Stereo_Buffer(); - ~Stereo_Buffer(); - blargg_err_t set_sample_rate( long, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int, int ) { return chan; } - void end_frame( blip_time_t ); - - long samples_avail() const { return bufs [0].samples_avail() * 2; } - long read_samples( blip_sample_t*, long ); - -private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; - channel_t chan; - int stereo_added; - int was_stereo; - - void mix_stereo_no_center( blip_sample_t*, blargg_long ); - void mix_stereo( blip_sample_t*, blargg_long ); - void mix_mono( blip_sample_t*, blargg_long ); -}; - -// Silent_Buffer generates no samples, useful where no sound is wanted -class Silent_Buffer : public Multi_Buffer { - channel_t chan; -public: - Silent_Buffer(); - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) - { - return Multi_Buffer::set_sample_rate( rate, msec ); - } - void clock_rate( long ) { } - void bass_freq( int ) { } - void clear() { } - channel_t channel( int, int ) { return chan; } - void end_frame( blip_time_t ) { } - long samples_avail() const { return 0; } - long read_samples( blip_sample_t*, long ) { return 0; } -}; - - -inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return 0; -} - -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } - -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } - -inline int Multi_Buffer::length() const { return length_; } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp deleted file mode 100644 index 31c7233c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Music_Emu.h" - -#include "Multi_Buffer.h" -#include - -/* 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" - -int const stereo = 2; // number of channels for stereo -int const silence_max = 6; // seconds -int const silence_threshold = 0x10; -long const fade_block_size = 512; -int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) - -Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 }; - -void Music_Emu::clear_track_vars() -{ - current_track_ = -1; - out_time = 0; - emu_time = 0; - emu_track_ended_ = true; - track_ended_ = true; - fade_start = LONG_MAX / 2 + 1; - fade_step = 1; - silence_time = 0; - silence_count = 0; - buf_remain = 0; - warning(); // clear warning -} - -void Music_Emu::unload() -{ - voice_count_ = 0; - clear_track_vars(); - Gme_File::unload(); -} - -Music_Emu::Music_Emu() -{ - effects_buffer = 0; - - sample_rate_ = 0; - mute_mask_ = 0; - tempo_ = 1.0; - gain_ = 1.0; - - // defaults - max_initial_silence = 2; - silence_lookahead = 3; - ignore_silence_ = false; - equalizer_.treble = -1.0; - equalizer_.bass = 60; - - static const char* const names [] = { - "Voice 1", "Voice 2", "Voice 3", "Voice 4", - "Voice 5", "Voice 6", "Voice 7", "Voice 8" - }; - set_voice_names( names ); - Music_Emu::unload(); // non-virtual -} - -Music_Emu::~Music_Emu() { delete effects_buffer; } - -blargg_err_t Music_Emu::set_sample_rate( long rate ) -{ - require( !sample_rate() ); // sample rate can't be changed once set - RETURN_ERR( set_sample_rate_( rate ) ); - RETURN_ERR( buf.resize( buf_size ) ); - sample_rate_ = rate; - return 0; -} - -void Music_Emu::pre_load() -{ - require( sample_rate() ); // set_sample_rate() must be called before loading a file - Gme_File::pre_load(); -} - -void Music_Emu::set_equalizer( equalizer_t const& eq ) -{ - equalizer_ = eq; - set_equalizer_( eq ); -} - -void Music_Emu::mute_voice( int index, bool mute ) -{ - require( (unsigned) index < (unsigned) voice_count() ); - int bit = 1 << index; - int mask = mute_mask_ | bit; - if ( !mute ) - mask ^= bit; - mute_voices( mask ); -} - -void Music_Emu::mute_voices( int mask ) -{ - require( sample_rate() ); // sample rate must be set first - mute_mask_ = mask; - mute_voices_( mask ); -} - -void Music_Emu::set_tempo( double t ) -{ - require( sample_rate() ); // sample rate must be set first - double const min = 0.02; - double const max = 4.00; - if ( t < min ) t = min; - if ( t > max ) t = max; - tempo_ = t; - set_tempo_( t ); -} - -void Music_Emu::post_load_() -{ - set_tempo( tempo_ ); - remute_voices(); -} - -blargg_err_t Music_Emu::start_track( int track ) -{ - clear_track_vars(); - - int remapped = track; - RETURN_ERR( remap_track_( &remapped ) ); - current_track_ = track; - RETURN_ERR( start_track_( remapped ) ); - - emu_track_ended_ = false; - track_ended_ = false; - - if ( !ignore_silence_ ) - { - // play until non-silence or end of track - for ( long end = max_initial_silence * stereo * sample_rate(); emu_time < end; ) - { - fill_buf(); - if ( buf_remain | (int) emu_track_ended_ ) - break; - } - - emu_time = buf_remain; - out_time = 0; - silence_time = 0; - silence_count = 0; - } - return track_ended() ? warning() : 0; -} - -void Music_Emu::end_track_if_error( blargg_err_t err ) -{ - if ( err ) - { - emu_track_ended_ = true; - set_warning( err ); - } -} - -// Tell/Seek - -blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const -{ - blargg_long sec = msec / 1000; - msec -= sec * 1000; - return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; -} - -long Music_Emu::tell() const -{ - blargg_long rate = sample_rate() * stereo; - blargg_long sec = out_time / rate; - return sec * 1000 + (out_time - sec * rate) * 1000 / rate; -} - -blargg_err_t Music_Emu::seek( long msec ) -{ - blargg_long time = msec_to_samples( msec ); - if ( time < out_time ) - RETURN_ERR( start_track( current_track_ ) ); - return skip( time - out_time ); -} - -blargg_err_t Music_Emu::skip( long count ) -{ - require( current_track() >= 0 ); // start_track() must have been called already - out_time += count; - - // remove from silence and buf first - { - long n = min( count, silence_count ); - silence_count -= n; - count -= n; - - n = min( count, buf_remain ); - buf_remain -= n; - count -= n; - } - - if ( count && !emu_track_ended_ ) - { - emu_time += count; - end_track_if_error( skip_( count ) ); - } - - if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended - track_ended_ |= emu_track_ended_; - - return 0; -} - -blargg_err_t Music_Emu::skip_( long count ) -{ - // for long skip, mute sound - const long threshold = 30000; - if ( count > threshold ) - { - int saved_mute = mute_mask_; - mute_voices( ~0 ); - - while ( count > threshold / 2 && !emu_track_ended_ ) - { - RETURN_ERR( play_( buf_size, buf.begin() ) ); - count -= buf_size; - } - - mute_voices( saved_mute ); - } - - while ( count && !emu_track_ended_ ) - { - long n = buf_size; - if ( n > count ) - n = count; - count -= n; - RETURN_ERR( play_( n, buf.begin() ) ); - } - return 0; -} - -// Fading - -void Music_Emu::set_fade( long start_msec, long length_msec ) -{ - fade_step = sample_rate() * length_msec / (fade_block_size * fade_shift * 1000 / stereo); - fade_start = msec_to_samples( start_msec ); -} - -// unit / pow( 2.0, (double) x / step ) -static int int_log( blargg_long x, int step, int unit ) -{ - int shift = x / step; - int fraction = (x - shift * step) * unit / step; - return ((unit - fraction) + (fraction >> 1)) >> shift; -} - -void Music_Emu::handle_fade( long out_count, sample_t* out ) -{ - for ( int i = 0; i < out_count; i += fade_block_size ) - { - int const shift = 14; - int const unit = 1 << shift; - int gain = int_log( (out_time + i - fade_start) / fade_block_size, - fade_step, unit ); - if ( gain < (unit >> fade_shift) ) - track_ended_ = emu_track_ended_ = true; - - sample_t* io = &out [i]; - for ( int count = min( fade_block_size, out_count - i ); count; --count ) - { - *io = sample_t ((*io * gain) >> shift); - ++io; - } - } -} - -// Silence detection - -void Music_Emu::emu_play( long count, sample_t* out ) -{ - check( current_track_ >= 0 ); - emu_time += count; - if ( current_track_ >= 0 && !emu_track_ended_ ) - end_track_if_error( play_( count, out ) ); - else - memset( out, 0, count * sizeof *out ); -} - -// number of consecutive silent samples at end -static long count_silence( Music_Emu::sample_t* begin, long size ) -{ - Music_Emu::sample_t first = *begin; - *begin = silence_threshold; // sentinel - Music_Emu::sample_t* p = begin + size; - while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } - *begin = first; - return size - (p - begin); -} - -// fill internal buffer and check it for silence -void Music_Emu::fill_buf() -{ - assert( !buf_remain ); - if ( !emu_track_ended_ ) - { - emu_play( buf_size, buf.begin() ); - long silence = count_silence( buf.begin(), buf_size ); - if ( silence < buf_size ) - { - silence_time = emu_time - silence; - buf_remain = buf_size; - return; - } - } - silence_count += buf_size; -} - -blargg_err_t Music_Emu::play( long out_count, sample_t* out ) -{ - if ( track_ended_ ) - { - memset( out, 0, out_count * sizeof *out ); - } - else - { - require( current_track() >= 0 ); - require( out_count % stereo == 0 ); - - assert( emu_time >= out_time ); - - // prints nifty graph of how far ahead we are when searching for silence - //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); - - long pos = 0; - if ( silence_count ) - { - // during a run of silence, run emulator at >=2x speed so it gets ahead - long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time; - while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) ) - fill_buf(); - - // fill with silence - pos = min( silence_count, out_count ); - memset( out, 0, pos * sizeof *out ); - silence_count -= pos; - - if ( emu_time - silence_time > silence_max * stereo * sample_rate() ) - { - track_ended_ = emu_track_ended_ = true; - silence_count = 0; - buf_remain = 0; - } - } - - if ( buf_remain ) - { - // empty silence buf - long n = min( buf_remain, out_count - pos ); - memcpy( &out [pos], buf.begin() + (buf_size - buf_remain), n * sizeof *out ); - buf_remain -= n; - pos += n; - } - - // generate remaining samples normally - long remain = out_count - pos; - if ( remain ) - { - emu_play( remain, out + pos ); - track_ended_ |= emu_track_ended_; - - if ( !ignore_silence_ || out_time > fade_start ) - { - // check end for a new run of silence - long silence = count_silence( out + pos, remain ); - if ( silence < remain ) - silence_time = emu_time - silence; - - if ( emu_time - silence_time >= buf_size ) - fill_buf(); // cause silence detection on next play() - } - } - - if ( out_time > fade_start ) - handle_fade( out_count, out ); - } - out_time += out_count; - return 0; -} - -// Gme_Info_ - -blargg_err_t Gme_Info_::set_sample_rate_( long ) { return 0; } -void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu -void Gme_Info_::post_load_() { Gme_File::post_load_(); } // skip Music_Emu -void Gme_Info_::set_equalizer_( equalizer_t const& ){ check( false ); } -void Gme_Info_::mute_voices_( int ) { check( false ); } -void Gme_Info_::set_tempo_( double ) { } -blargg_err_t Gme_Info_::start_track_( int ) { return "Use full emulator for playback"; } -blargg_err_t Gme_Info_::play_( long, sample_t* ) { return "Use full emulator for playback"; } diff --git a/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h deleted file mode 100644 index 573403ce..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h +++ /dev/null @@ -1,211 +0,0 @@ -// Common interface to game music file emulators - -// Game_Music_Emu 0.5.2 -#ifndef MUSIC_EMU_H -#define MUSIC_EMU_H - -#include "Gme_File.h" -class Multi_Buffer; - -struct Music_Emu : public Gme_File { -public: -// Basic functionality (see Gme_File.h for file loading/track info functions) - - // Set output sample rate. Must be called only once before loading file. - blargg_err_t set_sample_rate( long sample_rate ); - - // Start a track, where 0 is the first track. Also clears warning string. - blargg_err_t start_track( int ); - - // Generate 'count' samples info 'buf'. Output is in stereo. Any emulation - // errors set warning string, and major errors also end track. - typedef short sample_t; - blargg_err_t play( long count, sample_t* buf ); - -// Informational - - // Sample rate sound is generated at - long sample_rate() const; - - // Index of current track or -1 if one hasn't been started - int current_track() const; - - // Number of voices used by currently loaded file - int voice_count() const; - - // Names of voices - const char** voice_names() const; - -// Track status/control - - // Number of milliseconds (1000 msec = 1 second) played since beginning of track - long tell() const; - - // Seek to new time in track. Seeking backwards or far forward can take a while. - blargg_err_t seek( long msec ); - - // Skip n samples - blargg_err_t skip( long n ); - - // True if a track has reached its end - bool track_ended() const; - - // Set start time and length of track fade out. Once fade ends track_ended() returns - // true. Fade time can be changed while track is playing. - void set_fade( long start_msec, long length_msec = 8000 ); - - // Disable automatic end-of-track detection and skipping of silence at beginning - void ignore_silence( bool disable = true ); - - // Info for current track - Gme_File::track_info; - blargg_err_t track_info( track_info_t* out ) const; - -// Sound customization - - // Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. - // Track length as returned by track_info() assumes a tempo of 1.0. - void set_tempo( double ); - - // Mute/unmute voice i, where voice 0 is first voice - void mute_voice( int index, bool mute = true ); - - // Set muting state of all voices at once using a bit mask, where -1 mutes them all, - // 0 unmutes them all, 0x01 mutes just the first voice, etc. - void mute_voices( int mask ); - - // Change overall output amplitude, where 1.0 results in minimal clamping. - // Must be called before set_sample_rate(). - void set_gain( double ); - - // Request use of custom multichannel buffer. Only supported by "classic" emulators; - // on others this has no effect. Should be called only once *before* set_sample_rate(). - virtual void set_buffer( Multi_Buffer* ) { } - -// Sound equalization (treble/bass) - - // Frequency equalizer parameters (see gme.txt) - // See gme.h for definition of struct gme_equalizer_t. - typedef gme_equalizer_t equalizer_t; - - // Current frequency equalizater parameters - equalizer_t const& equalizer() const; - - // Set frequency equalizer parameters - void set_equalizer( equalizer_t const& ); - - // Equalizer settings for TV speaker - static equalizer_t const tv_eq; - -public: - Music_Emu(); - ~Music_Emu(); -protected: - void set_max_initial_silence( int n ) { max_initial_silence = n; } - void set_silence_lookahead( int n ) { silence_lookahead = n; } - void set_voice_count( int n ) { voice_count_ = n; } - void set_voice_names( const char* const* names ); - void set_track_ended() { emu_track_ended_ = true; } - double gain() const { return gain_; } - double tempo() const { return tempo_; } - void remute_voices(); - - virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; - virtual void set_equalizer_( equalizer_t const& ) { }; - virtual void mute_voices_( int mask ) = 0; - virtual void set_tempo_( double ) = 0; - virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this - virtual blargg_err_t play_( long count, sample_t* out ) = 0; - virtual blargg_err_t skip_( long count ); -protected: - virtual void unload(); - virtual void pre_load(); - virtual void post_load_(); -private: - // general - equalizer_t equalizer_; - int max_initial_silence; - const char** voice_names_; - int voice_count_; - int mute_mask_; - double tempo_; - double gain_; - - long sample_rate_; - blargg_long msec_to_samples( blargg_long msec ) const; - - // track-specific - int current_track_; - blargg_long out_time; // number of samples played since start of track - blargg_long emu_time; // number of samples emulator has generated since start of track - bool emu_track_ended_; // emulator has reached end of track - volatile bool track_ended_; - void clear_track_vars(); - void end_track_if_error( blargg_err_t ); - - // fading - blargg_long fade_start; - int fade_step; - void handle_fade( long count, sample_t* out ); - - // silence detection - int silence_lookahead; // speed to run emulator when looking ahead for silence - bool ignore_silence_; - long silence_time; // number of samples where most recent silence began - long silence_count; // number of samples of silence to play before using buf - long buf_remain; // number of samples left in silence buffer - enum { buf_size = 2048 }; - blargg_vector buf; - void fill_buf(); - void emu_play( long count, sample_t* out ); - - Multi_Buffer* effects_buffer; - friend Music_Emu* gme_new_emu( gme_type_t, long ); - friend void gme_set_stereo_depth( Music_Emu*, double ); -}; - -// base class for info-only derivations -struct Gme_Info_ : Music_Emu -{ - virtual blargg_err_t set_sample_rate_( long sample_rate ); - virtual void set_equalizer_( equalizer_t const& ); - virtual void mute_voices_( int mask ); - virtual void set_tempo_( double ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t play_( long count, sample_t* out ); - virtual void pre_load(); - virtual void post_load_(); -}; - -inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const -{ - return track_info( out, current_track_ ); -} - -inline long Music_Emu::sample_rate() const { return sample_rate_; } -inline const char** Music_Emu::voice_names() const { return voice_names_; } -inline int Music_Emu::voice_count() const { return voice_count_; } -inline int Music_Emu::current_track() const { return current_track_; } -inline bool Music_Emu::track_ended() const { return track_ended_; } -inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } - -inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; } -inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } -inline void Music_Emu::ignore_silence( bool b ) { ignore_silence_ = b; } -inline blargg_err_t Music_Emu::start_track_( int ) { return 0; } - -inline void Music_Emu::set_voice_names( const char* const* names ) -{ - // Intentional removal of const, so users don't have to remember obscure const in middle - voice_names_ = (const char**) names; -} - -inline void Music_Emu::mute_voices_( int ) { } - -inline void Music_Emu::set_gain( double g ) -{ - assert( !sample_rate() ); // you must set gain before setting sample rate - gain_ = g; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp deleted file mode 100644 index 8daf5d0e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp +++ /dev/null @@ -1,391 +0,0 @@ -// Nes_Snd_Emu 0.1.8. 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" - -int const amp_range = 15; - -Nes_Apu::Nes_Apu() : - square1( &square_synth ), - square2( &square_synth ) -{ - tempo_ = 1.0; - dmc.apu = this; - dmc.prg_reader = NULL; - irq_notifier_ = NULL; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = ▵ - oscs [3] = &noise; - oscs [4] = &dmc; - - output( NULL ); - 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 v ) -{ - dmc.nonlinear = true; - square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); - - const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); - triangle.synth.volume( 3.0 * tnd ); - noise.synth.volume( 2.0 * tnd ); - 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 ) -{ - dmc.nonlinear = false; - square_synth.volume( 0.1128 / amp_range * v ); - triangle.synth.volume( 0.12765 / amp_range * v ); - noise.synth.volume( 0.0741 / amp_range * v ); - dmc.synth.volume( 0.42545 / 127 * v ); -} - -void Nes_Apu::output( Blip_Buffer* buffer ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_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; - earliest_irq_ = no_irq; - frame_delay = 1; - write_register( 0, 0x4017, 0x00 ); - write_register( 0, 0x4015, 0x00 ); - - for ( nes_addr_t addr = start_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() -{ - nes_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_ ) - irq_notifier_( irq_data ); - } -} - -// frames - -void Nes_Apu::run_until( nes_time_t end_time ) -{ - require( end_time >= last_dmc_time ); - if ( end_time > next_dmc_read_time() ) - { - nes_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } -} - -void Nes_Apu::run_until_( nes_time_t end_time ) -{ - require( end_time >= last_time ); - - if ( end_time == last_time ) - return; - - if ( last_dmc_time < end_time ) - { - nes_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 - nes_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, nes_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( nes_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( nes_time_t time, nes_addr_t 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 - start_addr) > end_addr - start_addr ) - return; - - run_until_( time ); - - if ( addr < 0x4014 ) - { - // Write to channel - int osc_index = (addr - start_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 - 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( nes_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/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h deleted file mode 100644 index dbd8484c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h +++ /dev/null @@ -1,179 +0,0 @@ -// NES 2A03 APU sound chip emulator - -// Nes_Snd_Emu 0.1.8 -#ifndef NES_APU_H -#define NES_APU_H - -#include "blargg_common.h" - -typedef blargg_long nes_time_t; // CPU clock cycle count -typedef unsigned nes_addr_t; // 16-bit memory address - -#include "Nes_Oscs.h" - -struct apu_state_t; -class Nes_Buffer; - -class Nes_Apu { -public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Set 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, nes_addr_t ), void* user_data = NULL ); - - // 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 ). - - // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; - void write_register( nes_time_t, nes_addr_t, int data ); - - // Read from status register at 0x4015 - enum { status_addr = 0x4015 }; - int read_status( nes_time_t ); - - // Run all oscillators up to specified time, end current time frame, then - // start 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 ); - -// Additional optional features (can be ignored without any problem) - - // Reset internal frame counter, registers, and all oscillators. - // Use PAL timing if pal_timing is true, otherwise use NTSC timing. - // Set 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 ); - - // Adjust frame period - void set_tempo( double ); - - // Save/load exact emulation state - void save_state( apu_state_t* out ) const; - void load_state( apu_state_t const& ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Set treble equalization (see notes.txt) - void treble_eq( const blip_eq_t& ); - - // Set sound output of specific oscillator to buffer. If buffer is NULL, - // the specified oscillator is muted and emulation accuracy is reduced. - // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, - // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* buffer ); - - // Set 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 ); - - // Get 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 = LONG_MAX / 2 + 1 }; - enum { irq_waiting = 0 }; - nes_time_t earliest_irq( nes_time_t ) const; - - // Count 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; - - // Run 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 ); - -public: - Nes_Apu(); - BLARGG_DISABLE_NOTHROW -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); - static double nonlinear_tnd_gain() { return 0.75; } -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; - void (*irq_notifier_)( void* user_data ); - void* irq_data; - 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::osc_output( int osc, Blip_Buffer* buf ) -{ - assert( (unsigned) osc < osc_count ); - oscs [osc]->output = buf; -} - -inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const -{ - return earliest_irq_; -} - -inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) -{ - dmc.prg_reader_data = user_data; - dmc.prg_reader = func; -} - -inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) -{ - irq_notifier_ = func; - irq_data = user_data; -} - -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_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 + long (bits_remain - 1) * period; -} - -inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp deleted file mode 100644 index 480b4aa4..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp +++ /dev/null @@ -1,1084 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Nes_Cpu.h" - -#include "blargg_endian.h" -#include - -#define BLARGG_CPU_X86 1 - -/* 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 */ - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -#define FLUSH_TIME() (void) (s.time = s_time) -#define CACHE_TIME() (void) (s_time = s.time) - -#include "nes_cpu_io.h" - -#include "blargg_source.h" - -#ifndef CPU_DONE - #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } -#endif - -#ifndef CPU_READ_PPU - #define CPU_READ_PPU( cpu, addr, out, time )\ - {\ - FLUSH_TIME();\ - out = CPU_READ( cpu, addr, time );\ - CACHE_TIME();\ - } -#endif - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -inline void Nes_Cpu::set_code_page( int i, void const* p ) -{ - state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size ); -} - -int const st_n = 0x80; -int const st_v = 0x40; -int const st_r = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; - -void Nes_Cpu::reset( void const* unmapped_page ) -{ - check( state == &state_ ); - state = &state_; - r.status = st_i; - r.sp = 0xFF; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - state_.time = 0; - state_.base = 0; - irq_time_ = future_nes_time; - end_time_ = future_nes_time; - error_count_ = 0; - - assert( page_size == 0x800 ); // assumes this - set_code_page( page_count, unmapped_page ); - map_code( 0x2000, 0xE000, unmapped_page, true ); - map_code( 0x0000, 0x2000, low_mem, true ); - - blargg_verify_byte_order(); -} - -void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool mirror ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 ); - - unsigned page = start / page_size; - for ( unsigned n = size / page_size; n; --n ) - { - set_code_page( page++, data ); - if ( !mirror ) - data = (char const*) data + page_size; - } -} - -#define TIME (s_time + s.base) -#define READ_LIKELY_PPU( addr, out ) {CPU_READ_PPU( this, (addr), out, TIME );} -#define READ( addr ) CPU_READ( this, (addr), TIME ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} -#define READ_LOW( addr ) (low_mem [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) -#define READ_PROG( addr ) (s.code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -bool Nes_Cpu::run( nes_time_t end_time ) -{ - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - // even on x86, using s.time in place of s_time was slower - fint16 s_time = s.time; - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; - SET_SP( r.sp ); - - // status flags - #define IS_NEG (nz & 0x8080) - - #define CALC_STATUS( out ) do {\ - out = status & (st_v | st_d | st_i);\ - out |= ((nz >> 8) | nz) & st_n;\ - out |= c >> 8 & st_c;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & (st_v | st_d | st_i);\ - nz = in << 8;\ - c = nz;\ - nz |= ~in & st_z;\ - } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 - { - fuint8 temp = r.status; - SET_STATUS( temp ); - } - - goto loop; -dec_clock_loop: - s_time--; -loop: - - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) pc < 0x10000 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - check( -32768 <= s_time && s_time < 32767 ); - - uint8_t const* instr = s.code_map [pc >> page_bits]; - fuint8 opcode; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - opcode = instr [pc]; - pc++; - instr += pc; - #else - instr += PAGE_OFFSET( pc ); - opcode = *instr++; - pc++; - #endif - - static uint8_t const clock_table [256] = - {// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 - 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 - 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 - 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 - 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A - 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E - 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F - }; // 0x00 was 7 and 0xF2 was 2 - - fuint16 data; - -#if !BLARGG_CPU_X86 - if ( s_time >= 0 ) - goto out_of_time; - s_time += clock_table [opcode]; - - data = *instr; - - switch ( opcode ) - { -#else - - data = clock_table [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = *instr; - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; -#endif - -// Macros - -#define GET_MSB() (instr [1]) -#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) -#define GET_ADDR() GET_LE16( instr ) - -#define NO_PAGE_CROSSING( lsb ) -#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; - -#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - -#define IND_Y( cross, out ) {\ - fuint16 temp = READ_LOW( data ) + y;\ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - cross( temp );\ - } - -#define IND_X( out ) {\ - fuint16 temp = data + x;\ - out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ - } - -#define ARITH_ADDR_MODES( op )\ -case op - 0x04: /* (ind,x) */\ - IND_X( data )\ - goto ptr##op;\ -case op + 0x0C: /* (ind),y */\ - IND_Y( HANDLE_PAGE_CROSSING, data )\ - goto ptr##op;\ -case op + 0x10: /* zp,X */\ - data = uint8_t (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:\ - HANDLE_PAGE_CROSSING( data );\ -case op + 0x08: /* abs */\ - ADD_PAGE();\ -ptr##op:\ - FLUSH_TIME();\ - data = READ( data );\ - CACHE_TIME();\ -case op + 0x04: /* imm */\ -imm##op: - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ -{\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop;\ - pc = BOOST::uint16_t (pc + offset);\ - s_time += extra_clock >> 8 & 1;\ - goto loop;\ -} - -// Often-Used - - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (data + x) ); - pc++; - goto loop; - - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - fuint16 temp = pc + 1; - pc = GET_ADDR(); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x4C: // JMP abs - pc = GET_ADDR(); - goto loop; - - case 0xE8: // INX - INC_DEC_XY( x, 1 ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ); - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xC8: // INY - INC_DEC_XY( y, 1 ) - - case 0xA8: // TAY - y = a; - nz = a; - goto loop; - - case 0x98: // TYA - a = y; - nz = y; - goto loop; - - case 0xAD:{// LDA abs - unsigned addr = GET_ADDR(); - pc += 2; - READ_LIKELY_PPU( addr, nz ); - a = nz; - goto loop; - } - - case 0x60: // RTS - pc = 1 + READ_LOW( sp ); - pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); - sp = (sp - 0xFE) | 0x100; - goto loop; - - { - fuint16 addr; - - case 0x99: // STA abs,Y - addr = y + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x8D: // STA abs - addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x9D: // STA abs,X (slightly more common than STA abs) - addr = x + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - sta_ptr: - FLUSH_TIME(); - WRITE( addr, a ); - CACHE_TIME(); - goto loop; - - case 0x91: // STA (ind),Y - IND_Y( NO_PAGE_CROSSING, addr ) - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - IND_X( addr ) - pc++; - goto sta_ptr; - - } - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - - // common read instructions - { - fuint16 addr; - - case 0xA1: // LDA (ind,X) - IND_X( addr ) - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - HANDLE_PAGE_CROSSING( addr ); - addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); - pc++; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - HANDLE_PAGE_CROSSING( data + y ); - addr = GET_ADDR() + y; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xBD: // LDA abs,X - HANDLE_PAGE_CROSSING( data + x ); - addr = GET_ADDR() + x; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - a_nz_read_addr: - FLUSH_TIME(); - a = nz = READ( addr ); - CACHE_TIME(); - goto loop; - - } - -// Branch - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - -// Load/store - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (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 = uint8_t (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; - HANDLE_PAGE_CROSSING( data ); - case 0xAC:{// LDY abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xBE: // LDX abs,y - data += y; - HANDLE_PAGE_CROSSING( data ); - case 0xAE:{// LDX abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - x = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - unsigned addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, temp ); - goto loop; - } - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( 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 &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( 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 &= 0xFF; - goto loop; - -// Logical - - 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; - - case 0x2C:{// BIT abs - unsigned addr = GET_ADDR(); - pc += 2; - status &= ~st_v; - READ_LIKELY_PPU( addr, nz ); - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - } - - case 0x24: // BIT zp - nz = READ_LOW( data ); - pc++; - status &= ~st_v; - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - case 0xEB: // unofficial equivalent - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= ov >> 2 & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) 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 = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - ADD_PAGE(); - FLUSH_TIME(); - int temp = READ( 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(); - nz = c >> 8 & 1; - FLUSH_TIME(); - nz |= (c = READ( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (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 = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (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 - - case 0xCA: // DEX - INC_DEC_XY( x, -1 ) - - case 0x88: // DEY - INC_DEC_XY( y, -1 ) - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = (unsigned) -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 = (unsigned) -1; - inc_common: - FLUSH_TIME(); - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xAA: // TAX - x = a; - nz = a; - goto loop; - - case 0x8A: // TXA - a = x; - 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; - -// Stack - - case 0x48: // PHA - PUSH( a ); // verified - goto loop; - - case 0x68: // PLA - a = nz = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - goto loop; - - case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) goto loop; - if ( status & st_i ) goto loop; - s_time += delta; - s.base = irq_time_; - goto loop; - } - - case 0x28:{// PLP - fuint8 temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - fuint8 changed = status ^ temp; - SET_STATUS( temp ); - if ( !(changed & st_i) ) - goto loop; // I flag didn't change - if ( status & st_i ) - goto handle_sei; - goto handle_cli; - } - - case 0x08: { // PHP - fuint8 temp; - CALC_STATUS( temp ); - PUSH( temp | (st_b | st_r) ); - goto loop; - } - - case 0x6C:{// JMP (ind) - data = GET_ADDR(); - check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space - uint8_t const* page = s.code_map [data >> page_bits]; - pc = page [PAGE_OFFSET( data )]; - data = (data & 0xFF00) | ((data + 1) & 0xFF); - pc |= page [PAGE_OFFSET( data )] << 8; - goto loop; - } - - case 0x00: // BRK - goto handle_brk; - -// Flags - - case 0x38: // SEC - c = (unsigned) ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: { - //dprintf( "CLI at %d\n", TIME ); - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) - { - if ( TIME < irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = irq_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - if ( delta >= s_time + 1 ) - { - s.base += s_time + 1; - s_time = -1; - goto loop; - } - - // TODO: implement - delayed_cli: - dprintf( "Delayed CLI not emulated\n" ); - goto loop; - } - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - dprintf( "Delayed SEI not emulated\n" ); - goto loop; - } - -// Unofficial - - // SKW - Skip word - case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: - HANDLE_PAGE_CROSSING( data + x ); - case 0x0C: - pc++; - // SKB - Skip byte - case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: - case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: - pc++; - goto loop; - - // NOP - case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: - goto loop; - - case bad_opcode: // HLT - pc--; - if ( pc > 0xFFFF ) - { - // handle wrap-around (assumes caller has put page of HLT at 0x10000) - pc &= 0xFFFF; - goto loop; - } - case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: - case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: - goto stop; - -// Unimplemented - - case 0xFF: // force 256-entry jump table for optimization purposes - c |= 1; - default: - check( (unsigned) opcode <= 0xFF ); - // skip over proper number of bytes - static unsigned char const illop_lens [8] = { - 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0 - }; - fuint8 opcode = instr [-1]; - fint16 len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; - if ( opcode == 0x9C ) - len = 2; - pc += len; - error_count_++; - - if ( (opcode >> 4) == 0x0B ) - { - if ( opcode == 0xB3 ) - data = READ_LOW( data ); - if ( opcode != 0xB7 ) - HANDLE_PAGE_CROSSING( data + y ); - } - goto loop; - } - assert( false ); - - int result_; -handle_brk: - pc++; - result_ = 4; - -interrupt: - { - s_time += 7; - - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); - - sp = (sp - 3) | 0x100; - fuint8 temp; - CALC_STATUS( temp ); - temp |= st_r; - if ( result_ ) - temp |= st_b; // TODO: incorrectly sets B flag for IRQ - WRITE_LOW( sp, temp ); - - this->r.status = status |= st_i; - blargg_long delta = s.base - end_time_; - if ( delta >= 0 ) goto loop; - s_time += delta; - s.base = end_time_; - goto loop; - } - -out_of_time: - pc--; - FLUSH_TIME(); - CPU_DONE( this, TIME, result_ ); - CACHE_TIME(); - if ( result_ >= 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - -stop: - - s.time = s_time; - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return s_time < 0; -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h deleted file mode 100644 index d303b57c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h +++ /dev/null @@ -1,114 +0,0 @@ -// NES 6502 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef NES_CPU_H -#define NES_CPU_H - -#include "blargg_common.h" - -typedef blargg_long nes_time_t; // clock cycle count -typedef unsigned nes_addr_t; // 16-bit address -enum { future_nes_time = LONG_MAX / 2 + 1 }; - -class Nes_Cpu { -public: - typedef BOOST::uint8_t uint8_t; - - // Clear registers, map low memory and its three mirrors to address 0, - // and mirror unmapped_page in remaining memory - void reset( void const* unmapped_page = 0 ); - - // Map code memory (memory accessed via the program counter). Start and size - // must be multiple of page_size. If mirror is true, repeats code page - // throughout address range. - enum { page_size = 0x800 }; - void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); - - // Access emulated memory as CPU does - uint8_t const* get_code( nes_addr_t ); - - // 2KB of RAM at address 0 - uint8_t low_mem [0x800]; - - // NES 6502 registers. Not kept updated during a call to run(). - struct registers_t { - BOOST::uint16_t pc; - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; - }; - registers_t r; - - // Set end_time and run CPU from current time. Returns true if execution - // stopped due to encountering bad_opcode. - bool run( nes_time_t end_time ); - - // Time of beginning of next instruction to be executed - nes_time_t time() const { return state->time + state->base; } - void set_time( nes_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } - - nes_time_t irq_time() const { return irq_time_; } - void set_irq_time( nes_time_t ); - - nes_time_t end_time() const { return end_time_; } - void set_end_time( nes_time_t ); - - // Number of undefined instructions encountered and skipped - void clear_error_count() { error_count_ = 0; } - unsigned long error_count() const { return error_count_; } - - // CPU invokes bad opcode handler if it encounters this - enum { bad_opcode = 0xF2 }; - -public: - Nes_Cpu() { state = &state_; } - enum { page_bits = 11 }; - enum { page_count = 0x10000 >> page_bits }; - enum { irq_inhibit = 0x04 }; -private: - struct state_t { - uint8_t const* code_map [page_count + 1]; - nes_time_t base; - int time; - }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - nes_time_t irq_time_; - nes_time_t end_time_; - unsigned long error_count_; - - void set_code_page( int, void const* ); - inline int update_end_time( nes_time_t end, nes_time_t irq ); -}; - -inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) -{ - return state->code_map [addr >> page_bits] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; -} - -inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) -{ - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - int delta = state->base - t; - state->base = t; - return delta; -} - -inline void Nes_Cpu::set_irq_time( nes_time_t t ) -{ - state->time += update_end_time( end_time_, (irq_time_ = t) ); -} - -inline void Nes_Cpu::set_end_time( nes_time_t t ) -{ - state->time += update_end_time( (end_time_ = t), irq_time_ ); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp deleted file mode 100644 index c058f6b1..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Nes_Fme7_Apu.h" - -#include - -/* 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; - osc_output->set_modified(); - - // 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; - synth.offset( last_time, delta, osc_output ); - } - } - - blip_time_t time = last_time + delays [index]; - if ( time < end_time ) - { - int delta = amp * 2 - volume; - 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 += (blargg_long) count * period; - } - } - - delays [index] = time - end_time; - } - - last_time = end_time; -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h deleted file mode 100644 index eb60af03..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h +++ /dev/null @@ -1,131 +0,0 @@ -// Sunsoft FME-7 sound emulator - -// Game_Music_Emu 0.5.2 -#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 output( Blip_Buffer* ); - enum { osc_count = 3 }; - void osc_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 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::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -inline Nes_Fme7_Apu::Nes_Fme7_Apu() -{ - 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/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp deleted file mode 100644 index f3235b38..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ - -#include "Nes_Namco_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_Namco_Apu::Nes_Namco_Apu() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -void Nes_Namco_Apu::reset() -{ - last_time = 0; - addr_reg = 0; - - int i; - for ( i = 0; i < reg_count; i++ ) - reg [i] = 0; - - for ( i = 0; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - osc.delay = 0; - osc.last_amp = 0; - osc.wave_pos = 0; - } -} - -void Nes_Namco_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -/* -void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) -{ - reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); - - static const char hex [17] = "0123456789ABCDEF"; - int i; - for ( i = 0; i < reg_count; i++ ) - reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); - - for ( i = 0; i < osc_count; i++ ) - { - reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); - reflect_int16( data, BLARGG_4CHAR('P','O','S','0') + i, &oscs [i].wave_pos ); - } -} -*/ - -void Nes_Namco_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) -{ - int active_oscs = (reg [0x7F] >> 4 & 7) + 1; - for ( int i = osc_count - active_oscs; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - Blip_Buffer* output = osc.output; - if ( !output ) - continue; - output->set_modified(); - - blip_resampled_time_t time = - output->resampled_time( last_time ) + osc.delay; - blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); - osc.delay = 0; - if ( time < end_time ) - { - const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; - if ( !(osc_reg [4] & 0xE0) ) - continue; - - int volume = osc_reg [7] & 15; - if ( !volume ) - continue; - - blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; - if ( freq < 64 * active_oscs ) - continue; // prevent low frequencies from excessively delaying freq changes - blip_resampled_time_t period = - output->resampled_duration( 983040 ) / freq * active_oscs; - - int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; - if ( !wave_size ) - continue; - - int last_amp = osc.last_amp; - int wave_pos = osc.wave_pos; - - do - { - // read wave sample - int addr = wave_pos + osc_reg [6]; - int sample = reg [addr >> 1] >> (addr << 2 & 4); - wave_pos++; - sample = (sample & 15) * volume; - - // output impulse if amplitude changed - int delta = sample - last_amp; - if ( delta ) - { - last_amp = sample; - synth.offset_resampled( time, delta, output ); - } - - // next sample - time += period; - if ( wave_pos >= wave_size ) - wave_pos = 0; - } - while ( time < end_time ); - - osc.wave_pos = wave_pos; - osc.last_amp = last_amp; - } - osc.delay = time - end_time; - } - - last_time = nes_end_time; -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h deleted file mode 100644 index db5fea4b..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h +++ /dev/null @@ -1,102 +0,0 @@ -// Namco 106 sound chip emulator - -// Nes_Snd_Emu 0.1.8 -#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 output( Blip_Buffer* ); - enum { osc_count = 8 }; - void osc_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 { - blargg_long 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 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 * 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::osc_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/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp deleted file mode 100644 index 1ad3f59c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp +++ /dev/null @@ -1,551 +0,0 @@ -// Nes_Snd_Emu 0.1.8. 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_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 += (blargg_long) 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; - } - - output->set_modified(); - - 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 ) { - 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; - - { - 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_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 += (blargg_long) 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; - } - - output->set_modified(); - - // 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 ) - 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; - } - - 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 byte const dac_table [128] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, - 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, - 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, - 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, - 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, - 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, - 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, - 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, -}; - -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 ) - { - int old_dac = dac; - dac = data & 0x7F; - - // adjust last_amp so that "pop" amplitude will be properly non-linear - // with respect to change in dac - int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); - if ( !nonlinear ) - last_amp = faked_nonlinear; - } -} - -void Nes_Dmc::start() -{ - reload_sample(); - fill_buffer(); - recalc_irq(); -} - -void Nes_Dmc::fill_buffer() -{ - if ( !buf_full && length_counter ) - { - require( prg_reader ); // prg_reader must be set - buf = prg_reader( prg_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( dac ); - if ( !output ) - { - silence = true; - } - else - { - output->set_modified(); - if ( delta ) - 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; - - do - { - if ( !silence ) - { - int step = (bits & 1) * 4 - 2; - bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) { - dac += step; - synth.offset_inline( time, step, 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->last_amp = 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; - } - - output->set_modified(); - - const int volume = this->volume(); - int amp = (noise & 1) ? volume : 0; - { - int delta = update_amp( amp ); - if ( delta ) - 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); - - 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/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h b/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h deleted file mode 100644 index b675bfb4..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h +++ /dev/null @@ -1,147 +0,0 @@ -// Private oscillators used by Nes_Apu - -// Nes_Snd_Emu 0.1.8 -#ifndef NES_OSCS_H -#define NES_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Nes_Apu; - -struct Nes_Osc -{ - 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 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 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 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; - - int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function - void* prg_reader_data; - - Nes_Apu* apu; - - Blip_Synth synth; - - 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/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp deleted file mode 100644 index d178407c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// Nes_Snd_Emu 0.1.8. 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" - -Nes_Vrc6_Apu::Nes_Vrc6_Apu() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -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; - } -} - -void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -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; - output->set_modified(); - - 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; - 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; - - 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/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h deleted file mode 100644 index 18722233..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h +++ /dev/null @@ -1,95 +0,0 @@ -// Konami VRC6 sound chip emulator - -// Nes_Snd_Emu 0.1.8 -#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 output( Blip_Buffer* ); - enum { osc_count = 3 }; - void osc_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) * 0x100L + regs [1] + 1; - } - }; - - Vrc6_Osc oscs [osc_count]; - blip_time_t last_time; - - Blip_Synth saw_synth; - Blip_Synth 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::osc_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/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp deleted file mode 100644 index 678bddb2..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp +++ /dev/null @@ -1,557 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Nsf_Emu.h" - -#include "blargg_endian.h" -#include -#include - -#if !NSF_EMU_APU_ONLY - #include "Nes_Namco_Apu.h" - #include "Nes_Vrc6_Apu.h" - #include "Nes_Fme7_Apu.h" -#endif - -/* 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" - -int const vrc6_flag = 0x01; -int const namco_flag = 0x10; -int const fme7_flag = 0x20; - -long const clock_divisor = 12; - -Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80 }; -Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80 }; - -int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr ) -{ - return *((Nsf_Emu*) emu)->cpu::get_code( addr ); -} - -Nsf_Emu::Nsf_Emu() -{ - vrc6 = 0; - namco = 0; - fme7 = 0; - - set_type( gme_nsf_type ); - set_silence_lookahead( 6 ); - apu.dmc_reader( pcm_read, this ); - Music_Emu::set_equalizer( nes_eq ); - set_gain( 1.4 ); - memset( unmapped_code, Nes_Cpu::bad_opcode, sizeof unmapped_code ); -} - -Nsf_Emu::~Nsf_Emu() { unload(); } - -void Nsf_Emu::unload() -{ - #if !NSF_EMU_APU_ONLY - { - delete vrc6; - vrc6 = 0; - - delete namco; - namco = 0; - - delete fme7; - fme7 = 0; - } - #endif - - rom.clear(); - Music_Emu::unload(); -} - -// Track info - -static void copy_nsf_fields( Nsf_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 ); - if ( h.chip_flags ) - Gme_File::copy_field_( out->system, "Famicom" ); -} - -blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const -{ - copy_nsf_fields( header_, out ); - return 0; -} - -static blargg_err_t check_nsf_header( void const* header ) -{ - if ( memcmp( header, "NESM\x1A", 5 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Nsf_File : Gme_Info_ -{ - Nsf_Emu::header_t h; - - Nsf_File() { set_type( gme_nsf_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - blargg_err_t err = in.read( &h, Nsf_Emu::header_size ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - - if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) - set_warning( "Uses unsupported audio expansion hardware" ); - - set_track_count( h.track_count ); - return check_nsf_header( &h ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_nsf_fields( h, out ); - return 0; - } -}; - -static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } -static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } - -gme_type_t_ const gme_nsf_type [1] = { "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }; - -// Setup - -void Nsf_Emu::set_tempo_( double t ) -{ - unsigned playback_rate = get_le16( header_.ntsc_speed ); - unsigned standard_rate = 0x411A; - clock_rate_ = 1789772.72727; - play_period = 262 * 341L * 4 - 2; // two fewer PPU clocks every four frames - - if ( pal_only ) - { - play_period = 33247 * clock_divisor; - clock_rate_ = 1662607.125; - standard_rate = 0x4E20; - playback_rate = get_le16( header_.pal_speed ); - } - - if ( !playback_rate ) - playback_rate = standard_rate; - - if ( playback_rate != standard_rate || t != 1.0 ) - play_period = long (playback_rate * clock_rate_ / (1000000.0 / clock_divisor * t)); - - apu.set_tempo( t ); -} - -blargg_err_t Nsf_Emu::init_sound() -{ - if ( header_.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) - set_warning( "Uses unsupported audio expansion hardware" ); - - { - #define APU_NAMES "Square 1", "Square 2", "Triangle", "Noise", "DMC" - - int const count = Nes_Apu::osc_count; - static const char* const apu_names [count] = { APU_NAMES }; - set_voice_count( count ); - set_voice_names( apu_names ); - - } - - static int const types [] = { - wave_type | 1, wave_type | 2, wave_type | 0, - noise_type | 0, mixed_type | 1, - wave_type | 3, wave_type | 4, wave_type | 5, - wave_type | 6, wave_type | 7, wave_type | 8, wave_type | 9, - wave_type |10, wave_type |11, wave_type |12, wave_type |13 - }; - set_voice_types( types ); // common to all sound chip configurations - - double adjusted_gain = gain(); - - #if NSF_EMU_APU_ONLY - { - if ( header_.chip_flags ) - set_warning( "Uses unsupported audio expansion hardware" ); - } - #else - { - if ( header_.chip_flags & (namco_flag | vrc6_flag | fme7_flag) ) - set_voice_count( Nes_Apu::osc_count + 3 ); - - if ( header_.chip_flags & namco_flag ) - { - namco = BLARGG_NEW Nes_Namco_Apu; - CHECK_ALLOC( namco ); - adjusted_gain *= 0.75; - - int const count = Nes_Apu::osc_count + Nes_Namco_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Wave 1", "Wave 2", "Wave 3", "Wave 4", - "Wave 5", "Wave 6", "Wave 7", "Wave 8" - }; - set_voice_count( count ); - set_voice_names( names ); - } - - if ( header_.chip_flags & vrc6_flag ) - { - vrc6 = BLARGG_NEW Nes_Vrc6_Apu; - CHECK_ALLOC( vrc6 ); - adjusted_gain *= 0.75; - - { - int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Saw Wave", "Square 3", "Square 4" - }; - set_voice_count( count ); - set_voice_names( names ); - } - - if ( header_.chip_flags & namco_flag ) - { - int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count + - Nes_Namco_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Saw Wave", "Square 3", "Square 4", - "Wave 1", "Wave 2", "Wave 3", "Wave 4", - "Wave 5", "Wave 6", "Wave 7", "Wave 8" - }; - set_voice_count( count ); - set_voice_names( names ); - } - } - - if ( header_.chip_flags & fme7_flag ) - { - fme7 = BLARGG_NEW Nes_Fme7_Apu; - CHECK_ALLOC( fme7 ); - adjusted_gain *= 0.75; - - int const count = Nes_Apu::osc_count + Nes_Fme7_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Square 3", "Square 4", "Square 5" - }; - set_voice_count( count ); - set_voice_names( names ); - } - - if ( namco ) namco->volume( adjusted_gain ); - if ( vrc6 ) vrc6 ->volume( adjusted_gain ); - if ( fme7 ) fme7 ->volume( adjusted_gain ); - } - #endif - - apu.volume( adjusted_gain ); - - return 0; -} - -blargg_err_t Nsf_Emu::load_( Data_Reader& in ) -{ - assert( offsetof (header_t,unused [4]) == header_size ); - RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); - - set_track_count( header_.track_count ); - RETURN_ERR( check_nsf_header( &header_ ) ); - - if ( header_.vers != 1 ) - set_warning( "Unknown file version" ); - - // sound and memory - blargg_err_t err = init_sound(); - if ( err ) - return err; - - // set up data - nes_addr_t load_addr = get_le16( header_.load_addr ); - init_addr = get_le16( header_.init_addr ); - play_addr = get_le16( header_.play_addr ); - if ( !load_addr ) load_addr = rom_begin; - if ( !init_addr ) init_addr = rom_begin; - if ( !play_addr ) play_addr = rom_begin; - if ( load_addr < rom_begin || init_addr < rom_begin ) - { - const char* w = warning(); - if ( !w ) - w = "Corrupt file (invalid load/init/play address)"; - return w; - } - - rom.set_addr( load_addr % bank_size ); - int total_banks = rom.size() / bank_size; - - // bank switching - int first_bank = (load_addr - rom_begin) / bank_size; - for ( int i = 0; i < bank_count; i++ ) - { - unsigned bank = i - first_bank; - if ( bank >= (unsigned) total_banks ) - bank = 0; - initial_banks [i] = bank; - - if ( header_.banks [i] ) - { - // bank-switched - memcpy( initial_banks, header_.banks, sizeof initial_banks ); - break; - } - } - - pal_only = (header_.speed_flags & 3) == 1; - - #if !NSF_EMU_EXTRA_FLAGS - header_.speed_flags = 0; - #endif - - set_tempo( tempo() ); - - return setup_buffer( (long) (clock_rate_ + 0.5) ); -} - -void Nsf_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); - - #if !NSF_EMU_APU_ONLY - { - if ( namco ) namco->treble_eq( eq ); - if ( vrc6 ) vrc6 ->treble_eq( eq ); - if ( fme7 ) fme7 ->treble_eq( eq ); - } - #endif -} - -void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) -{ - if ( i < Nes_Apu::osc_count ) - { - apu.osc_output( i, buf ); - return; - } - i -= Nes_Apu::osc_count; - - #if !NSF_EMU_APU_ONLY - { - if ( fme7 && i < Nes_Fme7_Apu::osc_count ) - { - fme7->osc_output( i, buf ); - return; - } - - if ( vrc6 ) - { - if ( i < Nes_Vrc6_Apu::osc_count ) - { - // put saw first - if ( --i < 0 ) - i = 2; - vrc6->osc_output( i, buf ); - return; - } - i -= Nes_Vrc6_Apu::osc_count; - } - - if ( namco && i < Nes_Namco_Apu::osc_count ) - { - namco->osc_output( i, buf ); - return; - } - } - #endif -} - -// Emulation - -// see nes_cpu_io.h for read/write functions - -void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) -{ - #if !NSF_EMU_APU_ONLY - { - if ( namco ) - { - switch ( addr ) - { - case Nes_Namco_Apu::data_reg_addr: - namco->write_data( time(), data ); - return; - - case Nes_Namco_Apu::addr_reg_addr: - namco->write_addr( data ); - return; - } - } - - if ( addr >= Nes_Fme7_Apu::latch_addr && fme7 ) - { - switch ( addr & Nes_Fme7_Apu::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 ( vrc6 ) - { - unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1); - unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step; - if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count ) - { - vrc6->write_osc( time(), osc, reg, data ); - return; - } - } - } - #endif - - // unmapped write - - #ifndef NDEBUG - { - // some games write to $8000 and $8001 repeatedly - if ( addr == 0x8000 || addr == 0x8001 ) return; - - // probably namco sound mistakenly turned on in mck - if ( addr == 0x4800 || addr == 0xF800 ) return; - - // memory mapper? - if ( addr == 0xFFF8 ) return; - - dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data ); - } - #endif -} - -blargg_err_t Nsf_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( low_mem, 0, sizeof low_mem ); - memset( sram, 0, sizeof sram ); - - cpu::reset( unmapped_code ); // also maps low_mem - cpu::map_code( sram_addr, sizeof sram, sram ); - for ( int i = 0; i < bank_count; ++i ) - cpu_write( bank_select_addr + i, initial_banks [i] ); - - apu.reset( pal_only, (header_.speed_flags & 0x20) ? 0x3F : 0 ); - apu.write_register( 0, 0x4015, 0x0F ); - apu.write_register( 0, 0x4017, (header_.speed_flags & 0x10) ? 0x80 : 0 ); - #if !NSF_EMU_APU_ONLY - { - if ( namco ) namco->reset(); - if ( vrc6 ) vrc6 ->reset(); - if ( fme7 ) fme7 ->reset(); - } - #endif - - play_ready = 4; - play_extra = 0; - next_play = play_period / clock_divisor; - - saved_state.pc = badop_addr; - low_mem [0x1FF] = (badop_addr - 1) >> 8; - low_mem [0x1FE] = (badop_addr - 1) & 0xFF; - r.sp = 0xFD; - r.pc = init_addr; - r.a = track; - r.x = pal_only; - - return 0; -} - -blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) -{ - set_time( 0 ); - while ( time() < duration ) - { - nes_time_t end = min( (blip_time_t) next_play, duration ); - end = min( end, time() + 32767 ); // allows CPU to use 16-bit time delta - if ( cpu::run( end ) ) - { - if ( r.pc != badop_addr ) - { - set_warning( "Emulation error (illegal instruction)" ); - r.pc++; - } - else - { - play_ready = 1; - if ( saved_state.pc != badop_addr ) - { - cpu::r = saved_state; - saved_state.pc = badop_addr; - } - else - { - set_time( end ); - } - } - } - - if ( time() >= next_play ) - { - nes_time_t period = (play_period + play_extra) / clock_divisor; - play_extra = play_period - period * clock_divisor; - next_play += period; - if ( play_ready && !--play_ready ) - { - check( saved_state.pc == badop_addr ); - if ( r.pc != badop_addr ) - saved_state = cpu::r; - - r.pc = play_addr; - low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8; - low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF; - GME_FRAME_HOOK( this ); - } - } - } - - if ( cpu::error_count() ) - { - cpu::clear_error_count(); - set_warning( "Emulation error (illegal instruction)" ); - } - - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - if ( next_play < 0 ) - next_play = 0; - - apu.end_frame( duration ); - - #if !NSF_EMU_APU_ONLY - { - if ( namco ) namco->end_frame( duration ); - if ( vrc6 ) vrc6 ->end_frame( duration ); - if ( fme7 ) fme7 ->end_frame( duration ); - } - #endif - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h deleted file mode 100644 index e06b9172..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h +++ /dev/null @@ -1,106 +0,0 @@ -// Nintendo NES/Famicom NSF music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef NSF_EMU_H -#define NSF_EMU_H - -#include "Classic_Emu.h" -#include "Nes_Apu.h" -#include "Nes_Cpu.h" - -class Nsf_Emu : private Nes_Cpu, public Classic_Emu { - typedef Nes_Cpu cpu; -public: - // Equalizer profiles for US NES and Japanese Famicom - static equalizer_t const nes_eq; - static equalizer_t const famicom_eq; - - // NSF file header - enum { header_size = 0x80 }; - struct header_t - { - 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]; - 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]; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_nsf_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - -public: - Nsf_Emu(); - ~Nsf_Emu(); - Nes_Apu* apu_() { return &apu; } -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); -protected: - enum { bank_count = 8 }; - byte initial_banks [bank_count]; - nes_addr_t init_addr; - nes_addr_t play_addr; - double clock_rate_; - bool pal_only; - - // timing - Nes_Cpu::registers_t saved_state; - nes_time_t next_play; - nes_time_t play_period; - int play_extra; - int play_ready; - - enum { rom_begin = 0x8000 }; - enum { bank_select_addr = 0x5FF8 }; - enum { bank_size = 0x1000 }; - Rom_Data rom; - -public: private: friend class Nes_Cpu; - void cpu_jsr( nes_addr_t ); - int cpu_read( nes_addr_t ); - void cpu_write( nes_addr_t, int ); - void cpu_write_misc( nes_addr_t, int ); - enum { badop_addr = bank_select_addr }; - -private: - class Nes_Namco_Apu* namco; - class Nes_Vrc6_Apu* vrc6; - class Nes_Fme7_Apu* fme7; - Nes_Apu apu; - static int pcm_read( void*, nes_addr_t ); - blargg_err_t init_sound(); - - header_t header_; - - enum { sram_addr = 0x6000 }; - byte sram [0x2000]; - byte unmapped_code [Nes_Cpu::page_size + 8]; -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp deleted file mode 100644 index 0a785e60..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Nsfe_Emu.h" - -#include "blargg_endian.h" -#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" - -Nsfe_Info::Nsfe_Info() { playlist_disabled = false; } - -Nsfe_Info::~Nsfe_Info() { } - -inline void Nsfe_Info::unload() -{ - track_name_data.clear(); - track_names.clear(); - playlist.clear(); - track_times.clear(); -} - -// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ? -void Nsfe_Info::disable_playlist( bool b ) -{ - playlist_disabled = b; - info.track_count = playlist.size(); - if ( !info.track_count || playlist_disabled ) - info.track_count = actual_track_count_; -} - -int Nsfe_Info::remap_track( int track ) const -{ - if ( !playlist_disabled && (unsigned) track < playlist.size() ) - track = playlist [track]; - return track; -} - -// Read multiple strings and separate into individual strings -static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector& chars, - blargg_vector& strs ) -{ - RETURN_ERR( chars.resize( size + 1 ) ); - chars [size] = 0; // in case last string doesn't have terminator - RETURN_ERR( in.read( &chars [0], size ) ); - - RETURN_ERR( strs.resize( 128 ) ); - int count = 0; - for ( int i = 0; i < size; i++ ) - { - if ( (int) strs.size() <= count ) - RETURN_ERR( strs.resize( count * 2 ) ); - strs [count++] = &chars [i]; - while ( i < size && chars [i] ) - i++; - } - - return strs.resize( count ); -} - -// Copy in to out, where out has out_max characters allocated. Truncate to -// out_max - 1 characters. -static void copy_str( const char* in, char* out, int out_max ) -{ - out [out_max - 1] = 0; - strncpy( out, in, out_max - 1 ); -} - -struct nsfe_info_t -{ - byte load_addr [2]; - byte init_addr [2]; - byte play_addr [2]; - byte speed_flags; - byte chip_flags; - byte track_count; - byte first_track; - byte unused [6]; -}; - -blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) -{ - int const nsfe_info_size = 16; - assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); - - // check header - byte signature [4]; - blargg_err_t err = in.read( signature, sizeof signature ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - if ( memcmp( signature, "NSFE", 4 ) ) - return gme_wrong_file_type; - - // free previous info - track_name_data.clear(); - track_names.clear(); - playlist.clear(); - track_times.clear(); - - // default nsf header - static const Nsf_Emu::header_t base_header = - { - {'N','E','S','M','\x1A'},// tag - 1, // version - 1, 1, // track count, first track - {0,0},{0,0},{0,0}, // addresses - "","","", // strings - {0x1A, 0x41}, // NTSC rate - {0,0,0,0,0,0,0,0}, // banks - {0x20, 0x4E}, // PAL rate - 0, 0, // flags - {0,0,0,0} // unused - }; - Nsf_Emu::header_t& header = info; - header = base_header; - - // parse tags - int phase = 0; - while ( phase != 3 ) - { - // read size and tag - byte block_header [2] [4]; - RETURN_ERR( in.read( block_header, sizeof block_header ) ); - blargg_long size = get_le32( block_header [0] ); - blargg_long tag = get_le32( block_header [1] ); - - //dprintf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); - - switch ( tag ) - { - case BLARGG_4CHAR('O','F','N','I'): { - check( phase == 0 ); - if ( size < 8 ) - return "Corrupt file"; - - nsfe_info_t finfo; - finfo.track_count = 1; - finfo.first_track = 0; - - RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) ); - if ( size > nsfe_info_size ) - RETURN_ERR( in.skip( size - nsfe_info_size ) ); - phase = 1; - info.speed_flags = finfo.speed_flags; - info.chip_flags = finfo.chip_flags; - info.track_count = finfo.track_count; - this->actual_track_count_ = finfo.track_count; - info.first_track = finfo.first_track; - memcpy( info.load_addr, finfo.load_addr, 2 * 3 ); - break; - } - - case BLARGG_4CHAR('K','N','A','B'): - if ( size > (int) sizeof info.banks ) - return "Corrupt file"; - RETURN_ERR( in.read( info.banks, size ) ); - break; - - case BLARGG_4CHAR('h','t','u','a'): { - blargg_vector chars; - blargg_vector strs; - RETURN_ERR( read_strs( in, size, chars, strs ) ); - int n = strs.size(); - - if ( n > 3 ) - copy_str( strs [3], info.dumper, sizeof info.dumper ); - - if ( n > 2 ) - copy_str( strs [2], info.copyright, sizeof info.copyright ); - - if ( n > 1 ) - copy_str( strs [1], info.author, sizeof info.author ); - - if ( n > 0 ) - copy_str( strs [0], info.game, sizeof info.game ); - - break; - } - - case BLARGG_4CHAR('e','m','i','t'): - RETURN_ERR( track_times.resize( size / 4 ) ); - RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); - break; - - case BLARGG_4CHAR('l','b','l','t'): - RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); - break; - - case BLARGG_4CHAR('t','s','l','p'): - RETURN_ERR( playlist.resize( size ) ); - RETURN_ERR( in.read( &playlist [0], size ) ); - break; - - case BLARGG_4CHAR('A','T','A','D'): { - check( phase == 1 ); - phase = 2; - if ( !nsf_emu ) - { - RETURN_ERR( in.skip( size ) ); - } - else - { - Subset_Reader sub( &in, size ); // limit emu to nsf data - Remaining_Reader rem( &header, Nsf_Emu::header_size, &sub ); - RETURN_ERR( nsf_emu->load( rem ) ); - check( rem.remain() == 0 ); - } - break; - } - - case BLARGG_4CHAR('D','N','E','N'): - check( phase == 2 ); - phase = 3; - break; - - default: - // tags that can be skipped start with a lowercase character - check( islower( (tag >> 24) & 0xFF ) ); - RETURN_ERR( in.skip( size ) ); - break; - } - } - - return 0; -} - -blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const -{ - int remapped = remap_track( track ); - if ( (unsigned) remapped < track_times.size() ) - { - long length = (BOOST::int32_t) get_le32( track_times [remapped] ); - if ( length > 0 ) - out->length = length; - } - if ( (unsigned) remapped < track_names.size() ) - Gme_File::copy_field_( out->song, track_names [remapped] ); - - GME_COPY_FIELD( info, out, game ); - GME_COPY_FIELD( info, out, author ); - GME_COPY_FIELD( info, out, copyright ); - GME_COPY_FIELD( info, out, dumper ); - return 0; -} - -Nsfe_Emu::Nsfe_Emu() -{ - loading = false; - set_type( gme_nsfe_type ); -} - -Nsfe_Emu::~Nsfe_Emu() { } - -void Nsfe_Emu::unload() -{ - if ( !loading ) - info.unload(); // TODO: extremely hacky! - Nsf_Emu::unload(); -} - -blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const -{ - return info.track_info_( out, track ); -} - -struct Nsfe_File : Gme_Info_ -{ - Nsfe_Info info; - - Nsfe_File() { set_type( gme_nsfe_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - RETURN_ERR( info.load( in, 0 ) ); - info.disable_playlist( false ); - set_track_count( info.info.track_count ); - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int track ) const - { - return info.track_info_( out, track ); - } -}; - -static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } -static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } - -gme_type_t_ const gme_nsfe_type [1] = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }; - -blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) -{ - if ( loading ) - return Nsf_Emu::load_( in ); - - // TODO: this hacky recursion-avoidance could have subtle problems - loading = true; - blargg_err_t err = info.load( in, this ); - loading = false; - disable_playlist( false ); - return err; -} - -void Nsfe_Emu::disable_playlist( bool b ) -{ - info.disable_playlist( b ); - set_track_count( info.info.track_count ); -} - -void Nsfe_Emu::clear_playlist_() -{ - disable_playlist(); - Nsf_Emu::clear_playlist_(); -} - -blargg_err_t Nsfe_Emu::start_track_( int track ) -{ - return Nsf_Emu::start_track_( info.remap_track( track ) ); -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h deleted file mode 100644 index 561c3be0..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h +++ /dev/null @@ -1,68 +0,0 @@ -// Nintendo NES/Famicom NSFE music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef NSFE_EMU_H -#define NSFE_EMU_H - -#include "blargg_common.h" -#include "Nsf_Emu.h" - -// Allows reading info from NSFE file without creating emulator -class Nsfe_Info { -public: - blargg_err_t load( Data_Reader&, Nsf_Emu* ); - - struct info_t : Nsf_Emu::header_t - { - char game [256]; - char author [256]; - char copyright [256]; - char dumper [256]; - } info; - - 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(); - - Nsfe_Info(); - ~Nsfe_Info(); -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; } - -public: - // deprecated - struct header_t { char tag [4]; }; - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - void disable_playlist( bool = true ); // use clear_playlist() - -public: - Nsfe_Emu(); - ~Nsfe_Emu(); -protected: - blargg_err_t load_( Data_Reader& ); - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t start_track_( int ); - void unload(); - void clear_playlist_(); -private: - Nsfe_Info info; - bool loading; -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp deleted file mode 100644 index 23fa9072..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Sap_Apu.h" - -#include - -/* 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 max_frequency = 12000; // pure waves above this frequency are silenced - -static void gen_poly( blargg_ulong mask, int count, byte* out ) -{ - blargg_ulong 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; -blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; -blargg_ulong const poly5 = 0x167C6EA1; - -inline blargg_ulong run_poly5( blargg_ulong in, int shift ) -{ - return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); -} - -#define POLY_MASK( width, tap1, tap2 ) \ - ((1UL << (width - 1 - tap1)) | (1UL << (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 ); - blargg_ulong n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L + - poly5 [1] * 0x100L + poly5 [0]; - blargg_ulong rev = n & 1; - for ( int i = 1; i < poly5_len; i++ ) - rev |= (n >> i & 1) << (poly5_len - i); - dprintf( "poly5: 0x%08lX\n", rev ); - } -} - -Sap_Apu::Sap_Apu() -{ - impl = 0; - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, 0 ); -} - -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 - blargg_long 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 * 0x100L + 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 ) - { - output->set_modified(); - - 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; - 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 - blargg_ulong 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; - } - - // 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 ) - { - blargg_long 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, unsigned addr, int data ) -{ - run_until( time ); - int i = (addr ^ 0xD200) >> 1; - if ( 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; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h deleted file mode 100644 index c71ce31e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h +++ /dev/null @@ -1,77 +0,0 @@ -// Atari POKEY sound chip emulator - -// Game_Music_Emu 0.5.2 -#ifndef SAP_APU_H -#define SAP_APU_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Sap_Apu_Impl; - -class Sap_Apu { -public: - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* ); - - void reset( Sap_Apu_Impl* ); - - enum { start_addr = 0xD200 }; - enum { end_addr = 0xD209 }; - void write_data( blip_time_t, unsigned addr, int data ); - - void end_frame( blip_time_t ); - -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 = (1L << 4) - 1 }; - enum { poly9_len = (1L << 9) - 1 }; - enum { poly17_len = (1L << 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: - Blip_Synth synth; - - Sap_Apu_Impl(); - void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } - -private: - typedef unsigned char byte; - byte poly4 [Sap_Apu::poly4_len / 8 + 1]; - byte poly9 [Sap_Apu::poly9_len / 8 + 1]; - byte poly17 [Sap_Apu::poly17_len / 8 + 1]; - friend class Sap_Apu; -}; - -inline void Sap_Apu::osc_output( int i, Blip_Buffer* b ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = b; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp deleted file mode 100644 index 10dc6061..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp +++ /dev/null @@ -1,1011 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Sap_Cpu.h" - -#include -#include "blargg_endian.h" - -//#include "nes_cpu_log.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 */ - -#define FLUSH_TIME() (void) (s.time = s_time) -#define CACHE_TIME() (void) (s_time = s.time) - -#include "sap_cpu_io.h" - -#ifndef CPU_DONE - #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } -#endif - -#include "blargg_source.h" - -int const st_n = 0x80; -int const st_v = 0x40; -int const st_r = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; - -void Sap_Cpu::reset( void* new_mem ) -{ - check( state == &state_ ); - state = &state_; - mem = (uint8_t*) new_mem; - r.status = st_i; - r.sp = 0xFF; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - state_.time = 0; - state_.base = 0; - irq_time_ = future_sap_time; - end_time_ = future_sap_time; - - blargg_verify_byte_order(); -} - -#define TIME (s_time + s.base) -#define READ( addr ) CPU_READ( this, (addr), TIME ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} -#define READ_LOW( addr ) (mem [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) -#define READ_PROG( addr ) (READ_LOW( addr )) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - -bool Sap_Cpu::run( sap_time_t end_time ) -{ - bool illegal_encountered = false; - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - fint32 s_time = s.time; - uint8_t* const mem = this->mem; // cache - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; - SET_SP( r.sp ); - - // status flags - #define IS_NEG (nz & 0x8080) - - #define CALC_STATUS( out ) do {\ - out = status & (st_v | st_d | st_i);\ - out |= ((nz >> 8) | nz) & st_n;\ - out |= c >> 8 & st_c;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & (st_v | st_d | st_i);\ - nz = in << 8;\ - c = nz;\ - nz |= ~in & st_z;\ - } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 - { - fuint8 temp = r.status; - SET_STATUS( temp ); - } - - goto loop; -dec_clock_loop: - s_time--; -loop: - - #ifndef NDEBUG - { - sap_time_t correct = end_time_; - if ( !(status & st_i) && correct > irq_time_ ) - correct = irq_time_; - check( s.base == correct ); - } - #endif - - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - fuint8 opcode = mem [pc]; - pc++; - uint8_t const* instr = mem + pc; - - static uint8_t const clock_table [256] = - {// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 - 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 - 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 - 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 - 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A - 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F - }; // 0x00 was 7 - - fuint16 data; - data = clock_table [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = *instr; - - #ifdef NES_CPU_LOG_H - nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Macros - -#define GET_MSB() (instr [1]) -#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) -#define GET_ADDR() GET_LE16( instr ) - -#define NO_PAGE_CROSSING( lsb ) -#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; - -#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - -#define IND_Y( cross, out ) {\ - fuint16 temp = READ_LOW( data ) + y;\ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - cross( temp );\ - } - -#define IND_X( out ) {\ - fuint16 temp = data + x;\ - out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ - } - -#define ARITH_ADDR_MODES( op )\ -case op - 0x04: /* (ind,x) */\ - IND_X( data )\ - goto ptr##op;\ -case op + 0x0C: /* (ind),y */\ - IND_Y( HANDLE_PAGE_CROSSING, data )\ - goto ptr##op;\ -case op + 0x10: /* zp,X */\ - data = uint8_t (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:\ - HANDLE_PAGE_CROSSING( data );\ -case op + 0x08: /* abs */\ - ADD_PAGE();\ -ptr##op:\ - FLUSH_TIME();\ - data = READ( data );\ - CACHE_TIME();\ -case op + 0x04: /* imm */\ -imm##op: - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ -{\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop;\ - pc += offset;\ - s_time += extra_clock >> 8 & 1;\ - goto loop;\ -} - -// Often-Used - - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (data + x) ); - pc++; - goto loop; - - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - fuint16 temp = pc + 1; - pc = GET_ADDR(); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x4C: // JMP abs - pc = GET_ADDR(); - goto loop; - - case 0xE8: // INX - INC_DEC_XY( x, 1 ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ); - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xC8: // INY - INC_DEC_XY( y, 1 ) - - case 0xA8: // TAY - y = a; - nz = a; - goto loop; - - case 0x98: // TYA - a = y; - nz = y; - goto loop; - - case 0xAD:{// LDA abs - unsigned addr = GET_ADDR(); - pc += 2; - nz = READ( addr ); - a = nz; - goto loop; - } - - case 0x60: // RTS - pc = 1 + READ_LOW( sp ); - pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); - sp = (sp - 0xFE) | 0x100; - goto loop; - - { - fuint16 addr; - - case 0x99: // STA abs,Y - addr = y + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x8D: // STA abs - addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x9D: // STA abs,X (slightly more common than STA abs) - addr = x + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - sta_ptr: - FLUSH_TIME(); - WRITE( addr, a ); - CACHE_TIME(); - goto loop; - - case 0x91: // STA (ind),Y - IND_Y( NO_PAGE_CROSSING, addr ) - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - IND_X( addr ) - pc++; - goto sta_ptr; - - } - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - - // common read instructions - { - fuint16 addr; - - case 0xA1: // LDA (ind,X) - IND_X( addr ) - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - HANDLE_PAGE_CROSSING( addr ); - addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); - pc++; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - HANDLE_PAGE_CROSSING( data + y ); - addr = GET_ADDR() + y; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xBD: // LDA abs,X - HANDLE_PAGE_CROSSING( data + x ); - addr = GET_ADDR() + x; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - a_nz_read_addr: - FLUSH_TIME(); - a = nz = READ( addr ); - CACHE_TIME(); - goto loop; - - } - -// Branch - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - -// Load/store - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (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 = uint8_t (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; - HANDLE_PAGE_CROSSING( data ); - case 0xAC:{// LDY abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xBE: // LDX abs,y - data += y; - HANDLE_PAGE_CROSSING( data ); - case 0xAE:{// LDX abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - x = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - unsigned addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, temp ); - goto loop; - } - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( 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 &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( 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 &= 0xFF; - goto loop; - -// Logical - - 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; - - case 0x2C:{// BIT abs - unsigned addr = GET_ADDR(); - pc += 2; - status &= ~st_v; - nz = READ( addr ); - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - } - - case 0x24: // BIT zp - nz = READ_LOW( data ); - pc++; - status &= ~st_v; - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - case 0xEB: // unofficial equivalent - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - check( !(status & st_d) ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= ov >> 2 & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) 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 = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - ADD_PAGE(); - FLUSH_TIME(); - int temp = READ( 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(); - nz = c >> 8 & 1; - FLUSH_TIME(); - nz |= (c = READ( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (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 = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (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 - - case 0xCA: // DEX - INC_DEC_XY( x, -1 ) - - case 0x88: // DEY - INC_DEC_XY( y, -1 ) - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = (unsigned) -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 = (unsigned) -1; - inc_common: - FLUSH_TIME(); - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xAA: // TAX - x = a; - nz = a; - goto loop; - - case 0x8A: // TXA - a = x; - 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; - -// Stack - - case 0x48: // PHA - PUSH( a ); // verified - goto loop; - - case 0x68: // PLA - a = nz = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - goto loop; - - case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - this->r.status = status; // update externally-visible I flag - if ( (data ^ status) & st_i ) - { - sap_time_t new_time = end_time_; - if ( !(status & st_i) && new_time > irq_time_ ) - new_time = irq_time_; - blargg_long delta = s.base - new_time; - s.base = new_time; - s_time += delta; - } - goto loop; - } - - case 0x28:{// PLP - fuint8 temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - fuint8 changed = status ^ temp; - SET_STATUS( temp ); - if ( !(changed & st_i) ) - goto loop; // I flag didn't change - if ( status & st_i ) - goto handle_sei; - goto handle_cli; - } - - case 0x08: { // PHP - fuint8 temp; - CALC_STATUS( temp ); - PUSH( temp | (st_b | st_r) ); - goto loop; - } - - case 0x6C:{// JMP (ind) - data = GET_ADDR(); - pc = READ_PROG( data ); - data = (data & 0xFF00) | ((data + 1) & 0xFF); - pc |= 0x100 * READ_PROG( data ); - goto loop; - } - - case 0x00: // BRK - goto handle_brk; - -// Flags - - case 0x38: // SEC - c = (unsigned) ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) - { - if ( TIME < irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = 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; - irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop - goto loop; - } - delayed_cli: - dprintf( "Delayed CLI not emulated\n" ); - goto loop; - } - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - dprintf( "Delayed SEI not emulated\n" ); - goto loop; - } - -// Unofficial - - // SKW - Skip word - case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: - HANDLE_PAGE_CROSSING( data + x ); - case 0x0C: - pc++; - // SKB - Skip byte - case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: - case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: - pc++; - goto loop; - - // NOP - case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: - goto loop; - -// Unimplemented - - // halt - //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: - //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: - - default: - assert( (unsigned) opcode <= 0xFF ); - illegal_encountered = true; - pc--; - goto stop; - } - assert( false ); - - int result_; -handle_brk: - if ( (pc - 1) >= idle_addr ) - goto idle_done; - pc++; - result_ = 4; - dprintf( "BRK executed\n" ); - -interrupt: - { - s_time += 7; - - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); - - sp = (sp - 3) | 0x100; - fuint8 temp; - CALC_STATUS( temp ); - temp |= st_r; - if ( result_ ) - temp |= st_b; // TODO: incorrectly sets B flag for IRQ - WRITE_LOW( sp, temp ); - - status &= ~st_d; - status |= st_i; - this->r.status = status; // update externally-visible I flag - - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - goto loop; - } - -idle_done: - //s_time = 0; - pc--; - goto stop; -out_of_time: - pc--; - FLUSH_TIME(); - CPU_DONE( this, TIME, result_ ); - CACHE_TIME(); - if ( result_ >= 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - -stop: - - s.time = s_time; - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return illegal_encountered; -} - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h deleted file mode 100644 index 712f63cd..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h +++ /dev/null @@ -1,83 +0,0 @@ -// Atari 6502 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef SAP_CPU_H -#define SAP_CPU_H - -#include "blargg_common.h" - -typedef blargg_long sap_time_t; // clock cycle count -typedef unsigned sap_addr_t; // 16-bit address -enum { future_sap_time = LONG_MAX / 2 + 1 }; - -class Sap_Cpu { -public: - typedef BOOST::uint8_t uint8_t; - - // Clear all registers and keep pointer to 64K memory passed in - void reset( void* mem_64k ); - - // Run until specified time is reached. Returns true if suspicious/unsupported - // instruction was encountered at any point during run. - bool run( sap_time_t end_time ); - - // Registers are not updated until run() returns (except I flag in status) - struct registers_t { - BOOST::uint16_t pc; - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; - }; - registers_t r; - - enum { idle_addr = 0xFEFF }; - - // Time of beginning of next instruction to be executed - sap_time_t time() const { return state->time + state->base; } - void set_time( sap_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } - - sap_time_t irq_time() const { return irq_time_; } - void set_irq_time( sap_time_t ); - - sap_time_t end_time() const { return end_time_; } - void set_end_time( sap_time_t ); - -public: - Sap_Cpu() { state = &state_; } - enum { irq_inhibit = 0x04 }; -private: - struct state_t { - sap_time_t base; - sap_time_t time; - }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - sap_time_t irq_time_; - sap_time_t end_time_; - uint8_t* mem; - - inline sap_time_t update_end_time( sap_time_t end, sap_time_t irq ); -}; - -inline sap_time_t Sap_Cpu::update_end_time( sap_time_t t, sap_time_t irq ) -{ - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - sap_time_t delta = state->base - t; - state->base = t; - return delta; -} - -inline void Sap_Cpu::set_irq_time( sap_time_t t ) -{ - state->time += update_end_time( end_time_, (irq_time_ = t) ); -} - -inline void Sap_Cpu::set_end_time( sap_time_t t ) -{ - state->time += update_end_time( (end_time_ = t), irq_time_ ); -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp deleted file mode 100644 index 8314fd6e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp +++ /dev/null @@ -1,442 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Sap_Emu.h" - -#include "blargg_endian.h" -#include - -/* 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" - -long const base_scanline_period = 114; - -Sap_Emu::Sap_Emu() -{ - set_type( gme_sap_type ); - - static const char* const names [Sap_Apu::osc_count * 2] = { - "Wave 1", "Wave 2", "Wave 3", "Wave 4", - "Wave 5", "Wave 6", "Wave 7", "Wave 8", - }; - set_voice_names( names ); - - static int const types [Sap_Apu::osc_count * 2] = { - wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, - wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4, - }; - set_voice_types( types ); - set_silence_lookahead( 6 ); -} - -Sap_Emu::~Sap_Emu() { } - -// Track info - -// Returns 16 or greater if not hex -inline int from_hex_char( int h ) -{ - h -= 0x30; - if ( (unsigned) h > 9 ) - h = ((h - 0x11) & 0xDF) + 10; - return h; -} - -static long from_hex( byte const* in ) -{ - unsigned result = 0; - for ( int n = 4; n--; ) - { - int h = from_hex_char( *in++ ); - if ( h > 15 ) - return -1; - result = result * 0x10 + h; - } - return result; -} - -static int from_dec( byte const* in, byte const* end ) -{ - if ( in >= end ) - return -1; - - int n = 0; - while ( in < end ) - { - int dig = *in++ - '0'; - if ( (unsigned) dig > 9 ) - return -1; - n = n * 10 + dig; - } - return n; -} - -static void parse_string( byte const* in, byte const* end, int len, char* out ) -{ - byte const* start = in; - if ( *in++ == '\"' ) - { - start++; - while ( in < end && *in != '\"' ) - in++; - } - else - { - in = end; - } - len = min( len - 1, int (in - start) ); - out [len] = 0; - memcpy( out, start, len ); -} - -static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out ) -{ - out->track_count = 1; - out->author [0] = 0; - out->name [0] = 0; - out->copyright [0] = 0; - - if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) - return gme_wrong_file_type; - - byte const* file_end = in + size - 5; - in += 5; - while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) - { - byte const* line_end = in; - while ( line_end < file_end && *line_end != 0x0D ) - line_end++; - - char const* tag = (char const*) in; - while ( in < line_end && *in > ' ' ) - in++; - int tag_len = (char const*) in - tag; - - while ( in < line_end && *in <= ' ' ) in++; - - if ( tag_len <= 0 ) - { - // skip line - } - else if ( !strncmp( "INIT", tag, tag_len ) ) - { - out->init_addr = from_hex( in ); - if ( (unsigned long) out->init_addr > 0xFFFF ) - return "Invalid init address"; - } - else if ( !strncmp( "PLAYER", tag, tag_len ) ) - { - out->play_addr = from_hex( in ); - if ( (unsigned long) out->play_addr > 0xFFFF ) - return "Invalid play address"; - } - else if ( !strncmp( "MUSIC", tag, tag_len ) ) - { - out->music_addr = from_hex( in ); - if ( (unsigned long) out->music_addr > 0xFFFF ) - return "Invalid music address"; - } - else if ( !strncmp( "SONGS", tag, tag_len ) ) - { - out->track_count = from_dec( in, line_end ); - if ( out->track_count <= 0 ) - return "Invalid track count"; - } - else if ( !strncmp( "TYPE", tag, tag_len ) ) - { - switch ( out->type = *in ) - { - case 'C': - case 'B': - break; - - case 'D': - return "Digimusic not supported"; - - default: - return "Unsupported player type"; - } - } - else if ( !strncmp( "STEREO", tag, tag_len ) ) - { - out->stereo = true; - } - else if ( !strncmp( "FASTPLAY", tag, tag_len ) ) - { - out->fastplay = from_dec( in, line_end ); - if ( out->fastplay <= 0 ) - return "Invalid fastplay value"; - } - else if ( !strncmp( "AUTHOR", tag, tag_len ) ) - { - parse_string( in, line_end, sizeof out->author, out->author ); - } - else if ( !strncmp( "NAME", tag, tag_len ) ) - { - parse_string( in, line_end, sizeof out->name, out->name ); - } - else if ( !strncmp( "DATE", tag, tag_len ) ) - { - parse_string( in, line_end, sizeof out->copyright, out->copyright ); - } - - in = line_end + 2; - } - - if ( in [0] != 0xFF || in [1] != 0xFF ) - return "ROM data missing"; - out->rom_data = in + 2; - - return 0; -} - -static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out ) -{ - Gme_File::copy_field_( out->game, in.name ); - Gme_File::copy_field_( out->author, in.author ); - Gme_File::copy_field_( out->copyright, in.copyright ); -} - -blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const -{ - copy_sap_fields( info, out ); - return 0; -} - -struct Sap_File : Gme_Info_ -{ - Sap_Emu::info_t info; - - Sap_File() { set_type( gme_sap_type ); } - - blargg_err_t load_mem_( byte const* begin, long size ) - { - RETURN_ERR( parse_info( begin, size, &info ) ); - set_track_count( info.track_count ); - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_sap_fields( info, out ); - return 0; - } -}; - -static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } -static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } - -gme_type_t_ const gme_sap_type [1] = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }; - -// Setup - -blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) -{ - file_end = in + size; - - info.warning = 0; - info.type = 'B'; - info.stereo = false; - info.init_addr = -1; - info.play_addr = -1; - info.music_addr = -1; - info.fastplay = 312; - RETURN_ERR( parse_info( in, size, &info ) ); - - set_warning( info.warning ); - set_track_count( info.track_count ); - set_voice_count( Sap_Apu::osc_count << info.stereo ); - apu_impl.volume( gain() ); - - return setup_buffer( 1773447 ); -} - -void Sap_Emu::update_eq( blip_eq_t const& eq ) -{ - apu_impl.synth.treble_eq( eq ); -} - -void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - int i2 = i - Sap_Apu::osc_count; - if ( i2 >= 0 ) - apu2.osc_output( i2, right ); - else - apu.osc_output( i, (info.stereo ? left : center) ); -} - -// Emulation - -void Sap_Emu::set_tempo_( double t ) -{ - scanline_period = sap_time_t (base_scanline_period / t); -} - -inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; } - -void Sap_Emu::cpu_jsr( sap_addr_t addr ) -{ - check( r.sp >= 0xFE ); // catch anything trying to leave data on stack - r.pc = addr; - int high_byte = (idle_addr - 1) >> 8; - if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte ) - r.sp = 0xFF; // pop extra byte off - mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return - mem.ram [0x100 + r.sp--] = high_byte; - mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF; -} - -void Sap_Emu::run_routine( sap_addr_t addr ) -{ - cpu_jsr( addr ); - cpu::run( 312 * base_scanline_period * 60 ); - check( r.pc == idle_addr ); -} - -inline void Sap_Emu::call_init( int track ) -{ - switch ( info.type ) - { - case 'B': - r.a = track; - run_routine( info.init_addr ); - break; - - case 'C': - r.a = 0x70; - r.x = info.music_addr&0xFF; - r.y = info.music_addr >> 8; - run_routine( info.play_addr + 3 ); - r.a = 0; - r.x = track; - run_routine( info.play_addr + 3 ); - break; - } -} - -blargg_err_t Sap_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( &mem, 0, sizeof mem ); - - byte const* in = info.rom_data; - while ( file_end - in >= 5 ) - { - unsigned start = get_le16( in ); - unsigned end = get_le16( in + 2 ); - //dprintf( "Block $%04X-$%04X\n", start, end ); - in += 4; - if ( end < start ) - { - set_warning( "Invalid file data block" ); - break; - } - long len = end - start + 1; - if ( len > file_end - in ) - { - set_warning( "Invalid file data block" ); - break; - } - - memcpy( mem.ram + start, in, len ); - in += len; - if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) - in += 2; - } - - apu.reset( &apu_impl ); - apu2.reset( &apu_impl ); - cpu::reset( mem.ram ); - time_mask = 0; // disables sound during init - call_init( track ); - time_mask = -1; - - next_play = play_period(); - - return 0; -} - -// Emulation - -// see sap_cpu_io.h for read/write functions - -void Sap_Emu::cpu_write_( sap_addr_t addr, int data ) -{ - if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) ) - { - GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data ); - apu.write_data( time() & time_mask, addr, data ); - return; - } - - if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && - info.stereo ) - { - GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data ); - apu2.write_data( time() & time_mask, addr ^ 0x10, data ); - return; - } - - if ( (addr & ~0x0010) != 0xD20F || data != 0x03 ) - dprintf( "Unmapped write $%04X <- $%02X\n", addr, data ); -} - -inline void Sap_Emu::call_play() -{ - switch ( info.type ) - { - case 'B': - cpu_jsr( info.play_addr ); - break; - - case 'C': - cpu_jsr( info.play_addr + 6 ); - break; - } -} - -blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) -{ - set_time( 0 ); - while ( time() < duration ) - { - if ( cpu::run( duration ) || r.pc > idle_addr ) - return "Emulation error (illegal instruction)"; - - if ( r.pc == idle_addr ) - { - if ( next_play <= duration ) - { - set_time( next_play ); - next_play += play_period(); - call_play(); - GME_FRAME_HOOK( this ); - } - else - { - set_time( duration ); - } - } - } - - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - if ( next_play < 0 ) - next_play = 0; - apu.end_frame( duration ); - if ( info.stereo ) - apu2.end_frame( duration ); - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h deleted file mode 100644 index 4878faa6..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h +++ /dev/null @@ -1,69 +0,0 @@ -// Atari XL/XE SAP music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef SAP_EMU_H -#define SAP_EMU_H - -#include "Classic_Emu.h" -#include "Sap_Apu.h" -#include "Sap_Cpu.h" - -class Sap_Emu : private Sap_Cpu, public Classic_Emu { - typedef Sap_Cpu cpu; -public: - static gme_type_t static_type() { return gme_sap_type; } -public: - Sap_Emu(); - ~Sap_Emu(); - struct info_t { - byte const* rom_data; - const char* warning; - long init_addr; - long play_addr; - long music_addr; - int type; - int track_count; - int fastplay; - bool stereo; - char author [256]; - char name [256]; - char copyright [ 32]; - }; -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); -public: private: friend class Sap_Cpu; - int cpu_read( sap_addr_t ); - void cpu_write( sap_addr_t, int ); - void cpu_write_( sap_addr_t, int ); -private: - info_t info; - - byte const* file_end; - sap_time_t scanline_period; - sap_time_t next_play; - sap_time_t time_mask; - Sap_Apu apu; - Sap_Apu apu2; - - // large items - struct { - byte padding1 [0x100]; - byte ram [0x10000]; - byte padding2 [0x100]; - } mem; - Sap_Apu_Impl apu_impl; - - sap_time_t play_period() const; - void call_play(); - void cpu_jsr( sap_addr_t ); - void call_init( int track ); - void run_routine( sap_addr_t ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp deleted file mode 100644 index b41fdec4..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/ - -#include "Sms_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" - -// Sms_Osc - -Sms_Osc::Sms_Osc() -{ - output = 0; - outputs [0] = 0; // always stays NULL - outputs [1] = 0; - outputs [2] = 0; - outputs [3] = 0; -} - -void Sms_Osc::reset() -{ - delay = 0; - last_amp = 0; - volume = 0; - output_select = 3; - output = outputs [3]; -} - -// Sms_Square - -inline void Sms_Square::reset() -{ - period = 0; - phase = 0; - Sms_Osc::reset(); -} - -void Sms_Square::run( blip_time_t time, blip_time_t end_time ) -{ - if ( !volume || period <= 128 ) - { - // ignore 16kHz and higher - if ( last_amp ) - { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - time += delay; - if ( !period ) - { - time = end_time; - } - else if ( time < end_time ) - { - // keep calculating phase - int count = (end_time - time + period - 1) / period; - phase = (phase + count) & 1; - time += count * period; - } - } - else - { - int amp = phase ? volume : -volume; - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - int delta = amp * 2; - do - { - delta = -delta; - synth->offset_inline( time, delta, output ); - time += period; - phase ^= 1; - } - while ( time < end_time ); - this->last_amp = phase ? volume : -volume; - } - } - delay = time - end_time; -} - -// Sms_Noise - -static int const noise_periods [3] = { 0x100, 0x200, 0x400 }; - -inline void Sms_Noise::reset() -{ - period = &noise_periods [0]; - shifter = 0x8000; - feedback = 0x9000; - Sms_Osc::reset(); -} - -void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) -{ - int amp = volume; - if ( shifter & 1 ) - amp = -amp; - - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth.offset( time, delta, output ); - } - } - - time += delay; - if ( !volume ) - time = end_time; - - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - unsigned shifter = this->shifter; - int delta = amp * 2; - int period = *this->period * 2; - if ( !period ) - period = 16; - - do - { - int changed = shifter + 1; - shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1); - if ( changed & 2 ) // true if bits 0 and 1 differ - { - delta = -delta; - synth.offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->shifter = shifter; - this->last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Sms_Apu - -Sms_Apu::Sms_Apu() -{ - for ( int i = 0; i < 3; i++ ) - { - squares [i].synth = &square_synth; - oscs [i] = &squares [i]; - } - oscs [3] = &noise; - - volume( 1.0 ); - reset(); -} - -Sms_Apu::~Sms_Apu() -{ -} - -void Sms_Apu::volume( double vol ) -{ - vol *= 0.85 / (osc_count * 64 * 2); - square_synth.volume( vol ); - noise.synth.volume( vol ); -} - -void Sms_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); -} - -void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - require( (center && left && right) || (!center && !left && !right) ); - Sms_Osc& osc = *oscs [index]; - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; -} - -void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); -} - -void Sms_Apu::reset( unsigned feedback, int noise_width ) -{ - last_time = 0; - latch = 0; - - if ( !feedback || !noise_width ) - { - feedback = 0x0009; - noise_width = 16; - } - // convert to "Galios configuration" - looped_feedback = 1 << (noise_width - 1); - noise_feedback = 0; - while ( noise_width-- ) - { - noise_feedback = (noise_feedback << 1) | (feedback & 1); - feedback >>= 1; - } - - squares [0].reset(); - squares [1].reset(); - squares [2].reset(); - noise.reset(); -} - -void Sms_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_time ); // end_time must not be before previous time - - if ( end_time > last_time ) - { - // run oscillators - for ( int i = 0; i < osc_count; ++i ) - { - Sms_Osc& osc = *oscs [i]; - if ( osc.output ) - { - osc.output->set_modified(); - if ( i < 3 ) - squares [i].run( last_time, end_time ); - else - noise.run( last_time, end_time ); - } - } - - last_time = end_time; - } -} - -void Sms_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - assert( last_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 ); - - for ( int i = 0; i < osc_count; i++ ) - { - Sms_Osc& osc = *oscs [i]; - int flags = data >> i; - Blip_Buffer* old_output = osc.output; - osc.output_select = (flags >> 3 & 2) | (flags & 1); - osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output && osc.last_amp ) - { - if ( old_output ) - { - old_output->set_modified(); - square_synth.offset( time, -osc.last_amp, old_output ); - } - osc.last_amp = 0; - } - } -} - -// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) -static unsigned char const volumes [16] = { - 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0 -}; - -void Sms_Apu::write_data( blip_time_t time, int data ) -{ - require( (unsigned) data <= 0xFF ); - - run_until( time ); - - if ( data & 0x80 ) - latch = data; - - int index = (latch >> 5) & 3; - if ( latch & 0x10 ) - { - oscs [index]->volume = volumes [data & 15]; - } - else if ( index < 3 ) - { - Sms_Square& sq = squares [index]; - if ( data & 0x80 ) - sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF); - else - sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00); - } - else - { - int select = data & 3; - if ( select < 3 ) - noise.period = &noise_periods [select]; - else - noise.period = &squares [2].period; - - noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; - noise.shifter = 0x8000; - } -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h b/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h deleted file mode 100644 index 3c11a9c3..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h +++ /dev/null @@ -1,75 +0,0 @@ -// Sega Master System SN76489 PSG sound chip emulator - -// Sms_Snd_Emu 0.1.4 -#ifndef SMS_APU_H -#define SMS_APU_H - -#include "Sms_Oscs.h" - -class Sms_Apu { -public: - // Set overall volume of all oscillators, where 1.0 is full volume - void volume( double ); - - // Set treble equalization - void treble_eq( const blip_eq_t& ); - - // Outputs can be assigned to a single buffer for mono output, or to three - // buffers for stereo output (using Stereo_Buffer to do the mixing). - - // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silences all oscillators. - void output( Blip_Buffer* mono ); - void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL, - // silences oscillator. - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* mono ); - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Reset oscillators and internal state - void reset( unsigned noise_feedback = 0, int noise_width = 0 ); - - // Write GameGear left/right assignment byte - void write_ggstereo( blip_time_t, int ); - - // Write to data port - void write_data( blip_time_t, int ); - - // Run all oscillators up to specified time, end current frame, then - // start a new frame at time 0. - void end_frame( blip_time_t ); - -public: - Sms_Apu(); - ~Sms_Apu(); -private: - // noncopyable - Sms_Apu( const Sms_Apu& ); - Sms_Apu& operator = ( const Sms_Apu& ); - - Sms_Osc* oscs [osc_count]; - Sms_Square squares [3]; - Sms_Square::Synth square_synth; // used by squares - blip_time_t last_time; - int latch; - Sms_Noise noise; - unsigned noise_feedback; - unsigned looped_feedback; - - void run_until( blip_time_t ); -}; - -struct sms_apu_state_t -{ - unsigned char regs [8] [2]; - unsigned char latch; -}; - -inline void Sms_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - -inline void Sms_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h b/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h deleted file mode 100644 index 2a896fef..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h +++ /dev/null @@ -1,49 +0,0 @@ -// Private oscillators used by Sms_Apu - -// Sms_Snd_Emu 0.1.4 -#ifndef SMS_OSCS_H -#define SMS_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct Sms_Osc -{ - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int output_select; - - int delay; - int last_amp; - int volume; - - Sms_Osc(); - void reset(); -}; - -struct Sms_Square : Sms_Osc -{ - int period; - int phase; - - typedef Blip_Synth Synth; - const Synth* synth; - - void reset(); - void run( blip_time_t, blip_time_t ); -}; - -struct Sms_Noise : Sms_Osc -{ - const int* period; - unsigned shifter; - unsigned feedback; - - typedef Blip_Synth Synth; - Synth synth; - - void reset(); - void run( blip_time_t, blip_time_t ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp b/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp deleted file mode 100644 index e909ea18..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp +++ /dev/null @@ -1,489 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Snes_Spc.h" - -#include - -/* Copyright (C) 2004-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" - -// always in the future (CPU time can go over 0, but not by this much) -int const timer_disabled_time = 127; - -Snes_Spc::Snes_Spc() : dsp( mem.ram ), cpu( this, mem.ram ) -{ - set_tempo( 1.0 ); - - // Put STOP instruction around memory to catch PC underflow/overflow. - memset( mem.padding1, 0xFF, sizeof mem.padding1 ); - memset( mem.padding2, 0xFF, sizeof mem.padding2 ); - - // A few tracks read from the last four bytes of IPL ROM - boot_rom [sizeof boot_rom - 2] = 0xC0; - boot_rom [sizeof boot_rom - 1] = 0xFF; - memset( boot_rom, 0, sizeof boot_rom - 2 ); -} - -void Snes_Spc::set_tempo( double t ) -{ - int unit = (int) (16.0 / t + 0.5); - - timer [0].divisor = unit * 8; // 8 kHz - timer [1].divisor = unit * 8; // 8 kHz - timer [2].divisor = unit; // 64 kHz -} - -// Load - -void Snes_Spc::set_ipl_rom( void const* in ) -{ - memcpy( boot_rom, in, sizeof boot_rom ); -} - -blargg_err_t Snes_Spc::load_spc( const void* data, long size ) -{ - struct spc_file_t { - char signature [27]; - char unused [10]; - uint8_t pc [2]; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - char unused2 [212]; - uint8_t ram [0x10000]; - uint8_t dsp [128]; - uint8_t ipl_rom [128]; - }; - assert( offsetof (spc_file_t,ipl_rom) == spc_file_size ); - - const spc_file_t* spc = (spc_file_t const*) data; - - if ( size < spc_file_size ) - return "Not an SPC file"; - - if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 ) - return "Not an SPC file"; - - registers_t regs; - regs.pc = spc->pc [1] * 0x100 + spc->pc [0]; - regs.a = spc->a; - regs.x = spc->x; - regs.y = spc->y; - regs.status = spc->status; - regs.sp = spc->sp; - - if ( (unsigned long) size >= sizeof *spc ) - set_ipl_rom( spc->ipl_rom ); - - const char* error = load_state( regs, spc->ram, spc->dsp ); - - echo_accessed = false; - - return error; -} - -void Snes_Spc::clear_echo() -{ - if ( !(dsp.read( 0x6C ) & 0x20) ) - { - unsigned addr = 0x100 * dsp.read( 0x6D ); - size_t size = 0x800 * dsp.read( 0x7D ); - memset( mem.ram + addr, 0xFF, min( size, sizeof mem.ram - addr ) ); - } -} - -// Handle other file formats (emulator save states) in user code, not here. - -blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram, - const void* dsp_state ) -{ - // cpu - cpu.r = cpu_state; - - // Allow DSP to generate one sample before code starts - // (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it - // clears KON 31 cycles from starting execution. It works on the SNES - // since the SPC player adds a few extra cycles delay after restoring - // KON from the DSP registers at the end of an SPC file). - extra_cycles = 32; - - // ram - memcpy( mem.ram, new_ram, sizeof mem.ram ); - memcpy( extra_ram, mem.ram + rom_addr, sizeof extra_ram ); - - // boot rom (have to force enable_rom() to update it) - rom_enabled = !(mem.ram [0xF1] & 0x80); - enable_rom( !rom_enabled ); - - // dsp - dsp.reset(); - int i; - for ( i = 0; i < Spc_Dsp::register_count; i++ ) - dsp.write( i, ((uint8_t const*) dsp_state) [i] ); - - // timers - for ( i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - - t.next_tick = 0; - t.enabled = (mem.ram [0xF1] >> i) & 1; - if ( !t.enabled ) - t.next_tick = timer_disabled_time; - t.count = 0; - t.counter = mem.ram [0xFD + i] & 15; - - int p = mem.ram [0xFA + i]; - t.period = p ? p : 0x100; - } - - // Handle registers which already give 0 when read by setting RAM and not changing it. - // Put STOP instruction in registers which can be read, to catch attempted CPU execution. - mem.ram [0xF0] = 0; - mem.ram [0xF1] = 0; - mem.ram [0xF3] = 0xFF; - mem.ram [0xFA] = 0; - mem.ram [0xFB] = 0; - mem.ram [0xFC] = 0; - mem.ram [0xFD] = 0xFF; - mem.ram [0xFE] = 0xFF; - mem.ram [0xFF] = 0xFF; - - return 0; // success -} - -// Hardware - -// Current time starts negative and ends at 0 -inline spc_time_t Snes_Spc::time() const -{ - return -cpu.remain(); -} - -// Keep track of next time to run and avoid a function call if it hasn't been reached. - -// Timers - -void Snes_Spc::Timer::run_until_( spc_time_t time ) -{ - if ( !enabled ) - dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time ); - assert( enabled ); // when disabled, next_tick should always be in the future - - int elapsed = ((time - next_tick) / divisor) + 1; - next_tick += elapsed * divisor; - - elapsed += count; - if ( elapsed >= period ) // avoid unnecessary division - { - int n = elapsed / period; - elapsed -= n * period; - counter = (counter + n) & 15; - } - count = elapsed; -} - -// DSP - -const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second - -void Snes_Spc::run_dsp_( spc_time_t time ) -{ - int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample - sample_t* buf = sample_buf; - if ( buf ) { - sample_buf = buf + count * 2; // stereo - assert( sample_buf <= buf_end ); - } - next_dsp += count * clocks_per_sample; - dsp.run( count, buf ); -} - -inline void Snes_Spc::run_dsp( spc_time_t time ) -{ - if ( time >= next_dsp ) - run_dsp_( time ); -} - -// Debug-only check for read/write within echo buffer, since this might result in -// inaccurate emulation due to the DSP not being caught up to the present. -inline void Snes_Spc::check_for_echo_access( spc_addr_t addr ) -{ - if ( !echo_accessed && !(dsp.read( 0x6C ) & 0x20) ) - { - // ** If echo accesses are found that require running the DSP, cache - // the start and end address on DSP writes to speed up checking. - - unsigned start = 0x100 * dsp.read( 0x6D ); - unsigned end = start + 0x800 * dsp.read( 0x7D ); - if ( start <= addr && addr < end ) { - echo_accessed = true; - dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr ); - } - } -} - -// Read - -int Snes_Spc::read( spc_addr_t addr ) -{ - int result = mem.ram [addr]; - - if ( (rom_addr <= addr && addr < 0xFFFC || addr >= 0xFFFE) && rom_enabled ) - dprintf( "Read from ROM: %04X -> %02X\n", addr, result ); - - if ( unsigned (addr - 0xF0) < 0x10 ) - { - assert( 0xF0 <= addr && addr <= 0xFF ); - - // counters - int i = addr - 0xFD; - if ( i >= 0 ) - { - Timer& t = timer [i]; - t.run_until( time() ); - int old = t.counter; - t.counter = 0; - return old; - } - - // dsp - if ( addr == 0xF3 ) - { - run_dsp( time() ); - if ( mem.ram [0xF2] >= Spc_Dsp::register_count ) - dprintf( "DSP read from $%02X\n", (int) mem.ram [0xF2] ); - return dsp.read( mem.ram [0xF2] & 0x7F ); - } - - if ( addr == 0xF0 || addr == 0xF1 || addr == 0xF8 || - addr == 0xF9 || addr == 0xFA ) - dprintf( "Read from register $%02X\n", (int) addr ); - - // Registers which always read as 0 are handled by setting mem.ram [reg] to 0 - // at startup then never changing that value. - - check(( check_for_echo_access( addr ), true )); - } - - return result; -} - - -// Write - -void Snes_Spc::enable_rom( bool enable ) -{ - if ( rom_enabled != enable ) - { - rom_enabled = enable; - memcpy( mem.ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); - // TODO: ROM can still get overwritten when DSP writes to echo buffer - } -} - -void Snes_Spc::write( spc_addr_t addr, int data ) -{ - // first page is very common - if ( addr < 0xF0 ) { - mem.ram [addr] = (uint8_t) data; - } - else switch ( addr ) - { - // RAM - default: - check(( check_for_echo_access( addr ), true )); - if ( addr < rom_addr ) { - mem.ram [addr] = (uint8_t) data; - } - else { - extra_ram [addr - rom_addr] = (uint8_t) data; - if ( !rom_enabled ) - mem.ram [addr] = (uint8_t) data; - } - break; - - // DSP - //case 0xF2: // mapped to RAM - case 0xF3: { - run_dsp( time() ); - int reg = mem.ram [0xF2]; - if ( next_dsp > 0 ) { - // skip mode - - // key press - if ( reg == 0x4C ) - keys_pressed |= data & ~dsp.read( 0x5C ); - - // key release - if ( reg == 0x5C ) { - keys_released |= data; - keys_pressed &= ~data; - } - } - if ( reg < Spc_Dsp::register_count ) { - dsp.write( reg, data ); - } - else { - dprintf( "DSP write to $%02X\n", (int) reg ); - } - break; - } - - case 0xF0: // Test register - dprintf( "Wrote $%02X to $F0\n", (int) data ); - break; - - // Config - case 0xF1: - { - // timers - for ( int i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - if ( !(data & (1 << i)) ) { - t.enabled = 0; - t.next_tick = timer_disabled_time; - } - else if ( !t.enabled ) { - // just enabled - t.enabled = 1; - t.counter = 0; - t.count = 0; - t.next_tick = time(); - } - } - - // port clears - if ( data & 0x10 ) { - mem.ram [0xF4] = 0; - mem.ram [0xF5] = 0; - } - if ( data & 0x20 ) { - mem.ram [0xF6] = 0; - mem.ram [0xF7] = 0; - } - - enable_rom( (data & 0x80) != 0 ); - - break; - } - - // Ports - case 0xF4: - case 0xF5: - case 0xF6: - case 0xF7: - // to do: handle output ports - break; - - //case 0xF8: // verified on SNES that these are read/write (RAM) - //case 0xF9: - - // Timers - case 0xFA: - case 0xFB: - case 0xFC: { - Timer& t = timer [addr - 0xFA]; - if ( (t.period & 0xFF) != data ) { - t.run_until( time() ); - t.period = data ? data : 0x100; - } - break; - } - - // Counters (cleared on write) - case 0xFD: - case 0xFE: - case 0xFF: - dprintf( "Wrote to counter $%02X\n", (int) addr ); - timer [addr - 0xFD].counter = 0; - break; - } -} - -// Play - -blargg_err_t Snes_Spc::skip( long count ) -{ - if ( count > 4 * 32000L ) - { - // don't run DSP for long durations (2-3 times faster) - - const long sync_count = 32000L * 2; - - // keep track of any keys pressed/released (and not subsequently released) - keys_pressed = 0; - keys_released = 0; - // sentinel tells play to ignore DSP - RETURN_ERR( play( count - sync_count, skip_sentinel ) ); - - // press/release keys now - dsp.write( 0x5C, keys_released & ~keys_pressed ); - dsp.write( 0x4C, keys_pressed ); - - clear_echo(); - - // play the last few seconds normally to help synchronize DSP - count = sync_count; - } - - return play( count ); -} - -blargg_err_t Snes_Spc::play( long count, sample_t* out ) -{ - require( count % 2 == 0 ); // output is always in pairs of samples - - // CPU time() runs from -duration to 0 - spc_time_t duration = (count / 2) * clocks_per_sample; - - // DSP output is made on-the-fly when the CPU reads/writes DSP registers - sample_buf = out; - buf_end = out + (out && out != skip_sentinel ? count : 0); - next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample; - - // Localize timer next_tick times and run them to the present to prevent a running - // but ignored timer's next_tick from getting too far behind and overflowing. - for ( int i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - if ( t.enabled ) - { - t.next_tick -= duration; - t.run_until( -duration ); - } - } - - // Run CPU for duration, reduced by any extra cycles from previous run - int elapsed = cpu.run( duration - extra_cycles ); - if ( elapsed > 0 ) - { - dprintf( "Unhandled instruction $%02X, pc = $%04X\n", - (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc ); - return "Emulation error (illegal/unsupported instruction)"; - } - extra_cycles = -elapsed; - - // Catch DSP up to present. - run_dsp( 0 ); - if ( out ) { - assert( next_dsp == clocks_per_sample ); - assert( out == skip_sentinel || sample_buf - out == count ); - } - buf_end = 0; - - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h b/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h deleted file mode 100644 index b558fb71..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h +++ /dev/null @@ -1,121 +0,0 @@ -// Super Nintendo (SNES) SPC-700 APU Emulator - -// Game_Music_Emu 0.5.2 -#ifndef SNES_SPC_H -#define SNES_SPC_H - -#include "blargg_common.h" -#include "Spc_Cpu.h" -#include "Spc_Dsp.h" - -class Snes_Spc { -public: - - // Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true. - enum { spc_file_size = 0x10180 }; - blargg_err_t load_spc( const void* spc, long spc_size ); - - // Generate 'count' samples and optionally write to 'buf'. Count must be even. - // Sample output is 16-bit 32kHz, signed stereo pairs with the left channel first. - typedef short sample_t; - blargg_err_t play( long count, sample_t* buf = NULL ); - -// Optional functionality - - // Load copy of state into emulator. - typedef Spc_Cpu::registers_t registers_t; - blargg_err_t load_state( const registers_t& cpu_state, const void* ram_64k, - const void* dsp_regs_128 ); - - // Clear echo buffer, useful because many tracks have junk in the buffer. - void clear_echo(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { voice_count = Spc_Dsp::voice_count }; - void mute_voices( int mask ); - - // Skip forward by the specified number of samples (64000 samples = 1 second) - blargg_err_t skip( long count ); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped the - // 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated - void disable_surround( bool disable = true ); - - // Set 128 bytes to use for IPL boot ROM. Makes copy. Default is zero filled, - // to avoid including copyrighted code from the SPC-700. - void set_ipl_rom( const void* ); - - void set_tempo( double ); - -public: - Snes_Spc(); - typedef BOOST::uint8_t uint8_t; -private: - // timers - struct Timer - { - spc_time_t next_tick; - int period; - int count; - int divisor; - int enabled; - int counter; - - void run_until_( spc_time_t ); - void run_until( spc_time_t time ) - { - if ( time >= next_tick ) - run_until_( time ); - } - }; - enum { timer_count = 3 }; - Timer timer [timer_count]; - - // hardware - int extra_cycles; - spc_time_t time() const; - int read( spc_addr_t ); - void write( spc_addr_t, int ); - friend class Spc_Cpu; - - // dsp - sample_t* sample_buf; - sample_t* buf_end; // to do: remove this once possible bug resolved - spc_time_t next_dsp; - Spc_Dsp dsp; - int keys_pressed; - int keys_released; - sample_t skip_sentinel [1]; // special value for play() passed by skip() - void run_dsp( spc_time_t ); - void run_dsp_( spc_time_t ); - bool echo_accessed; - void check_for_echo_access( spc_addr_t ); - - // boot rom - enum { rom_size = 64 }; - enum { rom_addr = 0xFFC0 }; - bool rom_enabled; - void enable_rom( bool ); - - // CPU and RAM (at end because it's large) - Spc_Cpu cpu; - uint8_t extra_ram [rom_size]; - struct { - // padding to catch jumps before beginning or past end - uint8_t padding1 [0x100]; - uint8_t ram [0x10000]; - uint8_t padding2 [0x100]; - } mem; - uint8_t boot_rom [rom_size]; -}; - -inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } - -inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } - -inline void Snes_Spc::set_gain( double v ) { dsp.set_gain( v ); } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp deleted file mode 100644 index fb9983b8..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp +++ /dev/null @@ -1,1062 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Spc_Cpu.h" - -#include "blargg_endian.h" -#include "Snes_Spc.h" - -/* Copyright (C) 2004-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" - -// Several instructions are commented out (or not even implemented). These aren't -// used by the SPC files tested. - -// Optimize performance for the most common instructions, and size for the rest: -// -// 15% 0xF0 BEQ rel -// 8% 0xE4 MOV A,dp -// 4% 0xF5 MOV A,abs+X -// 4% 0xD0 BNE rel -// 4% 0x6F RET -// 4% 0x3F CALL addr -// 4% 0xF4 MOV A,dp+X -// 3% 0xC4 MOV dp,A -// 2% 0xEB MOV Y,dp -// 2% 0x3D INC X -// 2% 0xF6 MOV A,abs+Y -// (1% and below not shown) - -Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) -{ - remain_ = 0; - assert( INT_MAX >= 0x7FFFFFFF ); // requires 32-bit int - blargg_verify_byte_order(); -} - -#define READ( addr ) (emu.read( addr )) -#define WRITE( addr, value ) (emu.write( addr, value )) - -#define READ_DP( addr ) READ( (addr) + dp ) -#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) - -#define READ_PROG( addr ) (ram [addr]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -int Spc_Cpu::read( spc_addr_t addr ) -{ - return READ( addr ); -} - -void Spc_Cpu::write( spc_addr_t addr, int data ) -{ - WRITE( addr, data ); -} - -// Cycle table derived from text copy of SPC-700 manual (using regular expressions) -static unsigned char const cycle_table [0x100] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1 - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4 - 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7 - 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9 - 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B - 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C - 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D - 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E - 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 // F -}; - -// The C,mem instructions are hardly used, so a non-inline function is used for -// the common access code. -unsigned Spc_Cpu::mem_bit( spc_addr_t pc ) -{ - unsigned addr = READ_PROG16( pc ); - unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); - return (t << 8) & 0x100; -} - -spc_time_t Spc_Cpu::run( spc_time_t cycle_count ) -{ - remain_ = cycle_count; - - uint8_t* const ram = this->ram; // cache - - // Stack pointer is kept one greater than usual SPC stack pointer to allow - // common pre-decrement and post-increment memory instructions that some - // processors have. Address wrap-around isn't supported. - #define PUSH( v ) (*--sp = uint8_t (v)) - #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) - #define POP() (*sp++) - #define SET_SP( v ) (sp = ram + 0x101 + (v)) - #define GET_SP() (sp - 0x101 - ram) - - uint8_t* sp; - SET_SP( r.sp ); - - // registers - unsigned pc = (unsigned) r.pc; - int a = r.a; - int x = r.x; - int y = r.y; - - // status flags - - const int st_n = 0x80; - const int st_v = 0x40; - const int st_p = 0x20; - const int st_b = 0x10; - const int st_h = 0x08; - const int st_i = 0x04; - const int st_z = 0x02; - const int st_c = 0x01; - - #define IS_NEG (nz & 0x880) - - #define CALC_STATUS( out ) do {\ - out = status & ~(st_n | st_z | st_c);\ - out |= (c >> 8) & st_c;\ - out |= (dp >> 3) & st_p;\ - if ( IS_NEG ) out |= st_n;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & ~(st_n | st_z | st_c | st_p);\ - c = in << 8;\ - nz = (in << 4) & 0x800;\ - nz |= ~in & st_z;\ - dp = (in << 3) & 0x100;\ - } while ( 0 ) - - int status; - int c; // store C as 'c' & 0x100. - int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0 - unsigned dp; // direct page base - { - int temp = r.status; - SET_STATUS( temp ); - } - - goto loop; - - unsigned data; // first operand of instruction and temporary across function calls - - // Common endings for instructions -cbranch_taken_loop: // compare and branch - pc += (BOOST::int8_t) READ_PROG( pc ); - remain_ -= 2; -inc_pc_loop: // end of instruction with an operand - pc++; -loop: - - check( (unsigned) pc < 0x10000 ); - check( (unsigned) GET_SP() < 0x100 ); - - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - unsigned opcode = READ_PROG( pc ); - pc++; - // to do: if pc is at end of memory, this will get wrong byte - data = READ_PROG( pc ); - - if ( remain_ <= 0 ) - goto stop; - - remain_ -= cycle_table [opcode]; - - // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise - // use a local temporary. - switch ( opcode ) - { - - #define BRANCH( cond ) {\ - pc++;\ - int offset = (BOOST::int8_t) data;\ - if ( cond ) {\ - pc += offset;\ - remain_ -= 2;\ - }\ - goto loop;\ - } - -// Most-Common - - case 0xF0: // BEQ (most common) - BRANCH( !(uint8_t) nz ) - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ) - - case 0x3F: // CALL - PUSH16( pc + 2 ); - pc = READ_PROG16( pc ); - goto loop; - - case 0x6F: // RET - pc = POP(); - pc += POP() * 0x100; - goto loop; - -#define CASE( n ) case n: - -// Define common address modes based on opcode for immediate mode. Execution -// ends with data set to the address of the operand. -#define ADDR_MODES( op )\ - CASE( op - 0x02 ) /* (X) */\ - data = x + dp;\ - pc--;\ - goto end_##op;\ - CASE( op + 0x0F ) /* (dp)+Y */\ - data = READ_PROG16( data + dp ) + y;\ - goto end_##op;\ - CASE( op - 0x01 ) /* (dp+X) */\ - data = READ_PROG16( uint8_t (data + x) + dp );\ - goto end_##op;\ - CASE( op + 0x0E ) /* abs+Y */\ - data += y;\ - goto abs_##op;\ - CASE( op + 0x0D ) /* abs+X */\ - data += x;\ - CASE( op - 0x03 ) /* abs */\ - abs_##op:\ - pc++;\ - data += 0x100 * READ_PROG( pc );\ - goto end_##op;\ - CASE( op + 0x0C ) /* dp+X */\ - data = uint8_t (data + x);\ - CASE( op - 0x04 ) /* dp */\ - data += dp;\ - end_##op: - -// 1. 8-bit Data Transmission Commands. Group I - - ADDR_MODES( 0xE8 ) // MOV A,addr - // case 0xE4: // MOV a,dp (most common) - mov_a_addr: - a = nz = READ( data ); - goto inc_pc_loop; - case 0xBF: // MOV A,(X)+ - data = x + dp; - x = uint8_t (x + 1); - pc--; - goto mov_a_addr; - - case 0xE8: // MOV A,imm - a = data; - nz = data; - goto inc_pc_loop; - - case 0xF9: // MOV X,dp+Y - data = uint8_t (data + y); - case 0xF8: // MOV X,dp - data += dp; - goto mov_x_addr; - case 0xE9: // MOV X,abs - data = READ_PROG16( pc ); - pc++; - mov_x_addr: - data = READ( data ); - case 0xCD: // MOV X,imm - x = data; - nz = data; - goto inc_pc_loop; - - case 0xFB: // MOV Y,dp+X - data = uint8_t (data + x); - case 0xEB: // MOV Y,dp - data += dp; - goto mov_y_addr; - case 0xEC: // MOV Y,abs - data = READ_PROG16( pc ); - pc++; - mov_y_addr: - data = READ( data ); - case 0x8D: // MOV Y,imm - y = data; - nz = data; - goto inc_pc_loop; - -// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 - - ADDR_MODES( 0xC8 ) // MOV addr,A - WRITE( data, a ); - goto inc_pc_loop; - - { - int temp; - case 0xCC: // MOV abs,Y - temp = y; - goto mov_abs_temp; - case 0xC9: // MOV abs,X - temp = x; - mov_abs_temp: - WRITE( READ_PROG16( pc ), temp ); - pc += 2; - goto loop; - } - - case 0xD9: // MOV dp+Y,X - data = uint8_t (data + y); - case 0xD8: // MOV dp,X - WRITE( data + dp, x ); - goto inc_pc_loop; - - case 0xDB: // MOV dp+X,Y - data = uint8_t (data + x); - case 0xCB: // MOV dp,Y - WRITE( data + dp, y ); - goto inc_pc_loop; - - case 0xFA: // MOV dp,dp - data = READ( data + dp ); - case 0x8F: // MOV dp,#imm - pc++; - WRITE_DP( READ_PROG( pc ), data ); - goto inc_pc_loop; - -// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. - - case 0x7D: // MOV A,X - a = x; - nz = x; - goto loop; - - case 0xDD: // MOV A,Y - a = y; - nz = y; - goto loop; - - case 0x5D: // MOV X,A - x = a; - nz = a; - goto loop; - - case 0xFD: // MOV Y,A - y = a; - nz = a; - goto loop; - - case 0x9D: // MOV X,SP - x = nz = GET_SP(); - goto loop; - - case 0xBD: // MOV SP,X - SET_SP( x ); - goto loop; - - //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) - - case 0xAF: // MOV (X)+,A - WRITE_DP( x, a ); - x++; - goto loop; - -// 5. 8-BIT LOGIC OPERATION COMMANDS - -#define LOGICAL_OP( op, func )\ - ADDR_MODES( op ) /* addr */\ - data = READ( data );\ - case op: /* imm */\ - nz = a func##= data;\ - goto inc_pc_loop;\ - { unsigned addr;\ - case op + 0x11: /* X,Y */\ - data = READ_DP( y );\ - addr = x + dp;\ - pc--;\ - goto addr_##op;\ - case op + 0x01: /* dp,dp */\ - data = READ_DP( data );\ - case op + 0x10: /*dp,imm*/\ - pc++;\ - addr = READ_PROG( pc ) + dp;\ - addr_##op:\ - nz = data func READ( addr );\ - WRITE( addr, nz );\ - goto inc_pc_loop;\ - } - - LOGICAL_OP( 0x28, & ); // AND - - LOGICAL_OP( 0x08, | ); // OR - - LOGICAL_OP( 0x48, ^ ); // EOR - -// 4. 8-BIT ARITHMETIC OPERATION COMMANDS - - ADDR_MODES( 0x68 ) // CMP addr - data = READ( data ); - case 0x68: // CMP imm - nz = a - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x79: // CMP (X),(Y) - data = READ_DP( x ); - nz = data - READ_DP( y ); - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x69: // CMP (dp),(dp) - data = READ_DP( data ); - case 0x78: // CMP dp,imm - pc++; - nz = READ_DP( READ_PROG( pc ) ) - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x3E: // CMP X,dp - data += dp; - goto cmp_x_addr; - case 0x1E: // CMP X,abs - data = READ_PROG16( pc ); - pc++; - cmp_x_addr: - data = READ( data ); - case 0xC8: // CMP X,imm - nz = x - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x7E: // CMP Y,dp - data += dp; - goto cmp_y_addr; - case 0x5E: // CMP Y,abs - data = READ_PROG16( pc ); - pc++; - cmp_y_addr: - data = READ( data ); - case 0xAD: // CMP Y,imm - nz = y - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - { - int addr; - case 0xB9: // SBC (x),(y) - case 0x99: // ADC (x),(y) - pc--; // compensate for inc later - data = READ_DP( x ); - addr = y + dp; - goto adc_addr; - case 0xA9: // SBC dp,dp - case 0x89: // ADC dp,dp - data = READ_DP( data ); - case 0xB8: // SBC dp,imm - case 0x98: // ADC dp,imm - pc++; - addr = READ_PROG( pc ) + dp; - adc_addr: - nz = READ( addr ); - goto adc_data; - -// catch ADC and SBC together, then decode later based on operand -#undef CASE -#define CASE( n ) case n: case (n) + 0x20: - ADDR_MODES( 0x88 ) // ADC/SBC addr - data = READ( data ); - case 0xA8: // SBC imm - case 0x88: // ADC imm - addr = -1; // A - nz = a; - adc_data: { - if ( opcode & 0x20 ) - data ^= 0xFF; // SBC - int carry = (c >> 8) & 1; - int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - int hc = (nz & 15) + carry; - c = nz += data + carry; - hc = (nz & 15) - hc; - status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h); - if ( addr < 0 ) { - a = (uint8_t) nz; - goto inc_pc_loop; - } - WRITE( addr, (uint8_t) nz ); - goto inc_pc_loop; - } - - } - -// 6. ADDITION & SUBTRACTION COMMANDS - -#define INC_DEC_REG( reg, n )\ - nz = reg + n;\ - reg = (uint8_t) nz;\ - goto loop; - - case 0xBC: INC_DEC_REG( a, 1 ) // INC A - case 0x3D: INC_DEC_REG( x, 1 ) // INC X - case 0xFC: INC_DEC_REG( y, 1 ) // INC Y - - case 0x9C: INC_DEC_REG( a, -1 ) // DEC A - case 0x1D: INC_DEC_REG( x, -1 ) // DEC X - case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y - - case 0x9B: // DEC dp+X - case 0xBB: // INC dp+X - data = uint8_t (data + x); - case 0x8B: // DEC dp - case 0xAB: // INC dp - data += dp; - goto inc_abs; - case 0x8C: // DEC abs - case 0xAC: // INC abs - data = READ_PROG16( pc ); - pc++; - inc_abs: - nz = ((opcode >> 4) & 2) - 1; - nz += READ( data ); - WRITE( data, (uint8_t) nz ); - goto inc_pc_loop; - -// 7. SHIFT, ROTATION COMMANDS - - case 0x5C: // LSR A - c = 0; - case 0x7C:{// ROR A - nz = ((c >> 1) & 0x80) | (a >> 1); - c = a << 8; - a = nz; - goto loop; - } - - case 0x1C: // ASL A - c = 0; - case 0x3C:{// ROL A - int temp = (c >> 8) & 1; - c = a << 1; - nz = c | temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x0B: // ASL dp - c = 0; - data += dp; - goto rol_mem; - case 0x1B: // ASL dp+X - c = 0; - case 0x3B: // ROL dp+X - data = uint8_t (data + x); - case 0x2B: // ROL dp - data += dp; - goto rol_mem; - case 0x0C: // ASL abs - c = 0; - case 0x2C: // ROL abs - data = READ_PROG16( pc ); - pc++; - rol_mem: - nz = (c >> 8) & 1; - nz |= (c = READ( data ) << 1); - WRITE( data, (uint8_t) nz ); - goto inc_pc_loop; - - case 0x4B: // LSR dp - c = 0; - data += dp; - goto ror_mem; - case 0x5B: // LSR dp+X - c = 0; - case 0x7B: // ROR dp+X - data = uint8_t (data + x); - case 0x6B: // ROR dp - data += dp; - goto ror_mem; - case 0x4C: // LSR abs - c = 0; - case 0x6C: // ROR abs - data = READ_PROG16( pc ); - pc++; - ror_mem: { - int temp = READ( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - WRITE( data, nz ); - goto inc_pc_loop; - } - - case 0x9F: // XCN - nz = a = (a >> 4) | uint8_t (a << 4); - goto loop; - -// 8. 16-BIT TRANSMISION COMMANDS - - case 0xBA: // MOVW YA,dp - a = READ_DP( data ); - nz = (a & 0x7F) | (a >> 1); - y = READ_DP( uint8_t (data + 1) ); - nz |= y; - goto inc_pc_loop; - - case 0xDA: // MOVW dp,YA - WRITE_DP( data, a ); - WRITE_DP( uint8_t (data + 1), y ); - goto inc_pc_loop; - -// 9. 16-BIT OPERATION COMMANDS - - case 0x3A: // INCW dp - case 0x1A:{// DECW dp - data += dp; - - // low byte - int temp = READ( data ); - temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW - nz = ((temp >> 1) | temp) & 0x7F; - WRITE( data, (uint8_t) temp ); - - // high byte - data = uint8_t (data + 1) + dp; - temp >>= 8; - temp = uint8_t (temp + READ( data )); - nz |= temp; - WRITE( data, temp ); - - goto inc_pc_loop; - } - - case 0x9A: // SUBW YA,dp - case 0x7A: // ADDW YA,dp - { - // read 16-bit addend - int temp = READ_DP( data ); - int sign = READ_DP( uint8_t (data + 1) ); - temp += 0x100 * sign; - status &= ~(st_v | st_h); - - // to do: fix half-carry for SUBW (it's probably wrong) - - // for SUBW, negate and truncate to 16 bits - if ( opcode & 0x80 ) { - temp = (temp ^ 0xFFFF) + 1; - sign = temp >> 8; - } - - // add low byte (A) - temp += a; - a = (uint8_t) temp; - nz = (temp | (temp >> 1)) & 0x7F; - - // add high byte (Y) - temp >>= 8; - c = y + temp; - nz = (nz | c) & 0xFF; - - // half-carry (temporary avoids CodeWarrior optimizer bug) - unsigned hc = (c & 15) - (y & 15); - status |= (hc >> 4) & st_h; - - // overflow if sign of YA changed when previous sign and addend sign were same - status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; - - y = (uint8_t) c; - - goto inc_pc_loop; - } - - case 0x5A: { // CMPW YA,dp - int temp = a - READ_DP( data ); - nz = ((temp >> 1) | temp) & 0x7F; - temp = y + (temp >> 8); - temp -= READ_DP( uint8_t (data + 1) ); - nz |= temp; - c = ~temp; - nz &= 0xFF; - goto inc_pc_loop; - } - -// 10. MULTIPLICATION & DIVISON COMMANDS - - case 0xCF: { // MUL YA - unsigned temp = y * a; - a = (uint8_t) temp; - nz = ((temp >> 1) | temp) & 0x7F; - y = temp >> 8; - nz |= y; - goto loop; - } - - case 0x9E: // DIV YA,X - { - // behavior based on SPC CPU tests - - status &= ~(st_h | st_v); - - if ( (y & 15) >= (x & 15) ) - status |= st_h; - - if ( y >= x ) - status |= st_v; - - unsigned ya = y * 0x100 + a; - if ( y < x * 2 ) - { - a = ya / x; - y = ya - a * x; - } - else - { - a = 255 - (ya - x * 0x200) / (256 - x); - y = x + (ya - x * 0x200) % (256 - x); - } - - nz = (uint8_t) a; - a = (uint8_t) a; - - goto loop; - } - -// 11. DECIMAL COMPENSATION COMMANDS - - // seem unused - // case 0xDF: // DAA - // case 0xBE: // DAS - -// 12. BRANCHING COMMANDS - - case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; - goto inc_pc_loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x03: // BBS dp.bit,rel - case 0x23: - case 0x43: - case 0x63: - case 0x83: - case 0xA3: - case 0xC3: - case 0xE3: - pc++; - if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0x13: // BBC dp.bit,rel - case 0x33: - case 0x53: - case 0x73: - case 0x93: - case 0xB3: - case 0xD3: - case 0xF3: - pc++; - if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xDE: // CBNE dp+X,rel - data = uint8_t (data + x); - // fall through - case 0x2E: // CBNE dp,rel - pc++; - if ( READ_DP( data ) != a ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xFE: // DBNZ Y,rel - y = uint8_t (y - 1); - BRANCH( y ) - - case 0x6E: { // DBNZ dp,rel - pc++; - unsigned temp = READ_DP( data ) - 1; - WRITE_DP( (uint8_t) data, (uint8_t) temp ); - if ( temp ) - goto cbranch_taken_loop; - goto inc_pc_loop; - } - - case 0x1F: // JMP (abs+X) - pc = READ_PROG16( pc ) + x; - // fall through - case 0x5F: // JMP abs - pc = READ_PROG16( pc ); - goto loop; - -// 13. SUB-ROUTINE CALL RETURN COMMANDS - - case 0x0F:{// BRK - check( false ); // untested - PUSH16( pc + 1 ); - pc = READ_PROG16( 0xFFDE ); // vector address verified - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - status = (status | st_b) & ~st_i; - goto loop; - } - - case 0x4F: // PCALL offset - pc++; - PUSH16( pc ); - pc = 0xFF00 + data; - goto loop; - - case 0x01: // TCALL n - case 0x11: - case 0x21: - case 0x31: - case 0x41: - case 0x51: - case 0x61: - case 0x71: - case 0x81: - case 0x91: - case 0xA1: - case 0xB1: - case 0xC1: - case 0xD1: - case 0xE1: - case 0xF1: - PUSH16( pc ); - pc = READ_PROG16( 0xFFDE - (opcode >> 3) ); - goto loop; - -// 14. STACK OPERATION COMMANDS - - { - int temp; - case 0x7F: // RET1 - temp = POP(); - pc = POP(); - pc |= POP() << 8; - goto set_status; - case 0x8E: // POP PSW - temp = POP(); - set_status: - SET_STATUS( temp ); - goto loop; - } - - case 0x0D: { // PUSH PSW - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - goto loop; - } - - case 0x2D: // PUSH A - PUSH( a ); - goto loop; - - case 0x4D: // PUSH X - PUSH( x ); - goto loop; - - case 0x6D: // PUSH Y - PUSH( y ); - goto loop; - - case 0xAE: // POP A - a = POP(); - goto loop; - - case 0xCE: // POP X - x = POP(); - goto loop; - - case 0xEE: // POP Y - y = POP(); - goto loop; - -// 15. BIT OPERATION COMMANDS - - case 0x02: // SET1 - case 0x22: - case 0x42: - case 0x62: - case 0x82: - case 0xA2: - case 0xC2: - case 0xE2: - case 0x12: // CLR1 - case 0x32: - case 0x52: - case 0x72: - case 0x92: - case 0xB2: - case 0xD2: - case 0xF2: { - data += dp; - int bit = 1 << (opcode >> 5); - int mask = ~bit; - if ( opcode & 0x10 ) - bit = 0; - WRITE( data, (READ( data ) & mask) | bit ); - goto inc_pc_loop; - } - - case 0x0E: // TSET1 abs - case 0x4E:{// TCLR1 abs - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data ); - nz = temp & a; - temp &= ~a; - if ( !(opcode & 0x40) ) - temp |= a; - WRITE( data, temp ); - goto loop; - } - - case 0x4A: // AND1 C,mem.bit - c &= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x6A: // AND1 C,/mem.bit - check( false ); // untested - c &= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x0A: // OR1 C,mem.bit - check( false ); // untested - c |= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x2A: // OR1 C,/mem.bit - check( false ); // untested - c |= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x8A: // EOR1 C,mem.bit - c ^= mem_bit( pc ); - pc += 2; - goto loop; - - case 0xEA: { // NOT1 mem.bit - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1FFF ); - temp ^= 1 << (data >> 13); - WRITE( data & 0x1FFF, temp ); - goto loop; - } - - case 0xCA: { // MOV1 mem.bit,C - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1FFF ); - unsigned bit = data >> 13; - temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); - WRITE( data & 0x1FFF, temp ); - goto loop; - } - - case 0xAA: // MOV1 C,mem.bit - c = mem_bit( pc ); - pc += 2; - goto loop; - -// 16. PROGRAM STATUS FLAG OPERATION COMMANDS - - case 0x60: // CLRC - c = 0; - goto loop; - - case 0x80: // SETC - c = ~0; - goto loop; - - case 0xED: // NOTC - c ^= 0x100; - goto loop; - - case 0xE0: // CLRV - status &= ~(st_v | st_h); - goto loop; - - case 0x20: // CLRP - dp = 0; - goto loop; - - case 0x40: // SETP - dp = 0x100; - goto loop; - - case 0xA0: // EI - check( false ); // untested - status |= st_i; - goto loop; - - case 0xC0: // DI - check( false ); // untested - status &= ~st_i; - goto loop; - -// 17. OTHER COMMANDS - - case 0x00: // NOP - goto loop; - - //case 0xEF: // SLEEP - //case 0xFF: // STOP - - } // switch - - // unhandled instructions fall out of switch so emulator can catch them - -stop: - pc--; - - { - int temp; - CALC_STATUS( temp ); - r.status = (uint8_t) temp; - } - - r.pc = pc; - r.sp = (uint8_t) GET_SP(); - r.a = (uint8_t) a; - r.x = (uint8_t) x; - r.y = (uint8_t) y; - - return remain_; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h b/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h deleted file mode 100644 index 2252663b..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h +++ /dev/null @@ -1,57 +0,0 @@ -// Super Nintendo (SNES) SPC-700 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_CPU_H -#define SPC_CPU_H - -#include "blargg_common.h" - -typedef unsigned spc_addr_t; -typedef blargg_long spc_time_t; - -class Snes_Spc; - -class Spc_Cpu { - typedef BOOST::uint8_t uint8_t; - uint8_t* const ram; -public: - // Keeps pointer to 64K RAM - Spc_Cpu( Snes_Spc* spc, uint8_t* ram ); - - // SPC-700 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - } r; - - // Run CPU for at least 'count' cycles. Return the number of cycles remaining - // when emulation stopped (negative if extra cycles were emulated). Emulation - // stops when there are no more remaining cycles or an unhandled instruction - // is encountered (STOP, SLEEP, and any others not yet implemented). In the - // latter case, the return value is greater than zero. - spc_time_t run( spc_time_t count ); - - // Number of clock cycles remaining for current run() call - spc_time_t remain() const; - - // Access memory as the emulated CPU does - int read ( spc_addr_t ); - void write( spc_addr_t, int ); - -private: - // noncopyable - Spc_Cpu( const Spc_Cpu& ); - Spc_Cpu& operator = ( const Spc_Cpu& ); - unsigned mem_bit( spc_addr_t ); - - spc_time_t remain_; - Snes_Spc& emu; -}; - -inline spc_time_t Spc_Cpu::remain() const { return remain_; } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp b/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp deleted file mode 100644 index 3d934f63..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp +++ /dev/null @@ -1,666 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -// Based on Brad Martin's OpenSPC DSP emulator - -#include "Spc_Dsp.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2002 Brad Martin */ -/* Copyright (C) 2004-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" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ ) -{ - set_gain( 1.0 ); - mute_voices( 0 ); - disable_surround( false ); - - assert( offsetof (globals_t,unused9 [2]) == register_count ); - assert( sizeof (voice) == register_count ); - blargg_verify_byte_order(); -} - -void Spc_Dsp::mute_voices( int mask ) -{ - for ( int i = 0; i < voice_count; i++ ) - voice_state [i].enabled = (mask >> i & 1) ? 31 : 7; -} - -void Spc_Dsp::reset() -{ - keys = 0; - echo_ptr = 0; - noise_count = 0; - noise = 1; - fir_offset = 0; - - g.flags = 0xE0; // reset, mute, echo off - g.key_ons = 0; - - for ( int i = 0; i < voice_count; i++ ) - { - voice_t& v = voice_state [i]; - v.on_cnt = 0; - v.volume [0] = 0; - v.volume [1] = 0; - v.envstate = state_release; - } - - memset( fir_buf, 0, sizeof fir_buf ); -} - -void Spc_Dsp::write( int i, int data ) -{ - require( (unsigned) i < register_count ); - - reg [i] = data; - int high = i >> 4; - switch ( i & 0x0F ) - { - // voice volume - case 0: - case 1: { - short* volume = voice_state [high].volume; - int left = (int8_t) reg [i & ~1]; - int right = (int8_t) reg [i | 1]; - volume [0] = left; - volume [1] = right; - // kill surround only if enabled and signs of volumes differ - if ( left * right < surround_threshold ) - { - if ( left < 0 ) - volume [0] = -left; - else - volume [1] = -right; - } - break; - } - - // fir coefficients - case 0x0F: - fir_coeff [high] = (int8_t) data; // sign-extend - break; - } -} - -// This table is for envelope timing. It represents the number of counts -// that should be subtracted from the counter each sample period (32kHz). -// The counter starts at 30720 (0x7800). Each count divides exactly into -// 0x7800 without remainder. -const int env_rate_init = 0x7800; -static short const env_rates [0x20] = -{ - 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, - 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, - 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, - 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 -}; - -const int env_range = 0x800; - -inline int Spc_Dsp::clock_envelope( int v ) -{ /* Return value is current - * ENVX */ - raw_voice_t& raw_voice = this->voice [v]; - voice_t& voice = voice_state [v]; - - int envx = voice.envx; - if ( voice.envstate == state_release ) - { - /* - * Docs: "When in the state of "key off". the "click" sound is - * prevented by the addition of the fixed value 1/256" WTF??? - * Alright, I'm going to choose to interpret that this way: - * When a note is keyed off, start the RELEASE state, which - * subtracts 1/256th each sample period (32kHz). Note there's - * no need for a count because it always happens every update. - */ - envx -= env_range / 256; - if ( envx <= 0 ) - { - envx = 0; - keys &= ~(1 << v); - return -1; - } - voice.envx = envx; - raw_voice.envx = envx >> 8; - return envx; - } - - int cnt = voice.envcnt; - int adsr1 = raw_voice.adsr [0]; - if ( adsr1 & 0x80 ) - { - switch ( voice.envstate ) - { - case state_attack: { - // increase envelope by 1/64 each step - int t = adsr1 & 15; - if ( t == 15 ) - { - envx += env_range / 2; - } - else - { - cnt -= env_rates [t * 2 + 1]; - if ( cnt > 0 ) - break; - envx += env_range / 64; - cnt = env_rate_init; - } - if ( envx >= env_range ) - { - envx = env_range - 1; - voice.envstate = state_decay; - } - voice.envx = envx; - break; - } - - case state_decay: { - // Docs: "DR... [is multiplied] by the fixed value - // 1-1/256." Well, at least that makes some sense. - // Multiplying ENVX by 255/256 every time DECAY is - // updated. - cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - int sustain_level = raw_voice.adsr [1] >> 5; - - if ( envx <= (sustain_level + 1) * 0x100 ) - voice.envstate = state_sustain; - break; - } - - case state_sustain: - // Docs: "SR [is multiplied] by the fixed value 1-1/256." - // Multiplying ENVX by 255/256 every time SUSTAIN is - // updated. - cnt -= env_rates [raw_voice.adsr [1] & 0x1F]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - break; - - case state_release: - // handled above - break; - } - } - else - { /* GAIN mode is set */ - /* - * Note: if the game switches between ADSR and GAIN modes - * partway through, should the count be reset, or should it - * continue from where it was? Does the DSP actually watch for - * that bit to change, or does it just go along with whatever - * it sees when it performs the update? I'm going to assume - * the latter and not update the count, unless I see a game - * that obviously wants the other behavior. The effect would - * be pretty subtle, in any case. - */ - int t = raw_voice.gain; - if (t < 0x80) - { - envx = voice.envx = t << 4; - } - else switch (t >> 5) - { - case 4: /* Docs: "Decrease (linear): Subtraction - * of the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= env_range / 64; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 5: /* Docs: "Drecrease (exponential): - * Multiplication by the fixed value - * 1-1/256." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 6: /* Docs: "Increase (linear): Addition of - * the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx += env_range / 64; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - case 7: /* Docs: "Increase (bent line): Addition - * of the constant 1/64 up to .75 of the - * constaint 1/256 from .75 to 1." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - if ( envx < env_range * 3 / 4 ) - envx += env_range / 64; - else - envx += env_range / 256; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - } - } - voice.envcnt = cnt; - raw_voice.envx = envx >> 4; - return envx; -} - -// Clamp n into range -32768 <= n <= 32767 -inline int clamp_16( int n ) -{ - if ( (BOOST::int16_t) n != n ) - n = BOOST::int16_t (0x7FFF - (n >> 31)); - return n; -} - -void Spc_Dsp::run( long count, short* out_buf ) -{ - // to do: make clock_envelope() inline so that this becomes a leaf function? - - // Should we just fill the buffer with silence? Flags won't be cleared - // during this run so it seems it should keep resetting every sample. - if ( g.flags & 0x80 ) - reset(); - - struct src_dir { - char start [2]; - char loop [2]; - }; - - const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100]; - - int left_volume = g.left_volume; - int right_volume = g.right_volume; - if ( left_volume * right_volume < surround_threshold ) - right_volume = -right_volume; // kill global surround - left_volume *= emu_gain; - right_volume *= emu_gain; - - while ( --count >= 0 ) - { - // Here we check for keys on/off. Docs say that successive writes - // to KON/KOF must be separated by at least 2 Ts periods or risk - // being neglected. Therefore DSP only looks at these during an - // update, and not at the time of the write. Only need to do this - // once however, since the regs haven't changed over the whole - // period we need to catch up with. - - g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX. - - if ( g.noise_enables ) - { - noise_count -= env_rates [g.flags & 0x1F]; - if ( noise_count <= 0 ) - { - noise_count = env_rate_init; - - noise_amp = BOOST::int16_t (noise * 2); - - // TODO: switch to Galios style - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - - // What is the expected behavior when pitch modulation is enabled on - // voice 0? Jurassic Park 2 does this. Assume 0 for now. - blargg_long prev_outx = 0; - - int echol = 0; - int echor = 0; - int left = 0; - int right = 0; - for ( int vidx = 0; vidx < voice_count; vidx++ ) - { - const int vbit = 1 << vidx; - raw_voice_t& raw_voice = voice [vidx]; - voice_t& voice = voice_state [vidx]; - - if ( voice.on_cnt && !--voice.on_cnt ) - { - // key on - keys |= vbit; - voice.addr = GET_LE16( sd [raw_voice.waveform].start ); - voice.block_remain = 1; - voice.envx = 0; - voice.block_header = 0; - voice.fraction = 0x3FFF; // decode three samples immediately - voice.interp0 = 0; // BRR decoder filter uses previous two samples - voice.interp1 = 0; - - // NOTE: Real SNES does *not* appear to initialize the - // envelope counter to anything in particular. The first - // cycle always seems to come at a random time sooner than - // expected; as yet, I have been unable to find any - // pattern. I doubt it will matter though, so we'll go - // ahead and do the full time for now. - voice.envcnt = env_rate_init; - voice.envstate = state_attack; - } - - if ( g.key_ons & vbit & ~g.key_offs ) - { - // voice doesn't come on if key off is set - g.key_ons &= ~vbit; - voice.on_cnt = 8; - } - - if ( keys & g.key_offs & vbit ) - { - // key off - voice.envstate = state_release; - voice.on_cnt = 0; - } - - int envx; - if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) - { - raw_voice.envx = 0; - raw_voice.outx = 0; - prev_outx = 0; - continue; - } - - // Decode samples when fraction >= 1.0 (0x1000) - for ( int n = voice.fraction >> 12; --n >= 0; ) - { - if ( !--voice.block_remain ) - { - if ( voice.block_header & 1 ) - { - g.wave_ended |= vbit; - - if ( voice.block_header & 2 ) - { - // verified (played endless looping sample and ENDX was set) - voice.addr = GET_LE16( sd [raw_voice.waveform].loop ); - } - else - { - // first block was end block; don't play anything (verified) - goto sample_ended; // to do: find alternative to goto - } - } - - voice.block_header = ram [voice.addr++]; - voice.block_remain = 16; // nybbles - } - - // if next block has end flag set, *this* block ends *early* (verified) - if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 && - (voice.block_header & 3) != 3 ) - { - sample_ended: - g.wave_ended |= vbit; - keys &= ~vbit; - raw_voice.envx = 0; - voice.envx = 0; - // add silence samples to interpolation buffer - do - { - voice.interp3 = voice.interp2; - voice.interp2 = voice.interp1; - voice.interp1 = voice.interp0; - voice.interp0 = 0; - } - while ( --n >= 0 ); - break; - } - - int delta = ram [voice.addr]; - if ( voice.block_remain & 1 ) - { - delta <<= 4; // use lower nybble - voice.addr++; - } - - // Use sign-extended upper nybble - delta = int8_t (delta) >> 4; - - // For invalid ranges (D,E,F): if the nybble is negative, - // the result is F000. If positive, 0000. Nothing else - // like previous range, etc seems to have any effect. If - // range is valid, do the shift normally. Note these are - // both shifted right once to do the filters properly, but - // the output will be shifted back again at the end. - int shift = voice.block_header >> 4; - delta = (delta << shift) >> 1; - if ( shift > 0x0C ) - delta = (delta >> 14) & ~0x7FF; - - // One, two and three point IIR filters - int smp1 = voice.interp0; - int smp2 = voice.interp1; - if ( voice.block_header & 8 ) - { - delta += smp1; - delta -= smp2 >> 1; - if ( !(voice.block_header & 4) ) - { - delta += (-smp1 - (smp1 >> 1)) >> 5; - delta += smp2 >> 5; - } - else - { - delta += (-smp1 * 13) >> 7; - delta += (smp2 + (smp2 >> 1)) >> 4; - } - } - else if ( voice.block_header & 4 ) - { - delta += smp1 >> 1; - delta += (-smp1) >> 5; - } - - voice.interp3 = voice.interp2; - voice.interp2 = smp2; - voice.interp1 = smp1; - voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend - } - - // rate (with possible modulation) - int rate = GET_LE16( raw_voice.rate ) & 0x3FFF; - if ( g.pitch_mods & vbit ) - rate = (rate * (prev_outx + 32768)) >> 15; - - // Gaussian interpolation using most recent 4 samples - int index = voice.fraction >> 2 & 0x3FC; - voice.fraction = (voice.fraction & 0x0FFF) + rate; - const BOOST::int16_t* table = (BOOST::int16_t const*) ((char const*) gauss + index); - const BOOST::int16_t* table2 = (BOOST::int16_t const*) ((char const*) gauss + (255*4 - index)); - int s = ((table [0] * voice.interp3) >> 12) + - ((table [1] * voice.interp2) >> 12) + - ((table2 [1] * voice.interp1) >> 12); - s = (BOOST::int16_t) (s * 2); - s += (table2 [0] * voice.interp0) >> 11 & ~1; - int output = clamp_16( s ); - if ( g.noise_enables & vbit ) - output = noise_amp; - - // scale output and set outx values - output = (output * envx) >> 11 & ~1; - - // output and apply muting (by setting voice.enabled to 31) - // if voice is externally disabled (not a SNES feature) - int l = (voice.volume [0] * output) >> voice.enabled; - int r = (voice.volume [1] * output) >> voice.enabled; - prev_outx = output; - raw_voice.outx = int8_t (output >> 8); - if ( g.echo_ons & vbit ) - { - echol += l; - echor += r; - } - left += l; - right += r; - } - // end of channel loop - - // main volume control - left = (left * left_volume ) >> (7 + emu_gain_bits); - right = (right * right_volume) >> (7 + emu_gain_bits); - - // Echo FIR filter - - // read feedback from echo buffer - int echo_ptr = this->echo_ptr; - uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF]; - echo_ptr += 4; - if ( echo_ptr >= (g.echo_delay & 15) * 0x800 ) - echo_ptr = 0; - int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend - int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend - this->echo_ptr = echo_ptr; - - // put samples in history ring buffer - const int fir_offset = this->fir_offset; - short (*fir_pos) [2] = &fir_buf [fir_offset]; - this->fir_offset = (fir_offset + 7) & 7; // move backwards one step - fir_pos [0] [0] = (short) fb_left; - fir_pos [0] [1] = (short) fb_right; - fir_pos [8] [0] = (short) fb_left; // duplicate at +8 eliminates wrap checking below - fir_pos [8] [1] = (short) fb_right; - - // FIR - fb_left = fb_left * fir_coeff [7] + - fir_pos [1] [0] * fir_coeff [6] + - fir_pos [2] [0] * fir_coeff [5] + - fir_pos [3] [0] * fir_coeff [4] + - fir_pos [4] [0] * fir_coeff [3] + - fir_pos [5] [0] * fir_coeff [2] + - fir_pos [6] [0] * fir_coeff [1] + - fir_pos [7] [0] * fir_coeff [0]; - - fb_right = fb_right * fir_coeff [7] + - fir_pos [1] [1] * fir_coeff [6] + - fir_pos [2] [1] * fir_coeff [5] + - fir_pos [3] [1] * fir_coeff [4] + - fir_pos [4] [1] * fir_coeff [3] + - fir_pos [5] [1] * fir_coeff [2] + - fir_pos [6] [1] * fir_coeff [1] + - fir_pos [7] [1] * fir_coeff [0]; - - left += (fb_left * g.left_echo_volume ) >> 14; - right += (fb_right * g.right_echo_volume) >> 14; - - // echo buffer feedback - if ( !(g.flags & 0x20) ) - { - echol += (fb_left * g.echo_feedback) >> 14; - echor += (fb_right * g.echo_feedback) >> 14; - SET_LE16( echo_buf , clamp_16( echol ) ); - SET_LE16( echo_buf + 2, clamp_16( echor ) ); - } - - if ( out_buf ) - { - // write final samples - - left = clamp_16( left ); - right = clamp_16( right ); - - int mute = g.flags & 0x40; - - out_buf [0] = (short) left; - out_buf [1] = (short) right; - out_buf += 2; - - // muting - if ( mute ) - { - out_buf [-2] = 0; - out_buf [-1] = 0; - } - } - } -} - -// Base normal_gauss table is almost exactly (with an error of 0 or -1 for each entry): -// int normal_gauss [512]; -// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45 - -// Interleved gauss table (to improve cache coherency). -// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] -const BOOST::int16_t Spc_Dsp::gauss [512] = -{ - 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, - 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, - 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, - 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, - 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, - 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, - 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, - 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, - 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, - 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, - 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, - 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, - 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, - 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, - 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, - 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, - 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, - 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, - 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, - 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, - 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, - 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, - 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, - 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, - 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, - 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, - 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, - 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, - 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, - 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, - 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, - 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, -}; diff --git a/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h b/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h deleted file mode 100644 index 36492275..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h +++ /dev/null @@ -1,152 +0,0 @@ -// Super Nintendo (SNES) SPC DSP emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_DSP_H -#define SPC_DSP_H - -#include "blargg_common.h" - -class Spc_Dsp { - typedef BOOST::int8_t int8_t; - typedef BOOST::uint8_t uint8_t; -public: - - // Keeps pointer to 64K ram - Spc_Dsp( uint8_t* ram ); - - // Mute voice n if bit n (1 << n) of mask is clear. - enum { voice_count = 8 }; - void mute_voices( int mask ); - - // Clear state and silence everything. - void reset(); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to - // the 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated - void disable_surround( bool disable ); - - // Read/write register 'n', where n ranges from 0 to register_count - 1. - enum { register_count = 128 }; - int read ( int n ); - void write( int n, int ); - - // Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL. - void run( long count, short* buf = NULL ); - - -// End of public interface -private: - - struct raw_voice_t { - int8_t left_vol; - int8_t right_vol; - uint8_t rate [2]; - uint8_t waveform; - uint8_t adsr [2]; // envelope rates for attack, decay, and sustain - uint8_t gain; // envelope gain (if not using ADSR) - int8_t envx; // current envelope level - int8_t outx; // current sample - int8_t unused [6]; - }; - - struct globals_t { - int8_t unused1 [12]; - int8_t left_volume; // 0C Main Volume Left (-.7) - int8_t echo_feedback; // 0D Echo Feedback (-.7) - int8_t unused2 [14]; - int8_t right_volume; // 1C Main Volume Right (-.7) - int8_t unused3 [15]; - int8_t left_echo_volume; // 2C Echo Volume Left (-.7) - uint8_t pitch_mods; // 2D Pitch Modulation on/off for each voice - int8_t unused4 [14]; - int8_t right_echo_volume; // 3C Echo Volume Right (-.7) - uint8_t noise_enables; // 3D Noise output on/off for each voice - int8_t unused5 [14]; - uint8_t key_ons; // 4C Key On for each voice - uint8_t echo_ons; // 4D Echo on/off for each voice - int8_t unused6 [14]; - uint8_t key_offs; // 5C key off for each voice (instantiates release mode) - uint8_t wave_page; // 5D source directory (wave table offsets) - int8_t unused7 [14]; - uint8_t flags; // 6C flags and noise freq - uint8_t echo_page; // 6D - int8_t unused8 [14]; - uint8_t wave_ended; // 7C - uint8_t echo_delay; // 7D ms >> 4 - char unused9 [2]; - }; - - union { - raw_voice_t voice [voice_count]; - uint8_t reg [register_count]; - globals_t g; - }; - - uint8_t* const ram; - - // Cache of echo FIR values for faster access - short fir_coeff [voice_count]; - - // fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code - short fir_buf [16] [2]; - int fir_offset; // (0 to 7) - - enum { emu_gain_bits = 8 }; - int emu_gain; - - int keyed_on; // 8-bits for 8 voices - int keys; - - int echo_ptr; - int noise_amp; - int noise; - int noise_count; - - int surround_threshold; - - static BOOST::int16_t const gauss []; - - enum state_t { - state_attack, - state_decay, - state_sustain, - state_release - }; - - struct voice_t { - short volume [2]; - short fraction;// 12-bit fractional position - short interp3; // most recent four decoded samples - short interp2; - short interp1; - short interp0; - short block_remain; // number of nybbles remaining in current block - unsigned short addr; - short block_header; // header byte from current block - short envcnt; - short envx; - short on_cnt; - short enabled; // 7 if enabled, 31 if disabled - short envstate; - short unused; // pad to power of 2 - }; - - voice_t voice_state [voice_count]; - - int clock_envelope( int ); -}; - -inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; } - -inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); } - -inline int Spc_Dsp::read( int i ) -{ - assert( (unsigned) i < register_count ); - return reg [i]; -} - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp deleted file mode 100644 index 22be9e2a..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp +++ /dev/null @@ -1,326 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Spc_Emu.h" - -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2004-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" - -Spc_Emu::Spc_Emu() -{ - set_type( gme_spc_type ); - - static const char* const names [Snes_Spc::voice_count] = { - "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" - }; - set_voice_names( names ); - - set_gain( 1.4 ); -} - -Spc_Emu::~Spc_Emu() { } - -// Track info - -long const trailer_offset = 0x10200; - -byte const* Spc_Emu::trailer() const { return &file_data [min( file_size, trailer_offset )]; } - -long Spc_Emu::trailer_size() const { return max( 0L, file_size - trailer_offset ); } - -static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) -{ - // header - byte const* end = begin + size; - if ( size < 8 || memcmp( begin, "xid6", 4 ) ) - { - check( false ); - return; - } - long info_size = get_le32( begin + 4 ); - byte const* in = begin + 8; - if ( end - in > info_size ) - { - dprintf( "Extra data after SPC xid6 info\n" ); - end = in + info_size; - } - - int year = 0; - char copyright [256 + 5]; - int copyright_len = 0; - int const year_len = 5; - - while ( end - in >= 4 ) - { - // header - int id = in [0]; - int data = in [3] * 0x100 + in [2]; - int type = in [1]; - int len = type ? data : 0; - in += 4; - if ( len > end - in ) - { - check( false ); - break; // block goes past end of data - } - - // handle specific block types - char* field = 0; - switch ( id ) - { - case 0x01: field = out->song; break; - case 0x02: field = out->game; break; - case 0x03: field = out->author; break; - case 0x04: field = out->dumper; break; - case 0x07: field = out->comment; break; - case 0x14: year = data; break; - - //case 0x30: // intro length - // Many SPCs have intro length set wrong for looped tracks, making it useless - /* - case 0x30: - check( len == 4 ); - if ( len >= 4 ) - { - out->intro_length = get_le32( in ) / 64; - if ( out->length > 0 ) - { - long loop = out->length - out->intro_length; - if ( loop >= 2000 ) - out->loop_length = loop; - } - } - break; - */ - - case 0x13: - copyright_len = min( len, (int) sizeof copyright - year_len ); - memcpy( ©right [year_len], in, copyright_len ); - break; - - default: - if ( id < 0x01 || (id > 0x07 && id < 0x10) || - (id > 0x14 && id < 0x30) || id > 0x36 ) - dprintf( "Unknown SPC xid6 block: %X\n", (int) id ); - break; - } - if ( field ) - { - check( type == 1 ); - Gme_File::copy_field_( field, (char const*) in, len ); - } - - // skip to next block - in += len; - - // blocks are supposed to be 4-byte aligned with zero-padding... - byte const* unaligned = in; - while ( (in - begin) & 3 && in < end ) - { - if ( *in++ != 0 ) - { - // ...but some files have no padding - in = unaligned; - dprintf( "SPC info tag wasn't properly padded to align\n" ); - break; - } - } - } - - char* p = ©right [year_len]; - if ( year ) - { - *--p = ' '; - for ( int n = 4; n--; ) - { - *--p = char (year % 10 + '0'); - year /= 10; - } - copyright_len += year_len; - } - if ( copyright_len ) - Gme_File::copy_field_( out->copyright, p, copyright_len ); - - check( in == end ); -} - -static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size, - track_info_t* out ) -{ - // decode length (can be in text or binary format, sometimes ambiguous ugh) - long len_secs = 0; - for ( int i = 0; i < 3; i++ ) - { - unsigned n = h.len_secs [i] - '0'; - if ( n > 9 ) - { - // ignore single-digit text lengths - // (except if author field is present and begins at offset 1, ugh) - if ( i == 1 && (h.author [0] || !h.author [1]) ) - len_secs = 0; - break; - } - len_secs *= 10; - len_secs += n; - } - if ( !len_secs || len_secs > 0x1FFF ) - len_secs = get_le16( h.len_secs ); - if ( len_secs < 0x1FFF ) - out->length = len_secs * 1000; - - int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); - Gme_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset ); - - GME_COPY_FIELD( h, out, song ); - GME_COPY_FIELD( h, out, game ); - GME_COPY_FIELD( h, out, dumper ); - GME_COPY_FIELD( h, out, comment ); - - if ( xid6_size ) - get_spc_xid6( xid6, xid6_size, out ); -} - -blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const -{ - get_spc_info( header(), trailer(), trailer_size(), out ); - return 0; -} - -static blargg_err_t check_spc_header( void const* header ) -{ - if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Spc_File : Gme_Info_ -{ - Spc_Emu::header_t header; - blargg_vector xid6; - - Spc_File() { set_type( gme_spc_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - long file_size = in.remain(); - if ( file_size < Snes_Spc::spc_file_size ) - return gme_wrong_file_type; - RETURN_ERR( in.read( &header, Spc_Emu::header_size ) ); - RETURN_ERR( check_spc_header( header.tag ) ); - long const xid6_offset = 0x10200; - long xid6_size = file_size - xid6_offset; - if ( xid6_size > 0 ) - { - RETURN_ERR( xid6.resize( xid6_size ) ); - RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_size ) ); - RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); - } - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - get_spc_info( header, xid6.begin(), xid6.size(), out ); - return 0; - } -}; - -static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } -static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } - -gme_type_t_ const gme_spc_type [1] = { "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }; - -// Setup - -blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) -{ - apu.set_gain( gain() ); - if ( sample_rate != native_sample_rate ) - { - RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) ); - resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 ); - } - return 0; -} - -void Spc_Emu::mute_voices_( int m ) -{ - Music_Emu::mute_voices_( m ); - apu.mute_voices( m ); -} - -blargg_err_t Spc_Emu::load_mem_( byte const* in, long size ) -{ - assert( offsetof (header_t,unused2 [46]) == header_size ); - file_data = in; - file_size = size; - set_voice_count( Snes_Spc::voice_count ); - if ( size < Snes_Spc::spc_file_size ) - return gme_wrong_file_type; - return check_spc_header( in ); -} - -// Emulation - -void Spc_Emu::set_tempo_( double t ) { apu.set_tempo( t ); } - -blargg_err_t Spc_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - resampler.clear(); - RETURN_ERR( apu.load_spc( file_data, file_size ) ); - apu.clear_echo(); - return 0; -} - -blargg_err_t Spc_Emu::skip_( long count ) -{ - if ( sample_rate() != native_sample_rate ) - { - count = long (count * resampler.ratio()) & ~1; - count -= resampler.skip_input( count ); - } - - // TODO: shouldn't skip be adjusted for the 64 samples read afterwards? - - if ( count > 0 ) - RETURN_ERR( apu.skip( count ) ); - - // eliminate pop due to resampler - const int resampler_latency = 64; - sample_t buf [resampler_latency]; - return play_( resampler_latency, buf ); -} - -blargg_err_t Spc_Emu::play_( long count, sample_t* out ) -{ - if ( sample_rate() == native_sample_rate ) - return apu.play( count, out ); - - long remain = count; - while ( remain > 0 ) - { - remain -= resampler.read( &out [count - remain], remain ); - if ( remain > 0 ) - { - long n = resampler.max_write(); - RETURN_ERR( apu.play( n, resampler.buffer() ) ); - resampler.write( n ); - } - } - check( remain == 0 ); - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h deleted file mode 100644 index 44b54c30..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h +++ /dev/null @@ -1,77 +0,0 @@ -// Super Nintendo SPC music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_EMU_H -#define SPC_EMU_H - -#include "Fir_Resampler.h" -#include "Music_Emu.h" -#include "Snes_Spc.h" - -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 }; - - // SPC file header - enum { header_size = 0x100 }; - struct header_t - { - 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_data; } - - // Prevents channels and global volumes from being phase-negated - void disable_surround( bool disable = true ); - - static gme_type_t static_type() { return gme_spc_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - byte const* trailer() const; // use track_info() - long trailer_size() const; - -public: - Spc_Emu(); - ~Spc_Emu(); -protected: - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t set_sample_rate_( long ); - blargg_err_t start_track_( int ); - blargg_err_t play_( long, sample_t* ); - blargg_err_t skip_( long ); - void mute_voices_( int ); - void set_tempo_( double ); -private: - byte const* file_data; - long file_size; - Fir_Resampler<24> resampler; - Snes_Spc apu; -}; - -inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp deleted file mode 100644 index 0fef6bd9..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp +++ /dev/null @@ -1,412 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Vgm_Emu.h" - -#include "blargg_endian.h" -#include -#include - -/* 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" - -double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow -double const rolloff = 0.990; -double const oversample_factor = 1.5; - -Vgm_Emu::Vgm_Emu() -{ - disable_oversampling_ = false; - psg_rate = 0; - set_type( gme_vgm_type ); - - static int const types [8] = { - wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0 - }; - set_voice_types( types ); - - set_silence_lookahead( 1 ); // tracks should already be trimmed - - static equalizer_t const eq = { -14.0, 80 }; - set_equalizer( eq ); -} - -Vgm_Emu::~Vgm_Emu() { } - -// 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 long check_gd3_header( byte const* h, long remain ) -{ - if ( remain < gd3_header_size ) return 0; - if ( memcmp( h, "Gd3 ", 4 ) ) return 0; - if ( get_le32( h + 4 ) >= 0x200 ) return 0; - - long gd3_size = get_le32( h + 8 ); - if ( gd3_size > remain - gd3_header_size ) return 0; - - return gd3_size; -} - -byte const* Vgm_Emu::gd3_data( int* size ) const -{ - if ( size ) - *size = 0; - - long gd3_offset = get_le32( header().gd3_offset ) - 0x2C; - if ( gd3_offset < 0 ) - return 0; - - byte const* gd3 = data + header_size + gd3_offset; - long gd3_size = check_gd3_header( gd3, data_end - gd3 ); - if ( !gd3_size ) - return 0; - - if ( size ) - *size = gd3_size + gd3_header_size; - - return gd3; -} - -static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) -{ - long length = get_le32( h.track_duration ) * 10 / 441; - if ( length > 0 ) - { - long 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; - } - else - { - out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase) - out->intro_length = length; // make it clear that track is no longer than length - out->loop_length = 0; - } - } -} - -blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const -{ - get_vgm_length( header(), out ); - - int size; - byte const* gd3 = gd3_data( &size ); - if ( gd3 ) - parse_gd3( gd3 + gd3_header_size, gd3 + size, out ); - - return 0; -} - -static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h ) -{ - if ( memcmp( h.tag, "Vgm ", 4 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Vgm_File : Gme_Info_ -{ - Vgm_Emu::header_t h; - blargg_vector gd3; - - Vgm_File() { set_type( gme_vgm_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - long file_size = in.remain(); - if ( file_size <= Vgm_Emu::header_size ) - return gme_wrong_file_type; - - RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); - RETURN_ERR( check_vgm_header( h ) ); - - long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; - long remain = file_size - Vgm_Emu::header_size - gd3_offset; - byte gd3_h [gd3_header_size]; - if ( gd3_offset > 0 && remain >= gd3_header_size ) - { - RETURN_ERR( in.skip( gd3_offset ) ); - RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); - long 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() ) ); - } - } - return 0; - } - - 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 0; - } -}; - -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 ) -{ - if ( psg_rate ) - { - vgm_rate = (long) (44100 * t + 0.5); - blip_time_factor = (long) floor( double (1L << blip_time_bits) / vgm_rate * psg_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 = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 ); - //vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 ); - - fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); - } -} - -blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate ) -{ - RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) ); - return Classic_Emu::set_sample_rate_( sample_rate ); -} - -void Vgm_Emu::update_eq( blip_eq_t const& eq ) -{ - psg.treble_eq( eq ); - dac_synth.treble_eq( eq ); -} - -void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - if ( i < psg.osc_count ) - psg.osc_output( i, c, l, r ); -} - -void Vgm_Emu::mute_voices_( int mask ) -{ - Classic_Emu::mute_voices_( mask ); - dac_synth.output( &blip_buf ); - if ( uses_fm ) - { - psg.output( (mask & 0x80) ? 0 : &blip_buf ); - if ( ym2612.enabled() ) - { - dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); - ym2612.mute_voices( mask ); - } - - if ( ym2413.enabled() ) - { - int m = mask & 0x3F; - if ( mask & 0x20 ) - m |= 0x01E0; // channels 5-8 - if ( mask & 0x40 ) - m |= 0x3E00; - ym2413.mute_voices( m ); - } - } -} - -blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) -{ - assert( offsetof (header_t,unused2 [8]) == header_size ); - - if ( new_size <= header_size ) - return gme_wrong_file_type; - - header_t const& h = *(header_t const*) new_data; - - RETURN_ERR( check_vgm_header( h ) ); - - check( get_le32( h.version ) <= 0x150 ); - - // psg rate - psg_rate = get_le32( h.psg_rate ); - if ( !psg_rate ) - psg_rate = 3579545; - blip_buf.clock_rate( psg_rate ); - - data = new_data; - data_end = new_data + new_size; - - // get loop - loop_begin = data_end; - if ( get_le32( h.loop_offset ) ) - loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; - - set_voice_count( psg.osc_count ); - - RETURN_ERR( setup_fm() ); - - 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( uses_fm ? fm_names : psg_names ); - - // do after FM in case output buffer is changed - return Classic_Emu::setup_buffer( psg_rate ); -} - -blargg_err_t Vgm_Emu::setup_fm() -{ - long ym2612_rate = get_le32( header().ym2612_rate ); - long ym2413_rate = get_le32( header().ym2413_rate ); - if ( ym2413_rate && get_le32( header().version ) < 0x110 ) - update_fm_rates( &ym2413_rate, &ym2612_rate ); - - uses_fm = false; - - fm_rate = blip_buf.sample_rate() * oversample_factor; - - if ( ym2612_rate ) - { - uses_fm = true; - if ( disable_oversampling_ ) - fm_rate = ym2612_rate / 144.0; - Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); - RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); - ym2612.enable( true ); - set_voice_count( 8 ); - } - - if ( !uses_fm && ym2413_rate ) - { - uses_fm = true; - if ( disable_oversampling_ ) - fm_rate = ym2413_rate / 72.0; - Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); - int result = ym2413.set_rate( fm_rate, ym2413_rate ); - if ( result == 2 ) - return "YM2413 FM sound isn't supported"; - CHECK_ALLOC( !result ); - ym2413.enable( true ); - set_voice_count( 8 ); - } - - if ( uses_fm ) - { - RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); - psg.volume( 0.135 * fm_gain * gain() ); - } - else - { - ym2612.enable( false ); - ym2413.enable( false ); - psg.volume( gain() ); - } - - return 0; -} - -// Emulation - -blargg_err_t Vgm_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - psg.reset( get_le16( header().noise_feedback ), header().noise_width ); - - dac_disabled = -1; - pos = data + header_size; - pcm_data = pos; - pcm_pos = pos; - dac_amp = -1; - vgm_time = 0; - if ( get_le32( header().version ) >= 0x150 ) - { - long data_offset = get_le32( header().data_offset ); - check( data_offset ); - if ( data_offset ) - pos += data_offset + offsetof (header_t,data_offset) - 0x40; - } - - if ( uses_fm ) - { - if ( ym2413.enabled() ) - ym2413.reset(); - - if ( ym2612.enabled() ) - ym2612.reset(); - - fm_time_offset = 0; - blip_buf.clear(); - Dual_Resampler::clear(); - } - return 0; -} - -blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) -{ - time_io = run_commands( msec * vgm_rate / 1000 ); - psg.end_frame( time_io ); - return 0; -} - -blargg_err_t Vgm_Emu::play_( long count, sample_t* out ) -{ - if ( !uses_fm ) - return Classic_Emu::play_( count, out ); - - Dual_Resampler::dual_play( count, out, blip_buf ); - return 0; -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h deleted file mode 100644 index bcb784d5..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h +++ /dev/null @@ -1,84 +0,0 @@ -// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator - -// Game_Music_Emu 0.5.2 -#ifndef VGM_EMU_H -#define VGM_EMU_H - -#include "Vgm_Emu_Impl.h" - -// Emulates VGM music using SN76489/SN76496 PSG, 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. Currently YM2413 support requires that you supply a -// YM2413 sound chip emulator. I can provide one I've modified to work with the library. -class Vgm_Emu : public Vgm_Emu_Impl { -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 !uses_fm; } - - // Disable 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 header format - enum { header_size = 0x40 }; - struct header_t - { - char tag [4]; - byte data_size [4]; - byte version [4]; - byte psg_rate [4]; - byte ym2413_rate [4]; - byte gd3_offset [4]; - byte track_duration [4]; - byte loop_offset [4]; - byte loop_duration [4]; - byte frame_rate [4]; - byte noise_feedback [2]; - byte noise_width; - byte unused1; - byte ym2612_rate [4]; - byte ym2151_rate [4]; - byte data_offset [4]; - byte unused2 [8]; - }; - - // Header for currently loaded file - header_t const& header() const { return *(header_t const*) data; } - - static gme_type_t static_type() { return gme_vgm_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - byte const* gd3_data( int* size_out = 0 ) const; // use track_info() - -public: - Vgm_Emu(); - ~Vgm_Emu(); -protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t set_sample_rate_( long sample_rate ); - blargg_err_t start_track_( int ); - blargg_err_t play_( long count, sample_t* ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void mute_voices_( int mask ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); -private: - // removed; use disable_oversampling() and set_tempo() instead - Vgm_Emu( bool oversample, double tempo = 1.0 ); - double fm_rate; - long psg_rate; - long vgm_rate; - bool disable_oversampling_; - bool uses_fm; - blargg_err_t setup_fm(); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp b/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp deleted file mode 100644 index a2d7c93e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp +++ /dev/null @@ -1,314 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Vgm_Emu.h" - -#include -#include -#include "blargg_endian.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" - -enum { - cmd_gg_stereo = 0x4F, - cmd_psg = 0x50, - cmd_ym2413 = 0x51, - cmd_ym2612_port0 = 0x52, - cmd_ym2612_port1 = 0x53, - cmd_ym2151 = 0x54, - cmd_delay = 0x61, - cmd_delay_735 = 0x62, - cmd_delay_882 = 0x63, - cmd_byte_delay = 0x64, - cmd_end = 0x66, - cmd_data_block = 0x67, - cmd_short_delay = 0x70, - cmd_pcm_delay = 0x80, - cmd_pcm_seek = 0xE0, - - pcm_block_type = 0x00, - ym2612_dac_port = 0x2A -}; - -inline int command_len( int command ) -{ - switch ( command >> 4 ) - { - case 0x03: - case 0x04: - return 2; - - case 0x05: - case 0x0A: - case 0x0B: - return 3; - - case 0x0C: - case 0x0D: - return 4; - - case 0x0E: - case 0x0F: - return 5; - } - - check( false ); - return 1; -} - -template -inline void Ym_Emu::begin_frame( short* p ) -{ - require( enabled() ); - out = p; - last_time = 0; -} - -template -inline int Ym_Emu::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; -} - -inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const -{ - return (t * fm_time_factor + fm_time_offset) >> fm_time_bits; -} - -inline blip_time_t Vgm_Emu_Impl::to_blip_time( vgm_time_t t ) const -{ - return (t * blip_time_factor) >> blip_time_bits; -} - -void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp ) -{ - blip_time_t blip_time = to_blip_time( vgm_time ); - int old = dac_amp; - int delta = amp - old; - dac_amp = amp; - if ( old >= 0 ) - dac_synth.offset_inline( blip_time, delta, &blip_buf ); - else - dac_amp |= dac_disabled; -} - -blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) -{ - vgm_time_t vgm_time = this->vgm_time; - byte const* pos = this->pos; - if ( pos >= data_end ) - { - set_track_ended(); - if ( pos > data_end ) - set_warning( "Stream lacked end event" ); - } - - while ( vgm_time < end_time && pos < data_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: - pos = loop_begin; // if not looped, loop_begin == data_end - break; - - case cmd_delay_735: - vgm_time += 735; - break; - - case cmd_delay_882: - vgm_time += 882; - break; - - case cmd_gg_stereo: - psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ ); - break; - - case cmd_psg: - psg.write_data( to_blip_time( vgm_time ), *pos++ ); - break; - - case cmd_delay: - vgm_time += pos [1] * 0x100L + pos [0]; - pos += 2; - break; - - case cmd_byte_delay: - vgm_time += *pos++; - break; - - case cmd_ym2413: - if ( ym2413.run_until( to_fm_time( vgm_time ) ) ) - ym2413.write( pos [0], pos [1] ); - pos += 2; - break; - - case cmd_ym2612_port0: - if ( pos [0] == ym2612_dac_port ) - { - write_pcm( vgm_time, pos [1] ); - } - else if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) - { - if ( pos [0] == 0x2B ) - { - dac_disabled = (pos [1] >> 7 & 1) - 1; - dac_amp |= dac_disabled; - } - ym2612.write0( pos [0], pos [1] ); - } - pos += 2; - break; - - case cmd_ym2612_port1: - if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) - ym2612.write1( pos [0], pos [1] ); - pos += 2; - break; - - case cmd_data_block: { - check( *pos == cmd_end ); - int type = pos [1]; - long size = get_le32( pos + 2 ); - pos += 6; - if ( type == pcm_block_type ) - pcm_data = pos; - pos += size; - break; - } - - case cmd_pcm_seek: - pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L + - pos [1] * 0x100L + pos [0]; - pos += 4; - break; - - default: - int cmd = pos [-1]; - switch ( cmd & 0xF0 ) - { - case cmd_pcm_delay: - write_pcm( vgm_time, *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_blip_time( end_time ); -} - -int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) -{ - // to do: timing is working mostly by luck - - int min_pairs = sample_count >> 1; - int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1; - assert( to_fm_time( vgm_time ) <= min_pairs ); - int pairs = min_pairs; - while ( (pairs = to_fm_time( vgm_time )) < min_pairs ) - vgm_time++; - //dprintf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs ); - - if ( ym2612.enabled() ) - { - ym2612.begin_frame( buf ); - memset( buf, 0, pairs * stereo * sizeof *buf ); - } - else if ( ym2413.enabled() ) - { - ym2413.begin_frame( buf ); - } - - run_commands( vgm_time ); - ym2612.run_until( pairs ); - ym2413.run_until( pairs ); - - fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - - ((long) pairs << fm_time_bits); - - psg.end_frame( blip_time ); - - return pairs * stereo; -} - -// Update pre-1.10 header FM rates by scanning commands -void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const -{ - byte const* p = data + 0x40; - while ( p < data_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_ym2413: - *ym2612_rate = 0; - return; - - case cmd_ym2612_port0: - case cmd_ym2612_port1: - *ym2612_rate = *ym2413_rate; - *ym2413_rate = 0; - return; - - case cmd_ym2151: - *ym2413_rate = 0; - *ym2612_rate = 0; - return; - - default: - p += command_len( *p ); - } - } -} diff --git a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h b/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h deleted file mode 100644 index 4d387d09..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h +++ /dev/null @@ -1,71 +0,0 @@ -// Low-level parts of Vgm_Emu - -// Game_Music_Emu 0.5.2 -#ifndef VGM_EMU_IMPL_H -#define VGM_EMU_IMPL_H - -#include "Dual_Resampler.h" -#include "Classic_Emu.h" -#include "Ym2413_Emu.h" -#include "Ym2612_Emu.h" -#include "Sms_Apu.h" - -template -class Ym_Emu : public Emu { -protected: - int last_time; - short* out; - enum { disabled_time = -1 }; -public: - Ym_Emu() : last_time( disabled_time ), out( NULL ) { } - void enable( bool b ) { last_time = b ? 0 : disabled_time; } - bool enabled() const { return last_time != disabled_time; } - void begin_frame( short* p ); - int run_until( int time ); -}; - -class Vgm_Emu_Impl : public Classic_Emu, private Dual_Resampler { -public: - typedef Classic_Emu::sample_t sample_t; -protected: - enum { stereo = 2 }; - - typedef int vgm_time_t; - - enum { fm_time_bits = 12 }; - typedef int fm_time_t; - long fm_time_offset; - int fm_time_factor; - fm_time_t to_fm_time( vgm_time_t ) const; - - enum { blip_time_bits = 12 }; - int blip_time_factor; - blip_time_t to_blip_time( vgm_time_t ) const; - - byte const* data; - byte const* loop_begin; - byte const* data_end; - void update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const; - - vgm_time_t vgm_time; - byte const* pos; - blip_time_t run_commands( vgm_time_t ); - int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); - - byte const* pcm_data; - byte const* pcm_pos; - int dac_amp; - int dac_disabled; // -1 if disabled - void write_pcm( vgm_time_t, int amp ); - - Ym_Emu ym2612; - Ym_Emu ym2413; - - Blip_Buffer blip_buf; - Sms_Apu psg; - Blip_Synth dac_synth; - - friend class Vgm_Emu; -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp deleted file mode 100644 index ede67304..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp +++ /dev/null @@ -1,21 +0,0 @@ - -// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip - -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Ym2413_Emu.h" - -Ym2413_Emu::Ym2413_Emu() { } - -Ym2413_Emu::~Ym2413_Emu() { } - -int Ym2413_Emu::set_rate( double, double ) { return 2; } - -void Ym2413_Emu::reset() { } - -void Ym2413_Emu::write( int, int ) { } - -void Ym2413_Emu::mute_voices( int ) { } - -void Ym2413_Emu::run( int, sample_t* ) { } - diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h deleted file mode 100644 index 98a2a48e..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h +++ /dev/null @@ -1,33 +0,0 @@ -// YM2413 FM sound chip emulator interface - -// Game_Music_Emu 0.5.2 -#ifndef YM2413_EMU_H -#define YM2413_EMU_H - -class Ym2413_Emu { - struct OPLL* opll; -public: - Ym2413_Emu(); - ~Ym2413_Emu(); - - // Set output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( double sample_rate, double clock_rate ); - - // Reset to power-up state - void reset(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { channel_count = 14 }; - void mute_voices( int mask ); - - // Write 'data' to 'addr' - void write( int addr, int data ); - - // Run and write pair_count 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/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp b/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp deleted file mode 100644 index 41ebb093..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp +++ /dev/null @@ -1,1319 +0,0 @@ -// Game_Music_Emu 0.5.2. 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-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 */ - -// 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 only 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 - int env_xor; - int env_max; - - 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 -}; - -#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) - -#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 - -inline void set_seg( slot_t& s, int seg ) -{ - s.env_xor = 0; - s.env_max = INT_MAX; - s.SEG = seg; - if ( seg & 4 ) - { - s.env_xor = ENV_MASK; - s.env_max = ENV_MASK; - } -} - -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 */ - - set_seg( sl, (data & 8) ? (data & 0x0F) : 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 > sample_rate ); - - int i; - - // 144 = 12 * (prescale * 2) = 12 * 6 * 2 - // prescale set to 6 by default - - double Frequence = clock_rate / sample_rate / 144.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 - { - double x = MAX_OUT; // Max output - x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage - - g.TL_TAB [i] = (int) x; - 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++) - { - double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus - x = 20 * log10(1 / x); // 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 = 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 - - g.LFO_ENV_TAB [i] = (int) x; - - x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus - x *= (double) ((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(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); - x *= ENV_LENGHT; - - g.ENV_TAB [i] = (int) x; - - // Decay curve (just linear) - x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); - x *= ENV_LENGHT; - - g.ENV_TAB [ENV_LENGHT + i] = (int) x; - } - 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; // 3 and not 6 (Mickey Mania first music for test) - x /= ENV_STEP; - - 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 - - for(i = 0; i < 2048; i++) - { - double x = (double) (i) * 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 - - g.FINC_TAB [i] = (unsigned int) x; - } - - // 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 = 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 g.ENV_TAB - - 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 - - for(i = 0; i < 4; i++) - { - for (int j = 0; j < 32; j++) - { -#if ((SIN_LBITS + SIN_HBITS - 21) < 0) - double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); -#else - double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); -#endif - - g.DT_TAB [i + 0] [j] = (int) y; - g.DT_TAB [i + 4] [j] = (int) -y; - } - } - - // Tableau LFO - g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (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 - { - int release = sl->SEG & 1; - - if ( !release ) - { - // re KEY ON - - // sl->Fcnt = 0; - // sl->ChgEnM = ~0; - - sl->Ecnt = 0; - sl->Einc = sl->EincA; - sl->Ecmp = ENV_DECAY; - sl->Ecurp = ATTACK; - } - - set_seg( *sl, (sl->SEG << 1) & 4 ); - - if ( !release ) - 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] + ch.SLOT [S##x].TLL; \ - int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \ - ((temp##x - ch.SLOT [S##x].env_max) >> 31); - - 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 >>= 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)) + (1L << (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; - - long 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 ); } diff --git a/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h b/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h deleted file mode 100644 index 383ac72d..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h +++ /dev/null @@ -1,38 +0,0 @@ -// YM2612 FM sound chip emulator interface - -// Game_Music_Emu 0.5.2 -#ifndef YM2612_EMU_H -#define YM2612_EMU_H - -struct Ym2612_Impl; - -class Ym2612_Emu { - Ym2612_Impl* impl; -public: - Ym2612_Emu() { impl = 0; } - ~Ym2612_Emu(); - - // Set output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - const char* set_rate( double sample_rate, double clock_rate ); - - // Reset to power-up state - void reset(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { channel_count = 6 }; - void mute_voices( int mask ); - - // Write addr to register 0 then data to register 1 - void write0( int addr, int data ); - - // Write addr to register 2 then data to register 3 - void write1( int addr, int data ); - - // Run and add pair_count 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/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h b/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h deleted file mode 100644 index 9ab0bd7d..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h +++ /dev/null @@ -1,179 +0,0 @@ -// Sets up common environment for Shay Green's libraries. -// To change configuration options, modify blargg_config.h, not this file. - -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -#include -#include -#include -#include -#include - -#undef BLARGG_COMMON_H -// allow blargg_config.h to #include blargg_common.h -#include "blargg_config.h" -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -// STATIC_CAST(T,expr): Used in place of static_cast (expr) -#ifndef STATIC_CAST - #define STATIC_CAST(T,expr) ((T) (expr)) -#endif - -// blargg_err_t (0 on success, otherwise error string) -#ifndef blargg_err_t - typedef const char* blargg_err_t; -#endif - -// blargg_vector - very lightweight vector of POD types (no constructor/destructor) -template -class blargg_vector { - T* begin_; - size_t size_; -public: - blargg_vector() : begin_( 0 ), size_( 0 ) { } - ~blargg_vector() { free( begin_ ); } - size_t size() const { return size_; } - T* begin() const { return begin_; } - T* end() const { return begin_ + size_; } - blargg_err_t resize( size_t n ) - { - void* p = realloc( begin_, n * sizeof (T) ); - if ( !p && n ) - return "Out of memory"; - begin_ = (T*) p; - size_ = n; - return 0; - } - void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } - T& operator [] ( size_t n ) const - { - assert( n <= size_ ); // <= to allow past-the-end value - return begin_ [n]; - } -}; - -#ifndef BLARGG_DISABLE_NOTHROW - #if __cplusplus < 199711 - #define BLARGG_THROWS( spec ) - #else - #define BLARGG_THROWS( spec ) throw spec - #endif - #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ - void operator delete ( void* p ) { free( p ); } - #define BLARGG_NEW new -#else - #include - #define BLARGG_NEW new (std::nothrow) -#endif - -#define BLARGG_4CHAR( a, b, c, d ) \ - ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) - -// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -#ifndef BOOST_STATIC_ASSERT - #ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) - #else - // Some other compilers fail when declaring same function multiple times in class, - // so differentiate them by line - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) - #endif -#endif - -// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, -// compiler is assumed to support bool. If undefined, availability is determined. -#ifndef BLARGG_COMPILER_HAS_BOOL - #if defined (__MWERKS__) - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (_MSC_VER) - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (__GNUC__) - // supports bool - #elif __cplusplus < 199711 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif -#endif -#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL - // If you get errors here, modify your blargg_config.h file - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough -#include - -#if 0 -#if INT_MAX >= 0x7FFFFFFF - typedef int blargg_long; -#else - typedef long blargg_long; -#endif -#endif -typedef int64_t blargg_long; - -#if UINT_MAX >= 0xFFFFFFFF - typedef unsigned blargg_ulong; -#else - typedef unsigned long blargg_ulong; -#endif - -// BOOST::int8_t etc. - -// HAVE_STDINT_H: If defined, use for int8_t etc. -#if defined (HAVE_STDINT_H) - #include - #define BOOST - -// HAVE_INTTYPES_H: If defined, use for int8_t etc. -#elif defined (HAVE_INTTYPES_H) - #include - #define BOOST - -#else - struct BOOST - { - #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F - typedef signed char int8_t; - typedef unsigned char uint8_t; - #else - // No suitable 8-bit type available - typedef struct see_blargg_common_h int8_t; - typedef struct see_blargg_common_h uint8_t; - #endif - - #if USHRT_MAX == 0xFFFF - typedef short int16_t; - typedef unsigned short uint16_t; - #else - // No suitable 16-bit type available - typedef struct see_blargg_common_h int16_t; - typedef struct see_blargg_common_h uint16_t; - #endif - - #if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; - #elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; - #else - // No suitable 32-bit type available - typedef struct see_blargg_common_h int32_t; - typedef struct see_blargg_common_h uint32_t; - #endif - }; -#endif - -#endif -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h b/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h deleted file mode 100644 index 9e9c751d..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h +++ /dev/null @@ -1,30 +0,0 @@ -// Library configuration. Modify this file as necessary. - -#ifndef BLARGG_CONFIG_H -#define BLARGG_CONFIG_H - -// Uncomment to use zlib for transparent decompression of gzipped files -//#define HAVE_ZLIB_H - -// Uncomment to support only the listed game music types. See gme_type_list.cpp -// for a list of all types. -//#define GME_TYPE_LIST gme_nsf_type, gme_gbs_type - -// Uncomment to enable platform-specific optimizations -//#define BLARGG_NONPORTABLE 1 - -// Uncomment to use faster, lower quality sound synthesis -//#define BLIP_BUFFER_FAST 1 - -// Uncomment if automatic byte-order determination doesn't work -//#define BLARGG_BIG_ENDIAN 1 - -// Uncomment if you get errors in the bool section of blargg_common.h -//#define BLARGG_COMPILER_HAS_BOOL 1 - -// Use standard config.h if present -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h b/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h deleted file mode 100644 index 67165565..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h +++ /dev/null @@ -1,158 +0,0 @@ -// CPU Byte Order Utilities - -// Game_Music_Emu 0.5.2 -#ifndef BLARGG_ENDIAN -#define BLARGG_ENDIAN - -#include "blargg_common.h" - -// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) -#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 1 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one may be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else - -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 -#endif - -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__mips__) || defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#else - // No endian specified; assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN -#endif - -inline void blargg_verify_byte_order() -{ - #ifndef NDEBUG - #if BLARGG_BIG_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i == 0 ); - #elif BLARGG_LITTLE_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i != 0 ); - #endif - #endif -} - -inline unsigned get_le16( void const* p ) { - return ((unsigned char const*) p) [1] * 0x100u + - ((unsigned char const*) p) [0]; -} -inline unsigned get_be16( void const* p ) { - return ((unsigned char const*) p) [0] * 0x100u + - ((unsigned char const*) p) [1]; -} -inline blargg_ulong get_le32( void const* p ) { - return ((unsigned char const*) p) [3] * 0x01000000u + - ((unsigned char const*) p) [2] * 0x00010000u + - ((unsigned char const*) p) [1] * 0x00000100u + - ((unsigned char const*) p) [0]; -} -inline blargg_ulong get_be32( void const* p ) { - return ((unsigned char const*) p) [0] * 0x01000000u + - ((unsigned char const*) p) [1] * 0x00010000u + - ((unsigned char const*) p) [2] * 0x00000100u + - ((unsigned char const*) p) [3]; -} -inline void set_le16( void* p, unsigned n ) { - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be16( void* p, unsigned n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} -inline void set_le32( void* p, blargg_ulong n ) { - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be32( void* p, blargg_ulong n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [3] = (unsigned char) n; -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #endif - - #if BLARGG_CPU_POWERPC && defined (__MWERKS__) - // PowerPC has special byte-reversed instructions - // to do: assumes that PowerPC is running in big-endian mode - // to do: implement for other compilers which don't support these macros - #define GET_LE16( addr ) (__lhbrx( (addr), 0 )) - #define GET_LE32( addr ) (__lwbrx( (addr), 0 )) - #define SET_LE16( addr, data ) (__sthbrx( (data), (addr), 0 )) - #define SET_LE32( addr, data ) (__stwbrx( (data), (addr), 0 )) - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h b/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h deleted file mode 100644 index 945bf349..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h +++ /dev/null @@ -1,78 +0,0 @@ -// Included at the beginning of library source files, after all other #include lines -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert( expr ) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -inline void blargg_dprintf_( const char*, ... ) { } -#undef dprintf -#define dprintf (1) ? (void) 0 : blargg_dprintf_ - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#define check( expr ) ((void) 0) - -// If expr yields error string, return it from current function, otherwise continue. -#undef RETURN_ERR -#define RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is 0, return out of memory error string. -#undef CHECK_ALLOC -#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) - -// Avoid any macros which evaluate their arguments multiple times -#undef min -#undef max - -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -template -inline T min( T x, T y ) -{ - if ( x < y ) - return x; - return y; -} - -template -inline T max( T x, T y ) -{ - if ( x < y ) - return y; - return x; -} - -// TODO: good idea? bad idea? -#undef byte -#define byte byte_ -typedef unsigned char byte; - -// deprecated -#define BLARGG_CHECK_ALLOC CHECK_ALLOC -#define BLARGG_RETURN_ERR RETURN_ERR - -// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check -#ifdef BLARGG_SOURCE_BEGIN - #include BLARGG_SOURCE_BEGIN -#endif - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h b/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h deleted file mode 100644 index ada99ead..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h +++ /dev/null @@ -1,72 +0,0 @@ - -#include "Gbs_Emu.h" - -#include "blargg_source.h" - -int Gbs_Emu::cpu_read( gb_addr_t addr ) -{ - int result = *cpu::get_code( addr ); - if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) - result = apu.read_register( clock(), addr ); -#ifndef NDEBUG - else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) - dprintf( "Read from unmapped memory $%.4x\n", (unsigned) addr ); - else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 ) - dprintf( "Unhandled I/O read 0x%4X\n", (unsigned) addr ); -#endif - return result; -} - -void Gbs_Emu::cpu_write( gb_addr_t addr, int data ) -{ - unsigned offset = addr - ram_addr; - if ( offset <= 0xFFFF - ram_addr ) - { - ram [offset] = data; - if ( (addr ^ 0xE000) <= 0x1F80 - 1 ) - { - if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) - { - GME_APU_HOOK( this, addr - Gb_Apu::start_addr, data ); - apu.write_register( clock(), addr, data ); - } - else if ( (addr ^ 0xFF06) < 2 ) - update_timer(); - else if ( addr == joypad_addr ) - ram [offset] = 0; // keep joypad return value 0 - else - ram [offset] = 0xFF; - - //if ( addr == 0xFFFF ) - // dprintf( "Wrote interrupt mask\n" ); - } - } - else if ( (addr ^ 0x2000) <= 0x2000 - 1 ) - { - set_bank( data ); - } -#ifndef NDEBUG - else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) - { - dprintf( "Wrote to unmapped memory $%.4x\n", (unsigned) addr ); - } -#endif -} - -#define CPU_READ_FAST( cpu, addr, time, out ) \ - CPU_READ_FAST_( STATIC_CAST(Gbs_Emu*,cpu), addr, time, out ) - -#define CPU_READ_FAST_( emu, addr, time, out ) \ -{\ - out = READ_PROG( addr );\ - if ( unsigned (addr - Gb_Apu::start_addr) <= Gb_Apu::register_count )\ - out = emu->apu.read_register( emu->cpu_time - time * clocks_per_instr, addr );\ - else\ - check( out == emu->cpu_read( addr ) );\ -} - -#define CPU_READ( cpu, addr, time ) \ - STATIC_CAST(Gbs_Emu*,cpu)->cpu_read( addr ) - -#define CPU_WRITE( cpu, addr, data, time ) \ - STATIC_CAST(Gbs_Emu*,cpu)->cpu_write( addr, data ) diff --git a/gme/Game_Music_Emu-0.5.2/gme/gme.cpp b/gme/Game_Music_Emu-0.5.2/gme/gme.cpp deleted file mode 100644 index d6cebfa8..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/gme.cpp +++ /dev/null @@ -1,256 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Music_Emu.h" - -#if !GME_DISABLE_STEREO_DEPTH -#include "Effects_Buffer.h" -#endif -#include "blargg_endian.h" -#include -#include - -/* 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" - -#ifndef GME_TYPE_LIST - -// Default list of all supported game music types (copy this to blargg_config.h -// if you want to modify it) -#define GME_TYPE_LIST \ - gme_ay_type,\ - gme_gbs_type,\ - gme_gym_type,\ - gme_hes_type,\ - gme_kss_type,\ - gme_nsf_type,\ - gme_nsfe_type,\ - gme_sap_type,\ - gme_spc_type,\ - gme_vgm_type,\ - gme_vgz_type - -#endif - -static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; - -gme_type_t const* gme_type_list() -{ - return gme_type_list_; -} - -const char* gme_identify_header( void const* header ) -{ - switch ( get_be32( header ) ) - { - case BLARGG_4CHAR('Z','X','A','Y'): return "AY"; - case BLARGG_4CHAR('G','B','S',0x01): return "GBS"; - case BLARGG_4CHAR('G','Y','M','X'): return "GYM"; - case BLARGG_4CHAR('H','E','S','M'): return "HES"; - case BLARGG_4CHAR('K','S','C','C'): - case BLARGG_4CHAR('K','S','S','X'): return "KSS"; - case BLARGG_4CHAR('N','E','S','M'): return "NSF"; - case BLARGG_4CHAR('N','S','F','E'): return "NSFE"; - case BLARGG_4CHAR('S','A','P',0x0D): return "SAP"; - case BLARGG_4CHAR('S','N','E','S'): return "SPC"; - case BLARGG_4CHAR('V','g','m',' '): return "VGM"; - } - return ""; -} - -static void to_uppercase( const char* in, int len, char* out ) -{ - for ( int i = 0; i < len; i++ ) - { - if ( !(out [i] = toupper( in [i] )) ) - return; - } - *out = 0; // extension too long -} - -gme_type_t gme_identify_extension( const char* extension_ ) -{ - char const* end = strrchr( extension_, '.' ); - if ( end ) - extension_ = end + 1; - - char extension [6]; - to_uppercase( extension_, sizeof extension, extension ); - - for ( gme_type_t const* types = gme_type_list_; *types; types++ ) - if ( !strcmp( extension, (*types)->extension_ ) ) - return *types; - return 0; -} - -gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) -{ - *type_out = gme_identify_extension( path ); - // TODO: don't examine header if file has extension? - if ( !*type_out ) - { - char header [4]; - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - RETURN_ERR( in.read( header, sizeof header ) ); - *type_out = gme_identify_extension( gme_identify_header( header ) ); - } - return 0; -} - -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ) -{ - require( (data || !size) && out ); - *out = 0; - - gme_type_t file_type = 0; - if ( size >= 4 ) - file_type = gme_identify_extension( gme_identify_header( data ) ); - if ( !file_type ) - return gme_wrong_file_type; - - Music_Emu* emu = gme_new_emu( file_type, sample_rate ); - CHECK_ALLOC( emu ); - - gme_err_t err = gme_load_data( emu, data, size ); - - if ( err ) - delete emu; - else - *out = emu; - - return err; -} - -gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ) -{ - require( path && out ); - *out = 0; - - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - - char header [4]; - int header_size = 0; - - gme_type_t file_type = gme_identify_extension( path ); - if ( !file_type ) - { - header_size = sizeof header; - RETURN_ERR( in.read( header, sizeof header ) ); - file_type = gme_identify_extension( gme_identify_header( header ) ); - } - if ( !file_type ) - return gme_wrong_file_type; - - Music_Emu* emu = gme_new_emu( file_type, sample_rate ); - CHECK_ALLOC( emu ); - - // optimization: avoids seeking/re-reading header - Remaining_Reader rem( header, header_size, &in ); - gme_err_t err = emu->load( rem ); - in.close(); - - if ( err ) - delete emu; - else - *out = emu; - - return err; -} - -Music_Emu* gme_new_emu( gme_type_t type, long rate ) -{ - if ( type ) - { - if ( rate == gme_info_only ) - return type->new_info(); - - Music_Emu* me = type->new_emu(); - if ( me ) - { - #if !GME_DISABLE_STEREO_DEPTH - if ( type->flags_ & 1 ) - { - me->effects_buffer = BLARGG_NEW Effects_Buffer; - if ( me->effects_buffer ) - me->set_buffer( me->effects_buffer ); - } - - if ( !(type->flags_ & 1) || me->effects_buffer ) - #endif - { - if ( !me->set_sample_rate( rate ) ) - { - check( me->type() == type ); - return me; - } - } - delete me; - } - } - return 0; -} - -gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } - -gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) -{ - Mem_File_Reader in( data, size ); - return me->load( in ); -} - -gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) -{ - Callback_Reader in( func, size, data ); - return me->load( in ); -} - -void gme_delete( Music_Emu* me ) { delete me; } - -gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } - -const char* gme_warning( Music_Emu* me ) { return me->warning(); } - -int gme_track_count( Music_Emu const* me ) { return me->track_count(); } - -const char* gme_track_info( Music_Emu const* me, track_info_t* out, int track ) -{ - return me->track_info( out, track ); -} - -void gme_set_stereo_depth( Music_Emu* me, double depth ) -{ -#if !GME_DISABLE_STEREO_DEPTH - if ( me->effects_buffer ) - STATIC_CAST(Effects_Buffer*,me->effects_buffer)->set_depth( depth ); -#endif -} - -void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } -void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } -void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } - -gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } -gme_err_t gme_play ( Music_Emu* me, long n, short* p ) { return me->play( n, p ); } -void gme_set_fade ( Music_Emu* me, long start_msec ) { me->set_fade( start_msec ); } -int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } -long gme_tell ( Music_Emu const* me ) { return me->tell(); } -gme_err_t gme_seek ( Music_Emu* me, long msec ) { return me->seek( msec ); } -int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } -void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } -void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } -void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } -void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } -void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) { me->set_equalizer( *eq ); } -gme_equalizer_t gme_equalizer( Music_Emu const* me ) { return me->equalizer(); } -const char** gme_voice_names ( Music_Emu const* me ) { return me->voice_names(); } diff --git a/gme/Game_Music_Emu-0.5.2/gme/gme.h b/gme/Game_Music_Emu-0.5.2/gme/gme.h deleted file mode 100644 index 469c901c..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/gme.h +++ /dev/null @@ -1,222 +0,0 @@ -/* Game music emulator library C interface (also usable from C++) */ - -/* Game_Music_Emu 0.5.2 */ -#ifndef GME_H -#define GME_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Error string returned by library functions, or NULL if no error (success) */ -typedef const char* gme_err_t; - -/* First parameter of most gme_ functions is a pointer to the Music_Emu */ -typedef struct Music_Emu Music_Emu; - - -/******** Basic operations ********/ - -/* Create emulator and load game music file/data into it. Sets *out to new emulator. */ -gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ); - -/* Number of tracks available */ -int gme_track_count( Music_Emu const* ); - -/* Start a track, where 0 is the first track */ -gme_err_t gme_start_track( Music_Emu*, int index ); - -/* Generate 'count' 16-bit signed samples info 'out'. Output is in stereo. */ -gme_err_t gme_play( Music_Emu*, long count, short* out ); - -/* Finish using emulator and free memory */ -void gme_delete( Music_Emu* ); - - -/******** Track position/length ********/ - -/* Set time to start fading track out. Once fade ends track_ended() returns true. -Fade time can be changed while track is playing. */ -void gme_set_fade( Music_Emu*, long start_msec ); - -/* True if a track has reached its end */ -int gme_track_ended( Music_Emu const* ); - -/* Number of milliseconds (1000 = one second) played since beginning of track */ -long gme_tell( Music_Emu const* ); - -/* Seek to new time in track. Seeking backwards or far forward can take a while. */ -gme_err_t gme_seek( Music_Emu*, long msec ); - - -/******** Informational ********/ - -/* If you only need track information from a music file, pass gme_info_only for -sample_rate to open/load. */ -enum { gme_info_only = -1 }; - -/* Most recent warning string, or NULL if none. Clears current warning after returning. -Warning is also cleared when loading a file and starting a track. */ -const char* gme_warning( Music_Emu* ); - -/* Load m3u playlist file (must be done after loading music) */ -gme_err_t gme_load_m3u( Music_Emu*, const char* path ); - -/* Clear any loaded m3u playlist and any internal playlist that the music format -supports (NSFE for example). */ -void gme_clear_playlist( Music_Emu* ); - -/* Get information for a particular track (length, name, author, etc.) */ -typedef struct track_info_t track_info_t; -gme_err_t gme_track_info( Music_Emu const*, track_info_t* out, int track ); - -struct track_info_t -{ - long track_count; - - /* times in milliseconds; -1 if unknown */ - long length; - long intro_length; - long loop_length; - - /* empty string if not available */ - char system [256]; - char game [256]; - char song [256]; - char author [256]; - char copyright [256]; - char comment [256]; - char dumper [256]; -}; -enum { gme_max_field = 255 }; - - -/******** Advanced playback ********/ - -/* Adjust stereo echo depth, where 0.0 = off and 1.0 = maximum. Has no effect for -GYM, SPC, and Sega Genesis VGM music */ -void gme_set_stereo_depth( Music_Emu*, double depth ); - -/* Disable automatic end-of-track detection and skipping of silence at beginning -if ignore is true */ -void gme_ignore_silence( Music_Emu*, int ignore ); - -/* Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. -Track length as returned by track_info() assumes a tempo of 1.0. */ -void gme_set_tempo( Music_Emu*, double tempo ); - -/* Number of voices used by currently loaded file */ -int gme_voice_count( Music_Emu const* ); - -/* Names of voices */ -const char** gme_voice_names( Music_Emu const* ); - -/* Mute/unmute voice i, where voice 0 is first voice */ -void gme_mute_voice( Music_Emu*, int index, int mute ); - -/* Set muting state of all voices at once using a bit mask, where -1 mutes all -voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ -void gme_mute_voices( Music_Emu*, int muting_mask ); - -/* Frequency equalizer parameters (see gme.txt) */ -typedef struct gme_equalizer_t -{ - double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ - long bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ -} gme_equalizer_t; - -/* Get current frequency equalizater parameters */ -gme_equalizer_t gme_equalizer( Music_Emu const* ); - -/* Change frequency equalizer parameters */ -void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); - - - -/******** Game music types ********/ - -/* gme_type_t is a pointer to this structure. For example, gme_nsf_type->system is -"Nintendo NES" and gme_nsf_type->new_emu() is equilvant to new Nsf_Emu (in C++). */ -typedef struct gme_type_t_ const* gme_type_t; -struct gme_type_t_ -{ - const char* system; /* name of system this music file type is generally for */ - int track_count; /* non-zero for formats with a fixed number of tracks */ - Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ - Music_Emu* (*new_info)(); /* Create new info reader for this type */ - - /* internal */ - const char* extension_; - int flags_; -}; - -/* Emulator type constants for each supported file type */ -extern struct gme_type_t_ const gme_ay_type [], gme_gbs_type [], gme_gym_type [], - gme_hes_type [], gme_kss_type [], gme_nsf_type [], gme_nsfe_type [], - gme_sap_type [], gme_spc_type [], gme_vgm_type [], gme_vgz_type []; - -/* Type of this emulator */ -gme_type_t gme_type( Music_Emu const* ); - -/* Pointer to array of all music types, with NULL entry at end. Allows a player linked -to this library to support new music types without having to be updated. */ -gme_type_t const* gme_type_list(); - - -/******** Advanced file loading ********/ - -/* Error returned if file type is not supported */ -extern const char gme_wrong_file_type []; - -/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ); - -/* Determine likely game music type based on first four bytes of file. Returns -string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if -file header is not recognized. */ -const char* gme_identify_header( void const* header ); - -/* Get corresponding music type for file path or extension passed in. */ -gme_type_t gme_identify_extension( const char* path_or_extension ); - -/* Determine file type based on file's extension or header (if extension isn't recognized). -Sets *type_out to type, or 0 if unrecognized or error. */ -gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ); - -/* Create new emulator and set sample rate. Returns NULL if out of memory. If you only need -track information, pass gme_info_only for sample_rate. */ -Music_Emu* gme_new_emu( gme_type_t, long sample_rate ); - -/* Load music file into emulator */ -gme_err_t gme_load_file( Music_Emu*, const char* path ); - -/* Load music file from memory into emulator. Makes a copy of data passed. */ -gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); - -/* Load music file using custom data reader function that will be called to -read file data. Most emulators load the entire file in one read call. */ -typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, long count ); -gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); - -/* Load m3u playlist file from memory (must be done after loading music) */ -gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); - - -/******** User data ********/ - -/* Set/get pointer to data you want to associate with this emulator. -You can use this for whatever you want. */ -void gme_set_user_data( Music_Emu*, void* new_user_data ); -void* gme_user_data( Music_Emu const* ); - -/* Register cleanup function to be called when deleting emulator, or NULL to -clear it. Passes user_data to cleanup function. */ -typedef void (*gme_user_cleanup_t)( void* user_data ); -void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); - - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h b/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h deleted file mode 100644 index b3d71dad..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h +++ /dev/null @@ -1,101 +0,0 @@ - -#include "Hes_Emu.h" - -#include "blargg_source.h" - -int Hes_Emu::cpu_read( hes_addr_t addr ) -{ - check( addr <= 0xFFFF ); - int result = *cpu::get_code( addr ); - if ( mmr [addr >> page_shift] == 0xFF ) - result = cpu_read_( addr ); - return result; -} - -void Hes_Emu::cpu_write( hes_addr_t addr, int data ) -{ - check( addr <= 0xFFFF ); - byte* out = write_pages [addr >> page_shift]; - addr &= page_size - 1; - if ( out ) - out [addr] = data; - else if ( mmr [addr >> page_shift] == 0xFF ) - cpu_write_( addr, data ); -} - -inline byte const* Hes_Emu::cpu_set_mmr( int page, int bank ) -{ - write_pages [page] = 0; - if ( bank < 0x80 ) - return rom.at_addr( bank * (blargg_long) page_size ); - - byte* data = 0; - switch ( bank ) - { - case 0xF8: - data = cpu::ram; - break; - - case 0xF9: - case 0xFA: - case 0xFB: - data = &sgx [(bank - 0xF9) * page_size]; - break; - - default: - if ( bank != 0xFF ) - dprintf( "Unmapped bank $%02X\n", bank ); - return rom.unmapped(); - } - - write_pages [page] = data; - return data; -} - -#define CPU_READ_FAST( cpu, addr, time, out ) \ - CPU_READ_FAST_( STATIC_CAST(Hes_Emu*,cpu), addr, time, out ) - -#define CPU_READ_FAST_( cpu, addr, time, out ) \ -{\ - out = READ_PROG( addr );\ - if ( mmr [addr >> page_shift] == 0xFF )\ - {\ - FLUSH_TIME();\ - out = cpu->cpu_read_( addr );\ - CACHE_TIME();\ - }\ -} - -#define CPU_WRITE_FAST( cpu, addr, data, time ) \ - CPU_WRITE_FAST_( STATIC_CAST(Hes_Emu*,cpu), addr, data, time ) - -#define CPU_WRITE_FAST_( cpu, addr, data, time ) \ -{\ - byte* out = cpu->write_pages [addr >> page_shift];\ - addr &= page_size - 1;\ - if ( out )\ - {\ - out [addr] = data;\ - }\ - else if ( mmr [addr >> page_shift] == 0xFF )\ - {\ - FLUSH_TIME();\ - cpu->cpu_write_( addr, data );\ - CACHE_TIME();\ - }\ -} - -#define CPU_READ( cpu, addr, time ) \ - STATIC_CAST(Hes_Emu*,cpu)->cpu_read( addr ) - -#define CPU_WRITE( cpu, addr, data, time ) \ - STATIC_CAST(Hes_Emu*,cpu)->cpu_write( addr, data ) - -#define CPU_WRITE_VDP( cpu, addr, data, time ) \ - STATIC_CAST(Hes_Emu*,cpu)->cpu_write_vdp( addr, data ) - -#define CPU_SET_MMR( cpu, page, bank ) \ - STATIC_CAST(Hes_Emu*,cpu)->cpu_set_mmr( page, bank ) - -#define CPU_DONE( cpu, time, result_out ) \ - result_out = STATIC_CAST(Hes_Emu*,cpu)->cpu_done() diff --git a/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h b/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h deleted file mode 100644 index 4bae3793..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h +++ /dev/null @@ -1,83 +0,0 @@ - -#include "Nsf_Emu.h" - -#if !NSF_EMU_APU_ONLY - #include "Nes_Namco_Apu.h" -#endif - -#include "blargg_source.h" - -int Nsf_Emu::cpu_read( nes_addr_t addr ) -{ - int result; - - result = cpu::low_mem [addr & 0x7FF]; - if ( !(addr & 0xE000) ) - goto exit; - - result = *cpu::get_code( addr ); - if ( addr > 0x7FFF ) - goto exit; - - result = sram [addr & (sizeof sram - 1)]; - if ( addr > 0x5FFF ) - goto exit; - - if ( addr == Nes_Apu::status_addr ) - return apu.read_status( cpu::time() ); - - #if !NSF_EMU_APU_ONLY - if ( addr == Nes_Namco_Apu::data_reg_addr && namco ) - return namco->read_data(); - #endif - - result = addr >> 8; // simulate open bus - - if ( addr != 0x2002 ) - dprintf( "Read unmapped $%.4X\n", (unsigned) addr ); - -exit: - return result; -} - -void Nsf_Emu::cpu_write( nes_addr_t addr, int data ) -{ - { - nes_addr_t offset = addr ^ sram_addr; - if ( offset < sizeof sram ) - { - sram [offset] = data; - return; - } - } - { - int temp = addr & 0x7FF; - if ( !(addr & 0xE000) ) - { - cpu::low_mem [temp] = data; - return; - } - } - - if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr ) - { - GME_APU_HOOK( this, addr - Nes_Apu::start_addr, data ); - apu.write_register( cpu::time(), addr, data ); - return; - } - - unsigned bank = addr - bank_select_addr; - if ( bank < bank_count ) - { - blargg_long offset = rom.mask_addr( data * (blargg_long) bank_size ); - if ( offset >= rom.size() ) - set_warning( "Invalid bank" ); - cpu::map_code( (bank + 8) * bank_size, bank_size, rom.at_addr( offset ) ); - return; - } - - cpu_write_misc( addr, data ); -} - -#define CPU_READ( cpu, addr, time ) STATIC_CAST(Nsf_Emu&,*cpu).cpu_read( addr ) -#define CPU_WRITE( cpu, addr, data, time ) STATIC_CAST(Nsf_Emu&,*cpu).cpu_write( addr, data ) diff --git a/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h b/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h deleted file mode 100644 index 8c2f6dd0..00000000 --- a/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h +++ /dev/null @@ -1,26 +0,0 @@ - -#include "Sap_Emu.h" - -#include "blargg_source.h" - -#define CPU_WRITE( cpu, addr, data, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_write( addr, data ) - -void Sap_Emu::cpu_write( sap_addr_t addr, int data ) -{ - mem.ram [addr] = data; - if ( (addr >> 8) == 0xD2 ) - cpu_write_( addr, data ); -} - -#ifdef NDEBUG - #define CPU_READ( cpu, addr, time ) READ_LOW( addr ) -#else - #define CPU_READ( cpu, addr, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_read( addr ) - - int Sap_Emu::cpu_read( sap_addr_t addr ) - { - if ( (addr & 0xF900) == 0xD000 ) - dprintf( "Unmapped read $%04X\n", addr ); - return mem.ram [addr]; - } -#endif diff --git a/gme/Game_Music_Emu-0.5.2/license.txt b/gme/Game_Music_Emu-0.5.2/license.txt deleted file mode 100644 index 5faba9d4..00000000 --- a/gme/Game_Music_Emu-0.5.2/license.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, 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 library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete 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 distribute a copy of this License along with the -Library. - - 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 Library or any portion -of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -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 Library, 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 Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you 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. - - If distribution of 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 satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be 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. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library 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. - - 9. 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 Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -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 with -this License. - - 11. 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 Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library 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 Library. - -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. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library 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. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser 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 Library -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 Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -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 - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. 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 LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. 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 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 - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp b/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp deleted file mode 100644 index 74cb2c32..00000000 --- a/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp +++ /dev/null @@ -1,198 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Audio_Scope.h" - -#include -#include - -/* Copyright (C) 2005-2006 by Shay Green. Permission is hereby granted, free of -charge, to any person obtaining a copy of this software module and associated -documentation files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and -to permit persons to whom the Software is furnished to do so, subject to the -following conditions: The above copyright notice and this permission notice -shall be included in all copies or substantial portions of the Software. THE -SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -int const step_bits = 8; -int const step_unit = 1 << step_bits; -int const erase_color = 1; -int const draw_color = 2; - -Audio_Scope::Audio_Scope() -{ - surface = 0; - buf = 0; -} - -Audio_Scope::~Audio_Scope() -{ - free( buf ); - - if ( surface ) - SDL_FreeSurface( surface ); -} - -const char* Audio_Scope::init( int width, int height ) -{ - assert( height <= 256 ); - assert( !buf ); // can only call init() once - - buf = (byte*) calloc( width * sizeof *buf, 1 ); - if ( !buf ) - return "Out of memory"; - - low_y = 0; - high_y = height; - buf_size = width; - - for ( sample_shift = 6; sample_shift < 14; ) - if ( ((0x7FFFL * 2) >> sample_shift++) < height ) - break; - - v_offset = height / 2 - (0x10000 >> sample_shift); - - screen = SDL_SetVideoMode( width, height, 0, 0 ); - if ( !screen ) - return "Couldn't set video mode"; - - surface = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0 ); - if ( !screen ) - return "Couldn't create surface"; - - static SDL_Color palette [2] = { {0, 0, 0}, {0, 255, 0} }; - SDL_SetColors( surface, palette, 1, 2 ); - - return 0; // success -} - -const char* Audio_Scope::draw( const short* in, long count, double step ) -{ - int low = low_y; - int high = high_y; - - if ( count >= buf_size ) - { - count = buf_size; - low_y = 0x7FFF; - high_y = 0; - } - - if ( SDL_LockSurface( surface ) < 0 ) - return "Couldn't lock surface"; - render( in, count, (long) (step * step_unit) ); - SDL_UnlockSurface( surface ); - - if ( low > low_y ) - low = low_y; - - if ( high < high_y ) - high = high_y; - - SDL_Rect r; - r.x = 0; - r.w = buf_size; - r.y = low + v_offset; - r.h = high - low + 1; - - if ( SDL_BlitSurface( surface, &r, screen, &r ) < 0 ) - return "Blit to screen failed"; - - if ( SDL_Flip( screen ) < 0 ) - return "Couldn't flip screen"; - - return 0; // success -} - -void Audio_Scope::render( short const* in, long count, long step ) -{ - byte* old_pos = buf; - long surface_pitch = surface->pitch; - byte* out = (byte*) surface->pixels + v_offset * surface_pitch; - int old_erase = *old_pos; - int old_draw = 0; - long in_pos = 0; - - int low_y = this->low_y; - int high_y = this->high_y; - int half_step = (step + step_unit / 2) >> (step_bits + 1); - - while ( count-- ) - { - // Line drawing/erasing starts at previous sample and ends one short of - // current sample, except when previous and current are the same. - - // Extra read on the last iteration of line loops will always be at the - // height of the next sample, and thus within the gworld bounds. - - // Erase old line - { - int delta = *old_pos - old_erase; - int offset = old_erase * surface_pitch; - old_erase += delta; - - int next_line = surface_pitch; - if ( delta < 0 ) - { - delta = -delta; - next_line = -surface_pitch; - } - - do - { - out [offset] = erase_color; - offset += next_line; - } - while ( delta-- > 1 ); - } - - // Draw new line and put in old_buf - { - - int in_whole = in_pos >> step_bits; - int sample = (0x7FFF * 2 - in [in_whole] - in [in_whole + half_step]) >> sample_shift; - if ( !in_pos ) - old_draw = sample; - in_pos += step; - - int delta = sample - old_draw; - int offset = old_draw * surface_pitch; - old_draw += delta; - - int next_line = surface_pitch; - if ( delta < 0 ) - { - delta = -delta; - next_line = -surface_pitch; - } - - *old_pos++ = sample; - - // min/max updating can be interleved anywhere - - if ( low_y > sample ) - low_y = sample; - - do - { - out [offset] = draw_color; - offset += next_line; - } - while ( delta-- > 1 ); - - if ( high_y < sample ) - high_y = sample; - } - - out++; - } - - this->low_y = low_y; - this->high_y = high_y; -} diff --git a/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h b/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h deleted file mode 100644 index 75334676..00000000 --- a/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h +++ /dev/null @@ -1,36 +0,0 @@ -// Simple audio waveform scope in a window, using SDL multimedia library - -#ifndef AUDIO_SCOPE_H -#define AUDIO_SCOPE_H - -#include "SDL.h" - -class Audio_Scope { -public: - typedef const char* error_t; - - // Initialize scope window of specified size. Height must be 256 or less. - error_t init( int width, int height ); - - // Draw at most 'count' samples from 'in', skipping 'step' samples after - // each sample drawn. Step can be less than 1.0. - error_t draw( const short* in, long count, double step = 1.0 ); - - Audio_Scope(); - ~Audio_Scope(); - -private: - typedef unsigned char byte; - SDL_Surface* screen; - SDL_Surface* surface; - byte* buf; - int buf_size; - int sample_shift; - int low_y; - int high_y; - int v_offset; - - void render( short const* in, long count, long step ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp b/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp deleted file mode 100644 index a39819f1..00000000 --- a/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Music_Player.h" - -#include "gme/Music_Emu.h" - -#include -#include - -/* Copyright (C) 2005-2006 by Shay Green. Permission is hereby granted, free of -charge, to any person obtaining a copy of this software module and associated -documentation files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and -to permit persons to whom the Software is furnished to do so, subject to the -following conditions: The above copyright notice and this permission notice -shall be included in all copies or substantial portions of the Software. THE -SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "blargg_source.h" - -// Number of audio buffers per second. Adjust if you encounter audio skipping. -const int fill_rate = 45; - -// Simple sound driver using SDL -typedef void (*sound_callback_t)( void* data, short* out, int count ); -static const char* sound_init( long sample_rate, int buf_size, sound_callback_t, void* data ); -static void sound_start(); -static void sound_stop(); -static void sound_cleanup(); - -Music_Player::Music_Player() -{ - emu_ = 0; - scope_buf = 0; - paused = false; -} - -blargg_err_t Music_Player::init( long rate ) -{ - sample_rate = rate; - - int min_size = sample_rate * 2 / fill_rate; - int buf_size = 512; - while ( buf_size < min_size ) - buf_size *= 2; - - return sound_init( sample_rate, buf_size, fill_buffer, this ); -} - -void Music_Player::stop() -{ - sound_stop(); - delete emu_; - emu_ = 0; -} - -Music_Player::~Music_Player() -{ - stop(); - sound_cleanup(); -} - -blargg_err_t Music_Player::load_file( const char* path ) -{ - stop(); - - RETURN_ERR( gme_open_file( path, &emu_, sample_rate ) ); - - char m3u_path [256 + 5]; - strncpy( m3u_path, path, 256 ); - m3u_path [256] = 0; - char* p = strrchr( m3u_path, '.' ); - if ( !p ) - p = m3u_path + strlen( m3u_path ); - strcpy( p, ".m3u" ); - if ( emu_->load_m3u( m3u_path ) ) { } // ignore error - - return 0; -} - -int Music_Player::track_count() const -{ - return emu_ ? emu_->track_count() : false; -} - -blargg_err_t Music_Player::start_track( int track ) -{ - if ( emu_ ) - { - // Sound must not be running when operating on emulator - sound_stop(); - RETURN_ERR( emu_->start_track( track ) ); - - // Calculate track length - if ( !emu_->track_info( &track_info_ ) ) - { - if ( track_info_.length <= 0 ) - track_info_.length = track_info_.intro_length + - track_info_.loop_length * 2; - } - if ( track_info_.length <= 0 ) - track_info_.length = (long) (2.5 * 60 * 1000); - emu_->set_fade( track_info_.length ); - - paused = false; - sound_start(); - } - return 0; -} - -void Music_Player::pause( int b ) -{ - paused = b; - if ( b ) - sound_stop(); - else - sound_start(); -} - -void Music_Player::suspend() -{ - if ( !paused ) - sound_stop(); -} - -void Music_Player::resume() -{ - if ( !paused ) - sound_start(); -} - -bool Music_Player::track_ended() const -{ - return emu_ ? emu_->track_ended() : false; -} - -void Music_Player::set_stereo_depth( double tempo ) -{ - suspend(); - gme_set_stereo_depth( emu_, tempo ); - resume(); -} - -void Music_Player::set_tempo( double tempo ) -{ - suspend(); - emu_->set_tempo( tempo ); - resume(); -} - -void Music_Player::mute_voices( int mask ) -{ - suspend(); - emu_->mute_voices( mask ); - emu_->ignore_silence( mask != 0 ); - resume(); -} - -void Music_Player::fill_buffer( void* data, sample_t* out, int count ) -{ - Music_Player* self = (Music_Player*) data; - if ( self->emu_ ) - { - if ( self->emu_->play( count, out ) ) { } // ignore error - - if ( self->scope_buf ) - memcpy( self->scope_buf, out, self->scope_buf_size * sizeof *self->scope_buf ); - } -} - -// Sound output driver using SDL - -#include "SDL.h" - -static sound_callback_t sound_callback; -static void* sound_callback_data; - -static void sdl_callback( void* data, Uint8* out, int count ) -{ - if ( sound_callback ) - sound_callback( sound_callback_data, (short*) out, count / 2 ); -} - -static const char* sound_init( long sample_rate, int buf_size, - sound_callback_t cb, void* data ) -{ - sound_callback = cb; - sound_callback_data = data; - - static SDL_AudioSpec as; // making static clears all fields to 0 - as.freq = sample_rate; - as.format = AUDIO_S16SYS; - as.channels = 2; - as.callback = sdl_callback; - as.samples = buf_size; - if ( SDL_OpenAudio( &as, 0 ) < 0 ) - { - const char* err = SDL_GetError(); - if ( !err ) - err = "Couldn't open SDL audio"; - return err; - } - - return 0; -} - -static void sound_start() -{ - SDL_PauseAudio( false ); -} - -static void sound_stop() -{ - SDL_PauseAudio( true ); - - // be sure audio thread is not active - SDL_LockAudio(); - SDL_UnlockAudio(); -} - -static void sound_cleanup() -{ - sound_stop(); - SDL_CloseAudio(); -} diff --git a/gme/Game_Music_Emu-0.5.2/player/Music_Player.h b/gme/Game_Music_Emu-0.5.2/player/Music_Player.h deleted file mode 100644 index 7a573c45..00000000 --- a/gme/Game_Music_Emu-0.5.2/player/Music_Player.h +++ /dev/null @@ -1,69 +0,0 @@ -// Simple game music file player - -// Game_Music_Emu 0.5.2 -#ifndef MUSIC_PLAYER_H -#define MUSIC_PLAYER_H - -#include "gme/Music_Emu.h" - -class Music_Player { -public: - // Initialize player and set sample rate - blargg_err_t init( long sample_rate = 44100 ); - - // Load game music file. NULL on success, otherwise error string. - blargg_err_t load_file( const char* path ); - - // (Re)start playing track. Tracks are numbered from 0 to track_count() - 1. - blargg_err_t start_track( int track ); - - // Stop playing current file - void stop(); - -// Optional functions - - // Number of tracks in current file, or 0 if no file loaded. - int track_count() const; - - // Info for current track - track_info_t const& track_info() const { return track_info_; } - - // Pause/resume playing current track. - void pause( int ); - - // True if track ended - bool track_ended() const; - - // Pointer to emulator - Music_Emu* emu() const { return emu_; } - - // Set stereo depth, where 0.0 = none and 1.0 = maximum - void set_stereo_depth( double ); - - // Set tempo, where 0.5 = half speed, 1.0 = normal, 2.0 = double speed - void set_tempo( double ); - - // Set voice muting bitmask - void mute_voices( int ); - - // Set buffer to copy samples from each buffer into, or NULL to disable - typedef short sample_t; - void set_scope_buffer( sample_t* buf, int size ) { scope_buf = buf; scope_buf_size = size; } - -public: - Music_Player(); - ~Music_Player(); -private: - Music_Emu* emu_; - sample_t* scope_buf; - long sample_rate; - int scope_buf_size; - bool paused; - track_info_t track_info_; - - void suspend(); - void resume(); - static void fill_buffer( void*, sample_t*, int ); -}; - -#endif diff --git a/gme/Game_Music_Emu-0.5.2/player/player.cpp b/gme/Game_Music_Emu-0.5.2/player/player.cpp deleted file mode 100644 index a0685997..00000000 --- a/gme/Game_Music_Emu-0.5.2/player/player.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* How to play game music files with Music_Player (requires SDL library) - -Run program with path to a game music file. - -Left/Right Change track -Space Pause/unpause -E Normal/slight stereo echo/more stereo echo --/= Adjust tempo -1-9 Toggle channel on/off -0 Reset tempo and turn channels back on */ - -int const scope_width = 512; - -#include "Music_Player.h" -#include "Audio_Scope.h" - -#include -#include -#include -#include "SDL.h" - -void handle_error( const char* ); - -static bool paused; -static Audio_Scope* scope; -static Music_Player* player; -static short scope_buf [scope_width * 2]; - -static void init() -{ - // Start SDL - if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) - exit( EXIT_FAILURE ); - atexit( SDL_Quit ); - SDL_EnableKeyRepeat( 500, 80 ); - - // Init scope - scope = new Audio_Scope; - if ( !scope ) - handle_error( "Out of memory" ); - if ( scope->init( scope_width, 256 ) ) - handle_error( "Couldn't initialize scope" ); - memset( scope_buf, 0, sizeof scope_buf ); - - // Create player - player = new Music_Player; - if ( !player ) - handle_error( "Out of memory" ); - handle_error( player->init() ); - player->set_scope_buffer( scope_buf, scope_width * 2 ); -} - -static void start_track( int track, const char* path ) -{ - paused = false; - handle_error( player->start_track( track - 1 ) ); - - // update window title with track info - - long seconds = player->track_info().length / 1000; - const char* game = player->track_info().game; - if ( !*game ) - { - // extract filename - game = strrchr( path, '\\' ); // DOS - if ( !game ) - game = strrchr( path, '/' ); // UNIX - if ( !game ) - game = path; - else - game++; // skip path separator - } - - char title [512]; - sprintf( title, "%s: %d/%d %s (%ld:%02ld)", - game, track, player->track_count(), player->track_info().song, - seconds / 60, seconds % 60 ); - SDL_WM_SetCaption( title, title ); -} - -int main( int argc, char** argv ) -{ - init(); - - // Load file - const char* path = (argc > 1 ? argv [argc - 1] : "test.nsf"); - handle_error( player->load_file( path ) ); - start_track( 1, path ); - - // Main loop - int track = 1; - double tempo = 1.0; - bool running = true; - double stereo_depth = 0.0; - int muting_mask = 0; - while ( running ) - { - SDL_Delay( 1000 / 100 ); - - // Update scope - scope->draw( scope_buf, scope_width, 2 ); - - // Automatically go to next track when current one ends - if ( player->track_ended() ) - { - if ( track < player->track_count() ) - start_track( ++track, path ); - else - player->pause( paused = true ); - } - - // Handle keyboard input - SDL_Event e; - while ( SDL_PollEvent( &e ) ) - { - switch ( e.type ) - { - case SDL_QUIT: - running = false; - break; - - case SDL_KEYDOWN: - int key = e.key.keysym.sym; - switch ( key ) - { - case SDLK_q: - case SDLK_ESCAPE: // quit - running = false; - break; - - case SDLK_LEFT: // prev track - if ( !paused && !--track ) - track = 1; - start_track( track, path ); - break; - - case SDLK_RIGHT: // next track - if ( track < player->track_count() ) - start_track( ++track, path ); - break; - - case SDLK_MINUS: // reduce tempo - tempo -= 0.1; - if ( tempo < 0.1 ) - tempo = 0.1; - player->set_tempo( tempo ); - break; - - case SDLK_EQUALS: // increase tempo - tempo += 0.1; - if ( tempo > 2.0 ) - tempo = 2.0; - player->set_tempo( tempo ); - break; - - case SDLK_SPACE: // toggle pause - paused = !paused; - player->pause( paused ); - break; - - case SDLK_e: // toggle echo - stereo_depth += 0.2; - if ( stereo_depth > 0.5 ) - stereo_depth = 0; - player->set_stereo_depth( stereo_depth ); - break; - - case SDLK_0: // reset tempo and muting - tempo = 1.0; - muting_mask = 0; - player->set_tempo( tempo ); - player->mute_voices( muting_mask ); - break; - - default: - if ( SDLK_1 <= key && key <= SDLK_9 ) // toggle muting - { - muting_mask ^= 1 << (key - SDLK_1); - player->mute_voices( muting_mask ); - } - } - } - } - } - - // Cleanup - delete player; - delete scope; - - return 0; -} - -void handle_error( const char* error ) -{ - if ( error ) - { - // put error in window title - char str [256]; - sprintf( str, "Error: %s", error ); - fprintf( stderr, str ); - SDL_WM_SetCaption( str, str ); - - // wait for keyboard or mouse activity - SDL_Event e; - do - { - while ( !SDL_PollEvent( &e ) ) { } - } - while ( e.type != SDL_QUIT && e.type != SDL_KEYDOWN && e.type != SDL_MOUSEBUTTONDOWN ); - - exit( EXIT_FAILURE ); - } -} diff --git a/gme/Game_Music_Emu-0.5.2/readme.txt b/gme/Game_Music_Emu-0.5.2/readme.txt deleted file mode 100644 index e3470bfa..00000000 --- a/gme/Game_Music_Emu-0.5.2/readme.txt +++ /dev/null @@ -1,205 +0,0 @@ -Game_Music_Emu 0.5.2: Game Music Emulators ------------------------------------------- -Game_Music_Emu is a collection of video game music file emulators that -support the following formats and systems: - -AY ZX Spectrum/Amstrad CPC -GBS Nintendo Game Boy -GYM Sega Genesis/Mega Drive -HES NEC TurboGrafx-16/PC Engine -KSS MSX Home Computer/other Z80 systems (doesn't support FM sound) -NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound) -SAP Atari systems using POKEY sound chip -SPC Super Nintendo/Super Famicom -VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro - -Features: -* Can be used in C and C++ code -* High emphasis has been placed on making the library very easy to use -* One set of common functions work with all emulators the same way -* Several code examples, including music player using SDL -* Portable code for use on any system with modern or older C++ compilers -* Adjustable output sample rate using quality band-limited resampling -* Uniform access to text information fields and track timing information -* End-of-track fading and automatic look ahead silence detection -* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM -* Tempo can be adjusted and individual voices can be muted while playing -* Can read music data from file, memory, or custom reader function/class -* Can access track information without having to load into full emulator -* M3U track listing support for multi-track formats -* Modular design allows elimination of unneeded emulators/features - -This library has been used in game music players for Windows, Linux on -several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable, -GP2X, and Nintendo DS. - -Author : Shay Green -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs -License: GNU Lesser General Public License (LGPL) - - -Getting Started ---------------- -Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and -all source files in gme/. Be sure "test.nsf" is in the same directory. -Running the program should generate the recording "out.wav". - -Read gme.txt for more information. Post to the discussion forum for -assistance. - - -Files ------ -gme.txt General notes about the library -changes.txt Changes made since previous releases -design.txt Library design notes -license.txt GNU Lesser General Public License - -test.nsf Test file for NSF emulator -test.m3u Test m3u playlist for features.c demo - -demo/ - basics.c Records NSF file to wave sound file - cpp_basics.cpp C++ version of basics.c - features.c Demonstrates many additional features - Wave_Writer.h WAVE sound file writer used for demo output - Wave_Writer.cpp - -player/ Player using the SDL multimedia library - player.cpp Simple music player with waveform display - Music_Player.cpp Stand alone player for background music - Music_Player.h - Audio_Scope.cpp Audio waveform scope - Audio_Scope.h - -gme/ - blargg_config.h Library configuration (modify this file as needed) - - gme.h C interface (also usable in C++, and simpler too) - gme.cpp - - Gme_File.h File loading and track information - Music_Emu.h Track playback and adjustments - Data_Reader.h Custom data readers - - Effects_Buffer.h Sound buffer with stereo echo and panning - Effects_Buffer.cpp - - M3u_Playlist.h M3U playlist support - M3u_Playlist.cpp - - Ay_Emu.h ZX Spectrum AY emulator - Ay_Emu.cpp - Ay_Apu.cpp - Ay_Apu.h - Ay_Cpu.cpp - Ay_Cpu.h - - Gbs_Emu.h Nintendo Game Boy GBS emulator - Gbs_Emu.cpp - Gb_Apu.cpp - Gb_Apu.h - Gb_Cpu.cpp - Gb_Cpu.h - gb_cpu_io.h - Gb_Oscs.cpp - Gb_Oscs.h - - Hes_Emu.h TurboGrafx-16/PC Engine HES emulator - Hes_Apu.cpp - Hes_Apu.h - Hes_Cpu.cpp - Hes_Cpu.h - hes_cpu_io.h - Hes_Emu.cpp - - Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator - Kss_Emu.cpp - Kss_Cpu.cpp - Kss_Cpu.h - Kss_Scc_Apu.cpp - Kss_Scc_Apu.h - Ay_Apu.h - Ay_Apu.cpp - Sms_Apu.h - Sms_Apu.cpp - Sms_Oscs.h - - Nsf_Emu.h Nintendo NES NSF/NSFE emulator - Nsf_Emu.cpp - Nes_Apu.cpp - Nes_Apu.h - Nes_Cpu.cpp - Nes_Cpu.h - nes_cpu_io.h - Nes_Oscs.cpp - Nes_Oscs.h - Nes_Fme7_Apu.cpp - Nes_Fme7_Apu.h - Nes_Namco_Apu.cpp - Nes_Namco_Apu.h - Nes_Vrc6_Apu.cpp - Nes_Vrc6_Apu.h - Nsfe_Emu.h NSFE support - Nsfe_Emu.cpp - - Spc_Emu.h Super Nintendo SPC emulator - Spc_Emu.cpp - Snes_Spc.cpp - Snes_Spc.h - Spc_Cpu.cpp - Spc_Cpu.h - Spc_Dsp.cpp - Spc_Dsp.h - Fir_Resampler.cpp - Fir_Resampler.h - - Sap_Emu.h Atari SAP emulator - Sap_Emu.cpp - Sap_Apu.cpp - Sap_Apu.h - Sap_Cpu.cpp - Sap_Cpu.h - sap_cpu_io.h - - Vgm_Emu.h Sega VGM emulator - Vgm_Emu_Impl.cpp - Vgm_Emu_Impl.h - Vgm_Emu.cpp - Ym2413_Emu.cpp - Ym2413_Emu.h - Gym_Emu.h Sega Genesis GYM emulator - Gym_Emu.cpp - Sms_Apu.cpp Common Sega emulator files - Sms_Apu.h - Sms_Oscs.h - Ym2612_Emu.cpp - Ym2612_Emu.h - Dual_Resampler.cpp - Dual_Resampler.h - Fir_Resampler.cpp - Fir_Resampler.h - - blargg_common.h Common files needed by all emulators - blargg_endian.h - blargg_source.h - Blip_Buffer.cpp - Blip_Buffer.h - Gme_File.cpp - Music_Emu.cpp - Classic_Emu.h - Classic_Emu.cpp - Multi_Buffer.h - Multi_Buffer.cpp - Data_Reader.cpp - - -Legal ------ -Game_Music_Emu library copyright (C) 2003-2006 Shay Green. -SNES SPC DSP emulator based on OpenSPC, copyright (C) 2002 Brad Martin. -Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville. - --- -Shay Green diff --git a/gme/Game_Music_Emu-0.5.2/test.m3u b/gme/Game_Music_Emu-0.5.2/test.m3u deleted file mode 100644 index fd46bfe1..00000000 --- a/gme/Game_Music_Emu-0.5.2/test.m3u +++ /dev/null @@ -1,2 +0,0 @@ -# filename,track number,track name,track time -test.nsf,$00,BGM C,1:16 diff --git a/gme/Game_Music_Emu-0.5.2/test.nsf b/gme/Game_Music_Emu-0.5.2/test.nsf deleted file mode 100644 index da5fcedd..00000000 Binary files a/gme/Game_Music_Emu-0.5.2/test.nsf and /dev/null differ diff --git a/moduleconf.h b/moduleconf.h index 6f9ca732..b422a281 100644 --- a/moduleconf.h +++ b/moduleconf.h @@ -1,3 +1,2 @@ -PLUG(gme) PLUG(dumb) PLUG(stdio) diff --git a/plugins/gme/Game_Music_Emu-0.5.2/Makefile.am b/plugins/gme/Game_Music_Emu-0.5.2/Makefile.am new file mode 100644 index 00000000..49e0b8b8 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/Makefile.am @@ -0,0 +1,3 @@ +EXTRA_DIST = changes.txt design.txt gme.txt license.txt readme.txt + + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/changes.txt b/plugins/gme/Game_Music_Emu-0.5.2/changes.txt new file mode 100644 index 00000000..0405b590 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/changes.txt @@ -0,0 +1,218 @@ +Game_Music_Emu Change Log +------------------------- + +Game_Music_Emu 0.5.2 +-------------------- +- *TONS* of changes and improvements. You should re-read the new header +files and documentation as the changes will allow you to simplify your +code a lot (it might even be simpler to just rewrite it). Existing code +should continue to work without changes in most cases (see Deprecated +features in gme.txt). + +- New file formats: AY, HES, KSS, SAP, NSFE + +- All-new comprehensive C interface (also usable from C++). Simplifies +many things, especially file loading, and brings everything together in +one header file (gme.h). + +- Information tags and track names and times can be accessed for all +game music formats + +- New features supported by all emulators: end of track fading, +automatic silence detection, adjustable song tempo, seek to new time in +track + +- Updated mini player example to support track names and times, echo, +tempo, and channel muting, and added visual waveform display + +- Improved configuration to use blargg_config.h, which you can modify +and keep when you update to a newer libary version. Includes flag for +library to automatically handle gzipped files using zlib (so you don't +need to use Gzip_File_Reader anymore). + +- GBS: Fixed wave channel to not reset waveform when APU is powered off +(affected Garfield). Also improved invalid bank selection (affected Game +& Watch and others). + +- VGM: Added support for alternate noise shifter register +configurations, used by other systems like the BBC Micro. + +- SPC: Removed IPL ROM dump from emulator, as none of the SPC files I +scanned needed it, and an SPC file can include a copy if necessary. Also +re-enabled supposed clamping in gaussian interpolation between the third +and fourth lookups, though I don't know whether it matters + +- Added Music_Emu::load_mem() to use music data already in memory +(without copying it) + +- Added Music_Emu::warning(), which reports minor problems when loading +and playing a music file + +- Added Music_Emu::set_gain() for uniform adjustment of gain. Can only +be set during initialization, so not useful as a general volume control. + +- Added custom operator new to ensure that no exceptions are thrown in +the library (I'd use std::nothrow if it were part of pre-ISO (ARM) C++) + +- Added BLIP_BUFFER_FAST flag to blargg_config.h to use a lower quality +bandlimited synthesis in "classic" emulators, which might help +performance on ancient processors (measure first!). Don't use this +unless absolutely necessary, as quality suffers. + +- Improved performance a bit for x86 platforms + +- Text files now in DOS newline format so they will open in Notepad +properly + +- Removed requirement that file header structures not have any padding +added to the end + +- Fixed common bug in all CPU emulators where negative program counter +could crash emulator (occurred during a negative branch from the +beginning of memory). Also fixed related bug in Z80 emulator for +IX/IY+displacement mode. + +- Eliminated all warnings when compiling on gcc 4.0. The following +generates no diagnostics: + + gcc -S gme/*.cpp -o /dev/null -ansi -fno-gnu-keywords + -fno-nonansi-builtins -pedantic -W -Wabi -Wall -Wcast-align + -Wcast-qual -Wchar-subscripts -Wdisabled-optimization -Werror + -Winline -Wlong-long -Wmultichar -Winvalid-offsetof + -Wnon-virtual-dtor -Woverloaded-virtual -Wparentheses + -Wpointer-arith -Wredundant-decls -Wreorder -Wsign-compare + -Wsign-promo -Wunknown-pragmas -Wwrite-strings + + +Game_Music_Emu 0.3.0 +-------------------- +- Added more demos, including music player using the SDL multimedia +library for sound, and improved documentation + +- All: Improved interface to emulators to allow simpler setup and +loading. Instead of various init() functions, all now support +set_sample_rate( long rate ) and load( const char* file_path ). + +- All: Removed error return from start_track() and play(), and added +error_count() to get the total number of emulation errors since the +track was last started. See demos for examples of new usage. + +- All: Fixed mute_voices() muting to be preserved after loading files +and starting tracks, instead of being cleared as it was whenever a track +was started + +- VGM: Rewrote Vgm_Emu to support Sega Genesis/Mega Drive FM sound at +any sample rate with optional FM oversampling, support for alternate +YM2612 sound cores, and support for optional YM2413 + +- VGM: Added tempo control, useful for slowing 60Hz NTSC Sega Genesis +music to 50Hz PAL + +- VGM: Removed Vgm_Emu::track_data(), since I realized that this +information is already present in the VGM header (oops!) + +- GYM: Changed Gym_Emu::track_length() operation (see Gym_Emu.h) + +- NSF: Added support for Sunsoft FME-7 sound chip used by Gimmick +soundtrack + +- NSF: Fixed Namco 106 problems with Final Lap and others + +- Moved library sources to gme/ directory to reduce clutter, and merged +boost/ functionality into blargg_common.h + +- Added Gzip_File_Reader for transparently using gzipped files + + +Game_Music_Emu 0.2.4 +-------------------- +- Created a discussion forum for problems and feedback: +http://groups-beta.google.com/group/blargg-sound-libs + +- Changed error return value of Blip_Buffer::sample_rate() (also for +Stereo_Buffer, Effects_Buffer, etc.) to blargg_err_t (defined in +blargg_common.h), to make error reporting consistent with other +functions. This means the "no error" return value is the opposite of +what it was before, which will break current code which checks the error +return value: + + // current code (broken) + if ( !buf.sample_rate( samples_per_sec ) ) + out_of_memory(); + + // quick-and-dirty fix (just remove the ! operation) + if ( buf.sample_rate( samples_per_sec ) ) + out_of_memory(); + + // proper fix + blargg_err_t error = buf.sample_rate( samples_per_sec ); + if ( error ) + report_error( error ); + +- Implemented workaround for MSVC++ 6 compiler limitations, allowing it +to work on that compiler again + +- Added sample clamping to avoid wrap-around at high volumes, allowing +higher volume with little distortion + +- Added to-do list and design notes + +- Added Music_Emu::skip( long sample_count ) to skip ahead in current +track + +- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for +determining the length of non-looped GYM and VGM files + +- Partially implemented DMC non-linearity when its value is directly set +using $4011, which reduces previously over-emphasized "popping" of +percussion on some games (TMNT II in particular) + +- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly +using abs() instead of fabs()...argh) + +- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and +now stops sample slightly earlier than the end, as the SNES does. Fixed +a totally broken CPU addressing mode. + +- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music +sounds decent + +- Fixed a minor GBS emulation bug + +- Fixed GYM loop point bug when track was restarted before loop point +had been reached + +- Made default GBS frequency equalization less muffled + +- Added pseudo-surround effect removal for SPC files + +- Added Music_Emu::voice_names() which returns names for each voice. + +- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be +easily set for library sources + +- Changed assignment of expansion sound chips in Nsf_Emu to be spread +more evenly when using Effects_Buffer + +- Changed 'size_t' values in Blip_Buffer interface to 'long' + +- Changed demo to generate a WAVE sound file rather than an AIFF file + + +Game_Music_Emu 0.2.0 +-------------------- +- Redid framework and rewrote/cleaned up emulators + +- Changed licensing to GNU Lesser General Public License (LGPL) + +- Added Sega Genesis GYM and Super Nintendo SPC emulators + +- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator + +- Eliminated use of static mutable data in emulators, allowing +multi-instance safety + + +Game_Music_Emu 0.1.0 +-------------------- +- First release diff --git a/plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp b/plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp new file mode 100644 index 00000000..ec40959d --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.cpp @@ -0,0 +1,182 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Wave_Writer.h" + +#include +#include + +/* Copyright (C) 2003-2006 by Shay Green. Permission is hereby granted, free +of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. THE +SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +const int header_size = 0x2C; + +static void exit_with_error( const char* str ) +{ + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); +} + +Wave_Writer::Wave_Writer( long sample_rate, const char* filename ) +{ + sample_count_ = 0; + rate = sample_rate; + buf_pos = header_size; + chan_count = 1; + + buf = (unsigned char*) malloc( buf_size * sizeof *buf ); + if ( !buf ) + exit_with_error( "Out of memory" ); + + file = fopen( filename, "wb" ); + if ( !file ) + exit_with_error( "Couldn't open WAVE file for writing" ); + + setvbuf( file, 0, _IOFBF, 32 * 1024L ); +} + +void Wave_Writer::flush() +{ + if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) ) + exit_with_error( "Couldn't write WAVE data" ); + buf_pos = 0; +} + +void Wave_Writer::write( const sample_t* in, long remain, int skip ) +{ + sample_count_ += remain; + while ( remain ) + { + if ( buf_pos >= buf_size ) + flush(); + + long n = (buf_size - buf_pos) / sizeof (sample_t); + if ( n > remain ) + n = remain; + remain -= n; + + // convert to lsb first format + unsigned char* p = &buf [buf_pos]; + while ( n-- ) + { + int s = *in; + in += skip; + *p++ = (unsigned char) s; + *p++ = (unsigned char) (s >> 8); + } + + buf_pos = p - buf; + assert( buf_pos <= buf_size ); + } +} + + +void Wave_Writer::write( const float* in, long remain, int skip ) +{ + sample_count_ += remain; + while ( remain ) + { + if ( buf_pos >= buf_size ) + flush(); + + long n = (buf_size - buf_pos) / sizeof (sample_t); + if ( n > remain ) + n = remain; + remain -= n; + + // convert to lsb first format + unsigned char* p = &buf [buf_pos]; + while ( n-- ) + { + long s = (long) (*in * 0x7FFF); + in += skip; + if ( (short) s != s ) + s = 0x7FFF - (s >> 24); // clamp to 16 bits + *p++ = (unsigned char) s; + *p++ = (unsigned char) (s >> 8); + } + + buf_pos = p - buf; + assert( buf_pos <= buf_size ); + } +} + +void Wave_Writer::close() +{ + if ( file ) + { + flush(); + + // generate header + long ds = sample_count_ * sizeof (sample_t); + long rs = header_size - 8 + ds; + int frame_size = chan_count * sizeof (sample_t); + long bps = rate * frame_size; + unsigned char header [header_size] = + { + 'R','I','F','F', + rs,rs>>8, // length of rest of file + rs>>16,rs>>24, + 'W','A','V','E', + 'f','m','t',' ', + 0x10,0,0,0, // size of fmt chunk + 1,0, // uncompressed format + chan_count,0, // channel count + rate,rate >> 8, // sample rate + rate>>16,rate>>24, + bps,bps>>8, // bytes per second + bps>>16,bps>>24, + frame_size,0, // bytes per sample frame + 16,0, // bits per sample + 'd','a','t','a', + ds,ds>>8,ds>>16,ds>>24// size of sample data + // ... // sample data + }; + + // write header + fseek( file, 0, SEEK_SET ); + fwrite( header, sizeof header, 1, file ); + + fclose( file ); + file = 0; + free( buf ); + } +} + +Wave_Writer::~Wave_Writer() +{ + close(); +} + +// C interface + +static Wave_Writer* ww; + +void wave_open( long sample_rate, const char* filename ) +{ + ww = new Wave_Writer( sample_rate, filename ); + assert( ww ); +} + +void wave_enable_stereo() { ww->enable_stereo(); } + +long wave_sample_count() { return ww->sample_count(); } + +void wave_write( const short* buf, long count ) { ww->write( buf, count ); } + +void wave_close() +{ + delete ww; + ww = 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h b/plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h new file mode 100644 index 00000000..da08cc2a --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/demo/Wave_Writer.h @@ -0,0 +1,73 @@ +/* WAVE sound file writer for recording 16-bit output during program development */ + +#ifndef WAVE_WRITER_H +#define WAVE_WRITER_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* C interface */ +void wave_open( long sample_rate, const char* filename ); +void wave_enable_stereo( void ); +void wave_write( const short* buf, long count ); +long wave_sample_count( void ); +void wave_close( void ); + +#ifdef __cplusplus + } +#endif + +#ifdef __cplusplus +#include +#include + +/* C++ interface */ +class Wave_Writer { +public: + typedef short sample_t; + + // Create sound file with given sample rate (in Hz) and filename. + // Exits program if there's an error. + Wave_Writer( long sample_rate, char const* filename = "out.wav" ); + + // Enable stereo output + void enable_stereo(); + + // Append 'count' samples to file. Use every 'skip'th source sample; allows + // one channel of stereo sample pairs to be written by specifying a skip of 2. + void write( const sample_t*, long count, int skip = 1 ); + + // Append 'count' floating-point samples to file. Use every 'skip'th source sample; + // allows one channel of stereo sample pairs to be written by specifying a skip of 2. + void write( const float*, long count, int skip = 1 ); + + // Number of samples written so far + long sample_count() const; + + // Finish writing sound file and close it + void close(); + + ~Wave_Writer(); +public: + // Deprecated + void stereo( bool b ) { chan_count = b ? 2 : 1; } +private: + enum { buf_size = 32768 * 2 }; + unsigned char* buf; + FILE* file; + long sample_count_; + long rate; + long buf_pos; + int chan_count; + + void flush(); +}; + +inline void Wave_Writer::enable_stereo() { chan_count = 2; } + +inline long Wave_Writer::sample_count() const { return sample_count_; } + +#endif + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/demo/basics.c b/plugins/gme/Game_Music_Emu-0.5.2/demo/basics.c new file mode 100644 index 00000000..55178251 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/demo/basics.c @@ -0,0 +1,57 @@ +/* C example that opens a game music file and records 10 seconds to "out.wav" */ + +static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ + +#include "gme/gme.h" + +#include "Wave_Writer.h" /* wave_ functions for writing sound file */ +#include +#include + +void handle_error( const char* str ); + +int main() +{ + long sample_rate = 44100; /* number of samples per second */ + int track = 0; /* index of track to play (0 = first) */ + + /* Open music file in new emulator */ + Music_Emu* emu; + handle_error( gme_open_file( filename, &emu, sample_rate ) ); + + /* Start track */ + handle_error( gme_start_track( emu, track ) ); + + /* Begin writing to wave file */ + wave_open( sample_rate, "out.wav" ); + wave_enable_stereo(); + + /* Record 10 seconds of track */ + while ( gme_tell( emu ) < 10 * 1000L ) + { + /* Sample buffer */ + #define buf_size 1024 /* can be any multiple of 2 */ + short buf [buf_size]; + + /* Fill sample buffer */ + handle_error( gme_play( emu, buf_size, buf ) ); + + /* Write samples to wave file */ + wave_write( buf, buf_size ); + } + + /* Cleanup */ + gme_delete( emu ); + wave_close(); + + return 0; +} + +void handle_error( const char* str ) +{ + if ( str ) + { + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp b/plugins/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp new file mode 100644 index 00000000..53fab418 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/demo/cpp_basics.cpp @@ -0,0 +1,67 @@ +// C++ example that opens a game music file and records 10 seconds to "out.wav" + +static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ + +#include "gme/Music_Emu.h" + +#include "Wave_Writer.h" +#include +#include + +void handle_error( const char* str ); + +int main() +{ + long sample_rate = 44100; // number of samples per second + int track = 0; // index of track to play (0 = first) + + // Determine file type + gme_type_t file_type; + handle_error( gme_identify_file( filename, &file_type ) ); + if ( !file_type ) + handle_error( "Unsupported music type" ); + + // Create emulator and set sample rate + Music_Emu* emu = file_type->new_emu(); + if ( !emu ) + handle_error( "Out of memory" ); + handle_error( emu->set_sample_rate( sample_rate ) ); + + // Load music file into emulator + handle_error( emu->load_file( filename ) ); + + // Start track + handle_error( emu->start_track( track ) ); + + // Begin writing to wave file + Wave_Writer wave( sample_rate, "out.wav" ); + wave.enable_stereo(); + + // Record 10 seconds of track + while ( emu->tell() < 10 * 1000L ) + { + // Sample buffer + const long size = 1024; // can be any multiple of 2 + short buf [size]; + + // Fill buffer + handle_error( emu->play( size, buf ) ); + + // Write samples to wave file + wave.write( buf, size ); + } + + // Cleanup + delete emu; + + return 0; +} + +void handle_error( const char* str ) +{ + if ( str ) + { + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/demo/features.c b/plugins/gme/Game_Music_Emu-0.5.2/demo/features.c new file mode 100644 index 00000000..96a9a8a9 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/demo/features.c @@ -0,0 +1,149 @@ +/* C example that opens any music file type, opens an m3u playlist if present, +prints its info and voice names, customizes the sound, and fades a track out. +Records to "out.wav". */ + +static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ +static char playlist [] = "test.m3u"; /* uses this playlist, if present*/ + +#include "gme/gme.h" + +#include "Wave_Writer.h" /* wave_ functions for writing sound file */ +#include +#include + +void handle_error( const char* ); + +/* Example of loading from memory, which would be useful if using a zip file or +other custom format. In this example it's silly because we could just use +gme_load( &emu, sample_rate, path, 0 ). */ +Music_Emu* load_file( const char* path, long sample_rate ) +{ + Music_Emu* emu; + char* data; + long size; + + /* Read file data into memory. You might read the data from a zip file or + other compressed format. */ + FILE* in = fopen( path, "rb" ); + if ( !in ) + handle_error( "Couldn't open file" ); + fseek( in, 0, SEEK_END ); + size = ftell( in ); + rewind( in ); + + data = malloc( size ); + if ( !data ) + handle_error( "Out of memory" ); + if ( fread( data, size, 1, in ) <= 0 ) + handle_error( "Read error" ); + fclose( in ); + + handle_error( gme_open_data( data, size, &emu, sample_rate ) ); + free( data ); /* a copy is made of the data */ + return emu; +} + +/* Print any warning for most recent emulator action (load, start_track, play) */ +void print_warning( Music_Emu* emu ) +{ + const char* warning = gme_warning( emu ); + if ( warning ) + printf( "**** Warning: %s\n\n", warning ); +} + +static char my_data [] = "Our cleanup function was called"; + +/* Example cleanup function automatically called when emulator is deleted. */ +static void my_cleanup( void* my_data ) +{ + printf( "\n%s\n", (char*) my_data ); +} + +int main() +{ + long sample_rate = 44100; + int track = 0; /* index of track to play (0 = first) */ + int i; + + /* Load file into emulator */ + Music_Emu* emu = load_file( filename, sample_rate ); + print_warning( emu ); + + /* Register cleanup function and confirmation string as data */ + gme_set_user_data( emu, my_data ); + gme_set_user_cleanup( emu, my_cleanup ); + + /* Load .m3u playlist file. All tracks are assumed to use current file. + We ignore error here in case there is no m3u file present. */ + gme_load_m3u( emu, playlist ); + print_warning( emu ); + + /* Get and print main info for track */ + { + track_info_t info; + handle_error( gme_track_info( emu, &info, track ) ); + printf( "System : %s\n", info.system ); + printf( "Game : %s\n", info.game ); + printf( "Author : %s\n", info.author ); + printf( "Copyright: %s\n", info.copyright ); + printf( "Comment : %s\n", info.comment ); + printf( "Dumper : %s\n", info.dumper ); + printf( "Tracks : %d\n", (int) info.track_count ); + printf( "\n" ); + printf( "Track : %d\n", (int) track + 1 ); + printf( "Name : %s\n", info.song ); + printf( "Length : %ld:%02ld", + (long) info.length / 1000 / 60, (long) info.length / 1000 % 60 ); + if ( info.loop_length != 0 ) + printf( " (endless)" ); + printf( "\n\n" ); + } + + /* Print voice names */ + for ( i = 0; i < gme_voice_count( emu ); i++ ) + printf( "Voice %d: %s\n", i, gme_voice_names( emu ) [i] ); + + /* Add some stereo enhancement */ + gme_set_stereo_depth( emu, 0.20 ); + + /* Adjust equalizer for crisp, bassy sound */ + { + gme_equalizer_t eq; + eq.treble = 0.0; + eq.bass = 20; + gme_set_equalizer( emu, &eq ); + } + + /* Start track and begin fade at 10 seconds */ + handle_error( gme_start_track( emu, track ) ); + print_warning( emu ); + gme_set_fade( emu, 10 * 1000L ); + + /* Record track until it ends */ + wave_open( sample_rate, "out.wav" ); + wave_enable_stereo(); + while ( !gme_track_ended( emu ) ) + { + #define buf_size 1024 + short buf [buf_size]; + handle_error( gme_play( emu, buf_size, buf ) ); + print_warning( emu ); + wave_write( buf, buf_size ); + } + + /* Cleanup */ + gme_delete( emu ); + wave_close(); + + getchar(); + return 0; +} + +void handle_error( const char* str ) +{ + if ( str ) + { + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/design.txt b/plugins/gme/Game_Music_Emu-0.5.2/design.txt new file mode 100644 index 00000000..8c8c65b1 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/design.txt @@ -0,0 +1,194 @@ +Game_Music_Emu 0.5.2 Design +--------------------------- +This might be slightly out-of-date at times, but will be a big help in +understanding the library implementation. + + +Architecture +------------ +The library is essentially a bunch of independent game music file +emulators unified with a common interface. + +Gme_File and Music_Emu provide a common interface to the emulators. The +virtual functions are protected rather than public to allow pre- and +post-processing of arguments and data in one place. This allows the +emulator classes to assume that everything is set up properly when +starting a track and playing samples. + +All file input is done with the Data_Reader interface. Many derived +classes are present, for the usual disk-based file and block of memory, +to specialized adaptors for things like reading a subset of data or +combining a block of memory with a Data_Reader to the remaining data. +This makes the library much more flexible with regard to the source of +game music file data. I still added a specialized load_mem() function to +have the emulator keep a pointer to data already read in memory, for +those formats whose files can be absolutely huge (GYM, some VGMs). This +is important if for some reason the caller must load the data ahead of +time, but doesn't want the emulator needlessly making a copy. + +Since silence checking and fading are relatively complex, they are kept +separate from basic file loading and track information, which are +handled in the base class Gme_File. My original intent was to use +Gme_File as the common base class for full emulators and track +information-only readers, but implementing the C interface was much +simpler if both derived from Music_Emu. User C++ code can still benefit +from static checking by using Gme_File where only track information will +be accessed. + +Each emulator generally has three components: main emulator, CPU +emulator, and sound chip emulator(s). Each component has minimal +coupling, so use in a full emulator or stand alone is fairly easy. This +modularity really helps reduce complexity. Blip_Buffer helps a lot with +simplifying the APU interfaces and implementation. + +The "classic" emulators derive from Classic_Emu, which handles +Blip_Buffer filling and multiple channels. It uses Multi_Buffer for +output, allowing you to derive a custom buffer that could output each +voice to a separate sound channel and do different processing on each. +At some point I'm going to implement a better Effects_Buffer that allows +individual control of every channel. + +In implementing the C interface, I wanted a way to specify an emulator +type that didn't require linking in all the emulators. For each emulator +type there is a global object with pointers to functions to create the +emulator or a track information reader. The emulator type is thus a +pointer to this, which conveniently allows for a NULL value. The user +referencing this emulator type object is what ultimately links the +emulator in (unless new Foo_Emu is used in C++, of course). This type +also serves as a useful substitute for RTTI on older C++ compilers. + +Addendum: I have since added gme_type_list(), which causes all listed +emulators to be linked in. To avoid this, I make the list itself +editable in blargg_config.h. Having a built-in list allows +gme_load_file() to take a path and give back an emulator with the file +loaded, which is extremely useful for new users. + + +Interface conventions +---------------------- +If a function retains a pointer to or replaces the value of an object +passed, it takes a pointer so that it will be clear in the caller's +source code that care is required. + +Multi-word names have an underscore '_' separator between individual +words. + +Functions are named with lowercase words. Functions which perform an +action with side-effects are named with a verb phrase (i.e. load, move, +run). Functions which return the value of a piece of state are named +using a noun phrase (i.e. loaded, moved, running). + +Classes are named with capitalized words. Only the first letter of an +acronym is capitalized. Class names are nouns, sometimes suggestive of +what they do (i.e. File_Scanner). + +Structure, enumeration, and typedefs to these and built-in types are +named using lowercase words with a _t suffix. + +Macros are named with all-uppercase words. + +Internal names which can't be hidden due to technical reasons have an +underscore '_' suffix. + + +Managing Complexity +------------------- +Complexity has been a factor in most library decisions. Many features +have been passed by due to the complexity they would add. Once +complexity goes past a certain level, it mentally grasping the library +in its entirety, at which point more defects will occur and be hard to +find. + +I chose 16-bit signed samples because it seems to be the most common +format. Supporting multiple formats would add too much complexity to be +worth it. Other formats can be obtained via conversion. + +I've kept interfaces fairly lean, leaving many possible features +untapped but easy to add if necessary. For example the classic emulators +could have volume and frequency equalization adjusted separately for +each channel, since they each have an associated Blip_Synth. + +Source files of 400 lines or less seem to be the best size to limit +complexity. In a few cases there is no reasonable way to split longer +files, or there is benefit from having the source together in one file. + + +Preventing Bugs +--------------- +I've done many things to reduce the opportunity for defects. A general +principle is to write code so that defects will be as visible as +possible. I've used several techniques to achieve this. + +I put assertions at key points where defects seem likely or where +corruption due to a defect is likely to be visible. I've also put +assertions where violations of the interface are likely. In emulators +where I am unsure of exact hardware operation in a particular case, I +output a debug-only message noting that this has occurred; many times I +haven't implemented a hardware feature because nothing uses it. I've +made code brittle where there is no clear reason flexibility; code +written to handle every possibility sacrifices quality and reliability +to handle vaguely defined situations. + + +Flexibility through indirection +------------------------------- +I've tried to allow the most flexibility of modules by using indirection +to allow extension by the user. This keeps each module simpler and more +focused on its unique task. + +The classic emulators use Multi_Buffer, which potentially allows a +separate Blip_Buffer for each channel. This keeps emulators free of +typical code to allow output in mono, stereo, panning, etc. + +All emulators use a reader object to access file data, allowing it to be +stored in a regular file, compressed archive, memory, or generated +on-the-fly. Again, the library can be kept free of the particulars of +file access and changes required to support new formats. + + +Emulators in general +-------------------- +When I wrote the first NES sound emulator, I stored most of the state in +an emulator-specific format, with significant redundancy. In the +register write function I decoded everything into named variables. I +became tired of the verbosity and wanted to more closely model the +hardware, so I moved to a style of storing the last written value to +each register, along with as little other state as possible, mostly the +internal hardware registers. While this involves slightly more +recalculation, in most cases the emulation code is of comparable size. +It also makes state save/restore (for use in a full emulator) much +simpler. Finally, it makes debugging easier since the hardware registers +used in emulation are obvious. + + +CPU Cores +--------- +I've spent lots of time coming up with techniques to optimize the CPU +cores. Some of the most important: execute multiple instructions during +an emulation call, keep state in local variables to allow register +assignment, optimize state representation for most common instructions, +defer status flag calculation until actually needed, read program code +directly without a call to the memory read function, always pre-fetch +the operand byte before decoding instruction, and emulate instructions +using common blocks of code. + +I've successfully used Nes_Cpu in a fairly complete NES emulator, and +I'd like to make all the CPU emulators suitable for use in emulators. It +seems a waste for them to be used only for the small amount of emulation +necessary for game music files. + +I debugged the CPU cores by writing a test shell that ran them in +parallel with other CPU cores and compared all memory accesses and +processor states at each step. This provided good value at little cost. + +The CPU mapping page size is adjustable to allow the best tradeoff +between memory/cache usage and handler granularity. The interface allows +code to be somewhat independent of the page size. + +I optimize program memory accesses to direct reads rather than calls to +the memory read function. My assumption is that it would be difficult to +get useful code out of hardware I/O addresses, so no software will +intentionally execute out of I/O space. Since the page size can be +changed easily, most program memory mapping schemes can be accommodated. +This greatly reduces memory access function calls. + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme.txt b/plugins/gme/Game_Music_Emu-0.5.2/gme.txt new file mode 100644 index 00000000..2c963d8e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme.txt @@ -0,0 +1,464 @@ +Game_Music_Emu 0.5.2 +-------------------- +Author : Shay Green +Website: http://www.slack.net/~ant/libs/ +Forum : http://groups.google.com/group/blargg-sound-libs +License: GNU Lesser General Public License (LGPL) + +Contents +-------- +* Overview +* C and C++ interfaces +* Function reference +* Error handling +* Emulator types +* M3U playlist support +* Information fields +* Track length +* Loading file data +* Sound parameters +* VGM/GYM YM2413 & YM2612 FM sound +* Modular construction +* Obscure features +* Solving problems +* Deprecated features +* Thanks + + +Overview +-------- +This library can open game music files, play tracks, and read game and +track information tags. To play a game music file, do the following: + +* Open the file with gme_open_file() +* Start a track with gme_start_track(); +* Generate samples as needed with gme_play() +* Play samples through speaker using your operating system +* Delete emulator when done with gme_delete() + +Your code must arrange for the generated samples to be played through +the computer's speaker using whatever method your operating system +requires. + +There are many additional features available; you can: + +* Determine of the type of a music file without opening it with +gme_identify_*() +* Load just the file's information tags with gme_info_only +* Load from a block of memory rather than a file with gme_load_data() +* Arrange for a fade-out at a particular time with gme_set_fade +* Find when a track has ended with gme_track_ended() +* Seek to a new time in the track with gme_seek() +* Load an extended m3u playlist with gme_load_m3u() +* Get a list of the voices (channels) and mute them individually with +gme_voice_names() and gme_mute_voice() +* Change the playback tempo without affecting pitch with gme_set_tempo() +* Adjust treble/bass equalization with gme_set_equalizer() +* Associate your own data with an emulator and later get it back with +gme_set_user_data() +* Register a function of yours to be called back when the emulator is +deleted with gme_set_user_cleanup() + +Refer to gme.h for a comprehensive summary of features. + + +C and C++ interfaces +-------------------- +While the library is written in C++, an extensive C interface is +provided in gme.h. This C interface will be referred to throughout this +documentation unless a feature is only available in the full C++ +interface. All C interface functions and other names have the gme_ +prefix, so you can recognize a C++-only feature by the lack of gme_ in +the names used (contact me if you'd like a feature added to the C +interface). If you're building a shared library, I highly recommend +sticking to the C interface only, because it will be more stable between +releases of the library than the C++ interface. Finally, the C and C++ +interfaces can be freely mixed without problems. Compare demo/basics.c +with demo/cpp_basics.cpp to see how the C and C++ interfaces translate +between each other. + + +Function reference +------------------ +Read the following header files for a complete reference to functions +and features. The second group of header files can only be used in C++. + +blargg_config.h Library configuration +gme.h C interface (also usable from C++) + +Gme_File.h File loading and track information +Music_Emu.h Track playback and adjustments +Data_Reader.h Custom data readers +Effects_Buffer.h Sound buffer with adjustable stereo echo and panning +M3u_Playlist.h M3U playlist support +Gbs_Emu.h GBS equalizer settings +Nsf_Emu.h NSF equalizer settings +Spc_Emu.h SPC surround disable +Vgm_Emu.h VGM oversampling disable and custom buffer query + + +Error handling +-------------- +Functions which can fail have a return type of gme_err_t (blargg_err_t +in the C++ interfaces), which is a pointer to an error string (const +char*). If a function is successful it returns NULL. Errors that you can +easily avoid are checked with debug assertions; gme_err_t return values +are only used for genuine run-time errors that can't be easily predicted +in advance (out of memory, I/O errors, incompatible file data). Your +code should check all error values. + +To improve usability for C programmers, C++ programmers unfamiliar with +exceptions, and compatibility with older C++ compilers, the library does +*not* throw any C++ exceptions and uses malloc() instead of the standard +operator new. This means that you *must* check for NULL when creating a +library object with the new operator. + +When loading a music file in the wrong emulator or trying to load a +non-music file, gme_wrong_file_type is returned. You can check for this +error in C++ like this: + + gme_err_t err = gme_open_file( path, &emu ); + if ( err == gme_wrong_file_type ) + ... + +To check for minor problems, call gme_warning() to get a string +describing the last warning. Your player should allow the user some way +of knowing when this is the case, since these minor errors could affect +playback. Without this information the user can't solve problems as +well. When playing a track, gme_warning() returns minor playback-related +problems (major playback problems end the track immediately and set the +warning string). + + +Emulator types +-------------- +The library includes several game music emulators that each support a +different file type. Each is identified by a gme_type_t constant defined +in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use +gme_open_file() or gme_open_data(), the library does the work of +determining the file type and creating an appropriate emulator. If you +want more control over this process, read on. + +There are two basic ways to identify a game music file's type: look at +its file extension, or read the header data. The library includes +functions to help with both methods. The first is preferable because it +is fast and the most common way to identify files. Sometimes the +extension is lost or wrong, so the header must be read. + +Use gme_identify_extension() to find the correct game music type based +on a filename. To identify a file based on its extension and header +contents, use gme_identify_file(). If you read the header data yourself, +use gme_identify_header(). + +If you want to remove support for some music types to reduce your +executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to +support just NSF and GBS, use this: + + #define GME_TYPE_LIST gme_nsf_type, gme_gbs_type + + +M3U playlist support +-------------------- +The library supports playlists in an extended m3u format with +gme_load_m3u() to give track names and times to multi-song formats: AY, +GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format +itself is not well-defined so some m3u files won't work properly +(particularly those provided with KSS files). Only m3u files referencing +a single file are supported; your code must handle m3u files covering +more than one game music file, though it can use the built-in m3u +parsing provided by the library. + + +Information fields +------------------ +Support is provided for the various text fields and length information +in a file with gme_track_info(). If you just need track information for +a file (for example, building a playlist), use gme_new_info() in place +of gme_new_emu(), load the file normally, then you can access the track +count and info, but nothing else. + + M3U VGM GYM SPC SAP NSFE NSF AY GBS HES KSS + ------------------------------------------------------- +Track Count | * * * * * * * * * + | +System | * * * * * * * * * * + | +Game | * * * * * * * + | +Song | * * * * * * * + | +Author | * * * * * * * * + | +Copyright | * * * * * * * * + | +Comment | * * * * + | +Dumper | * * * * + | +Length | * * * * * * + | +Intro Length| * * * + | +Loop Length | * * * + +As listed above, the HES and KSS file formats don't include a track +count, and tracks are often scattered over the 0-255 range, so an m3u +playlist for these is a must. + +Unavailable text fields are set to an empty string and times to -1. Your +code should be prepared for any combination of available and unavailable +fields, as a particular music file might not use all of the supported +fields listed above. + +Currently text fields are truncated to 255 characters. Obscure fields of +some formats are not currently decoded; contact me if you want one +added. + + +Track length +------------ +The library leaves it up to you as to when to stop playing a track. You +can ask for available length information and then tell the library what +time it should start fading the track with gme_set_fade(). By default it +also continually checks for 6 or more seconds of silence to mark the end +of a track. Here is a reasonable algorithm you can use to decide how +long to play a track: + +* If the track length is > 0, use it +* If the loop length > 0, play for intro + loop * 2 +* Otherwise, default to 2.5 minutes (150000 msec) + +If you want to play a track longer than normal, be sure the loop length +isn't zero. See Music_Player.cpp around line 145 for example code. + +By default, the library skips silence at the beginning of a track. It +also continually checks for the end of a non-looping track by watching +for 6 seconds of unbroken silence. When doing this is scans *ahead* by +several seconds so it can report the end of the track after only one +second of silence has actually played. This feature can be disabled with +gme_ignore_silence(). + + +Loading file data +----------------- +The library allows file data to be loaded in many different ways. All +load functions return an error which you should check. The following +examples assume these variables: + + Music_Emu* emu; + gme_err_t error; + +If you're letting the library determine a file's type, you can use +either gme_open_file() or gme_open_data(): + + error = gme_open_file( pathname, &emu ); + error = gme_open_data( pointer, size, &emu ); + +If you're manually determining file type and using used gme_new_emu() to +create an emulator, you can use the following methods of loading: + +* From a block of memory: + + error = gme_load_data( emu, pointer, size ); + +* Have library call your function to read data: + + gme_err_t my_read( void* my_data, void* out, long count ) + { + // code that reads 'count' bytes into 'out' buffer + // and return 0 if no error + } + + error = gme_load_custom( emu, my_read, file_size, my_data ); + +* If you must load the file data into memory yourself, you can have the +library use your data directly *without* making a copy. If you do this, +you must not free the data until you're done playing the file. + + error = emu->load_mem( pointer, size ); + +* If you've already read the first bytes of a file (perhaps to determine +the file type) and want to avoid seeking back to the beginning for +performance reasons, use Remaining_Reader: + + Std_File_Reader in; + error = in.open( file_path ); + + char header [4]; + error = in.read( &header, sizeof header ); + ... + + Remaining_Reader rem( &header, sizeof header, &in ); + error = emu->load( rem ); + +If you merely need access to a file's header after loading, use the +emulator-specific header() functions, after casting the Music_Emu +pointer to the specific emulator's type. This example examines the +chip_flags field of the header if it's an NSF file: + + if ( music_emu->type() == gme_nsf_type ) + { + Nsf_Emu* nsf_emu = (Nsf_Emu*) music_emu; + if ( nsf_emu->header().chip_flags & 0x01 ) + ... + } + +Contact me if you want more information about loading files. + + +Sound parameters +---------------- +All emulators support an arbitrary output sampling rate. A rate of 44100 +Hz should work well on most systems. Since band-limited synthesis is +used, a sampling rate above 48000 Hz is not necessary and will actually +reduce sound quality and performance. + +All emulators also support adjustable gain, mainly for the purpose of +getting consistent volume between different music formats and avoiding +excessive modulation. The gain can only be set *before* setting the +emulator's sampling rate, so it's not useful as a general volume +control. The default gains of emulators are set so that they give +generally similar volumes, though some soundtracks are significantly +louder or quieter than normal. + +Some emulators support adjustable treble and bass frequency equalization +(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer(). +Parameters are specified using gme_equalizer_t eq = { treble_dB, +bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives +normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble +for an extra crisp sound. Bass_freq sets the frequency where bass +response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, +and 15000 Hz removes all bass. For example, the following makes the +sound extra-crisp but lacking bass: + + gme_equalizer_t eq = { 5.0, 1000 }; + gme_set_equalizer( music_emu, &eq ); + +Each emulator's equalization defaults to approximate the particular +console's sound quality; this default can be determined by calling +equalizer() just after creating the emulator. The Music_Emu::tv_eq +profile gives sound as if coming from a TV speaker, and some emulators +include other profiles for different versions of the system. For +example, to use Famicom sound equalization with the NSF emulator, do the +following: + + music_emu->set_equalizer( Nsf_Emu::famicom_eq ); + + +VGM/GYM YM2413 & YM2612 FM sound +-------------------------------- +The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound +chip emulator based on the Gens project. Because this has some +inaccuracies, other YM2612 emulators can be used in its place by +re-implementing the interface in YM2612_Emu.h. Available on my website +is a modified version of MAME's YM2612 emulator, which sounds better in +some ways and whose author is still making improvements. + +VGM music files using the YM2413 FM sound chip are also supported, but a +YM2413 emulator isn't included with the library due to technical +reasons. I have put one of the available YM2413 emulators on my website +that can be used directly. + + +Modular construction +-------------------- +The library is made of many fairly independent modules. If you're using +only one music file emulator, you can eliminate many of the library +sources from your program. Refer to the files list in readme.txt to get +a general idea of what can be removed, and be sure to edit GME_TYPE_LIST +(see "Emulator types" above). Post to the forum if you'd like me to put +together a smaller version for a particular use, as this only takes me a +few minutes to do. + +If you want to use one of the individual sound chip emulators (or CPU +cores) in your own console emulator, first check the libraries page on +my website since I have released several of them as stand alone +libraries with included documentation and examples on their use. If you +don't find it as a standalone library, contact me and I'll consider +separating it. + +The "classic" sound chips use my Blip_Buffer library, which greatly +simplifies their implementation and efficiently handles band-limited +synthesis. It is also available as a stand alone library with +documentation and many examples. + + +Obscure features +---------------- +The library's flexibility allows many possibilities. Contact me if you +want help implementing ideas or removing limitations. + +* Uses no global/static variables, allowing multiple instances of any +emulator. This is useful in a music player if you want to allow +simultaneous recording or scanning of other tracks while one is already +playing. This will also be useful if your platform disallows global +data. + +* Emulators that support a custom sound buffer can have *every* voice +routed to a different Blip_Buffer, allowing custom processing on each +voice. For example you could record a Game Boy track as a 4-channel +sound file. + +* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive +synthesis on "classic" emulators, which might help on some really old +processors. This significantly lowers sound quality and prevents treble +equalization. Try this if your platform's processor isn't fast enough +for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces +processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth +the quality loss. + + +Solving problems +---------------- +If you're having problems, try the following: + +* If you're getting garbled sound, try this simple siren generator in +place of your call to play(). This will quickly tell whether the problem +is in the library or in your code. + + static void play_siren( long count, short* out ) + { + static double a, a2; + while ( count-- ) + *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); + } + +* Enable debugging support in your environment. This enables assertions +and other run-time checks. + +* Turn the compiler's optimizer is off. Sometimes an optimizer generates +bad code. + +* If multiple threads are being used, ensure that only one at a time is +accessing a given set of objects from the library. This library is not +in general thread-safe, though independent objects can be used in +separate threads. + +* If all else fails, see if the demos work. + + +Deprecated features +------------------- +The following functions and other features have been deprecated and will +be removed in a future release of the library. Alternatives to the +deprecated features are listed to the right. + +Music_Emu::error_count() warning() +load( header, reader ) see "Loading file data" above +Spc_Emu::trailer() track_info() +Spc_Emu::trailer_size() +Gym_Emu::track_length() track_info() +Vgm_Emu::gd3_data() track_info() +Nsfe_Emu::disable_playlist() clear_playlist() + + +Thanks +------ +Big thanks to Chris Moeller (kode54) for help with library testing and +feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and +for original work on openspc++ that was used when developing Spc_Emu. +Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the +start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz, +nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using, +and giving feedback for the library in their respective game music +players. diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp new file mode 100644 index 00000000..9dc5bb28 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.cpp @@ -0,0 +1,395 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Ay_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" + +// 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). +unsigned 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 ), +}; + +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; + } + } + + output( 0 ); + volume( 1.0 ); + reset(); +} + +void Ay_Apu::reset() +{ + last_time = 0; + noise.delay = 0; + noise.lfsr = 1; + + osc_t* osc = &oscs [osc_count]; + do + { + osc--; + osc->period = period_factor; + osc->delay = 0; + osc->last_amp = 0; + osc->phase = 0; + } + while ( osc != oscs ); + + for ( int i = sizeof regs; --i >= 0; ) + regs [i] = 0; + regs [7] = 0xFF; + write_data_( 13, 0 ); +} + +void Ay_Apu::write_data_( int addr, int data ) +{ + assert( (unsigned) addr < reg_count ); + + if ( (unsigned) addr >= 14 ) + { + #ifdef dprintf + dprintf( "Wrote to I/O port %02X\n", (int) addr ); + #endif + } + + // 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) * (0x100L * 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; + blargg_ulong const old_noise_lfsr = noise.lfsr; + + // envelope period + blip_time_t const env_period_factor = period_factor * 2; // verified + blip_time_t env_period = (regs [12] * 0x100L + 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 = (blargg_ulong) (osc_output->clock_rate() + + inaudible_freq) / (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 volume = amp_table [vol_mode & 0x0F] >> half_vol; + int osc_env_pos = env.pos; + if ( vol_mode & 0x10 ) + { + volume = env.wave [osc_env_pos] >> half_vol; + // 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 + { + blargg_long count = (final_end_time - time + period - 1) / period; + time += count * period; + osc->phase ^= count & 1; + } + + // noise time + blip_time_t ntime = final_end_time; + blargg_ulong 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 + blargg_long remain = end - ntime; + blargg_long 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; + //phase ^= 1; + } + //assert( phase == (delta > 0) ); + phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); + // (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; + + 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; + 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 ) + { + blargg_long 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/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h new file mode 100644 index 00000000..31956939 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Apu.h @@ -0,0 +1,107 @@ +// AY-3-8910 sound chip emulator + +// Game_Music_Emu 0.5.2 +#ifndef AY_APU_H +#define AY_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Ay_Apu { +public: + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Reset sound chip + void reset(); + + // Write to register at specified time + enum { reg_count = 16 }; + void write( blip_time_t time, int addr, int data ); + + // Run sound to specified time, end current time frame, then start 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( blip_time_t length ); + +// Additional features + + // Set sound output of specific oscillator to buffer, where index is + // 0, 1, or 2. If buffer is NULL, the specified oscillator is muted. + enum { osc_count = 3 }; + void osc_output( int index, Blip_Buffer* ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see documentation) + void treble_eq( blip_eq_t const& ); + +public: + Ay_Apu(); + typedef unsigned char byte; +private: + struct osc_t + { + blip_time_t period; + blip_time_t delay; + short last_amp; + short phase; + Blip_Buffer* output; + } oscs [osc_count]; + blip_time_t last_time; + byte latch; + byte regs [reg_count]; + + struct { + blip_time_t delay; + blargg_ulong lfsr; + } noise; + + struct { + blip_time_t delay; + byte const* wave; + int pos; + byte modes [8] [48]; // values already passed through volume table + } env; + + void run_until( blip_time_t ); + void write_data_( int addr, int data ); +public: + enum { amp_range = 255 }; + Blip_Synth synth_; +}; + +inline void Ay_Apu::volume( double v ) { synth_.volume( 0.7 / osc_count / amp_range * v ); } + +inline void Ay_Apu::treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } + +inline void Ay_Apu::write( blip_time_t time, int addr, int data ) +{ + run_until( time ); + write_data_( addr, data ); +} + +inline void Ay_Apu::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Ay_Apu::output( Blip_Buffer* buf ) +{ + osc_output( 0, buf ); + osc_output( 1, buf ); + osc_output( 2, buf ); +} + +inline void Ay_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp new file mode 100644 index 00000000..6ff7156b --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.cpp @@ -0,0 +1,1665 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +/* +Last validated with zexall 2006.11.21 5:26 PM +* Doesn't implement the R register or immediate interrupt after EI. +* Address wrap-around isn't completely correct, but is prevented from crashing emulator. +*/ + +#include "Ay_Cpu.h" + +#include "blargg_endian.h" +#include + +//#include "z80_cpu_log.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 */ + +#define SYNC_TIME() (void) (s.time = s_time) +#define RELOAD_TIME() (void) (s_time = s.time) + +// Callbacks to emulator + +#define CPU_OUT( cpu, addr, data, TIME )\ + ay_cpu_out( cpu, TIME, addr, data ) + +#define CPU_IN( cpu, addr, TIME )\ + ay_cpu_in( cpu, addr ) + +#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; + +#define SZ28P( n ) szpc [n] +#define SZ28PC( n ) szpc [n] +#define SZ28C( n ) (szpc [n] & ~P04) +#define SZ28( n ) SZ28C( n ) + +#define SET_R( n ) (void) (r.r = n) +#define GET_R() (r.r) + +Ay_Cpu::Ay_Cpu() +{ + state = &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; +} + +void Ay_Cpu::reset( void* m ) +{ + mem = (uint8_t*) m; + + check( state == &state_ ); + state = &state_; + state_.time = 0; + state_.base = 0; + end_time_ = 0; + + memset( &r, 0, sizeof r ); +} + +#define TIME (s_time + s.base) +#define READ_PROG( addr ) (mem [addr]) +#define INSTR( offset ) READ_PROG( pc + (offset) ) +#define GET_ADDR() GET_LE16( &READ_PROG( pc ) ) +#define READ( addr ) READ_PROG( addr ) +#define WRITE( addr, data ) (void) (READ_PROG( addr ) = data) +#define READ_WORD( addr ) GET_LE16( &READ_PROG( addr ) ) +#define WRITE_WORD( addr, data ) SET_LE16( &READ_PROG( addr ), data ) +#define IN( addr ) CPU_IN( this, addr, TIME ) +#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) + +#if BLARGG_BIG_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [n]) +#elif BLARGG_LITTLE_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) +#else + #error "Byte order of CPU must be known" +#endif + +//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) + +// help compiler see that it can just adjust stack offset, saving an extra instruction +#define R16( n, shift, offset )\ + (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) + +#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e +#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f +#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g +#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h + +// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 +static byte const ed_dd_timing [0x100] = { +//0 1 2 3 4 5 6 7 8 9 A B C D E F +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, +0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, +0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, +0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +// even on x86, using short and unsigned char was slower +typedef int fint16; +typedef unsigned fuint16; +typedef unsigned fuint8; + +bool Ay_Cpu::run( cpu_time_t end_time ) +{ + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + bool warning = false; + + typedef BOOST::int8_t int8_t; + + union { + regs_t rg; + pairs_t rp; + uint8_t r8_ [8]; // indexed + uint16_t r16_ [4]; + }; + rg = this->r.b; + + cpu_time_t s_time = s.time; + uint8_t* const mem = this->mem; // cache + fuint16 pc = r.pc; + fuint16 sp = r.sp; + fuint16 ix = r.ix; // TODO: keep in memory for direct access? + fuint16 iy = r.iy; + int flags = r.b.flags; + + goto loop; +jr_not_taken: + s_time -= 5; + goto loop; +call_not_taken: + s_time -= 7; +jp_not_taken: + pc += 2; +loop: + + check( (unsigned long) pc < 0x10000 ); + check( (unsigned long) sp < 0x10000 ); + check( (unsigned) flags < 0x100 ); + check( (unsigned) ix < 0x10000 ); + check( (unsigned) iy < 0x10000 ); + + fuint8 opcode; + opcode = READ_PROG( pc ); + pc++; + + static byte const base_timing [0x100] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 + 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 + 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 + 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 + 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B + 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C + 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D + 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E + 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F + }; + + fuint16 data; + data = base_timing [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = READ_PROG( pc ); + + #ifdef Z80_CPU_LOG_H + //log_opcode( opcode, READ_PROG( pc ) ); + z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); + z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), + READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Common + + case 0x00: // NOP + CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. + goto loop; + + case 0x08:{// EX AF,AF' + int temp = r.alt.b.a; + r.alt.b.a = rg.a; + rg.a = temp; + + temp = r.alt.b.flags; + r.alt.b.flags = flags; + flags = temp; + goto loop; + } + + case 0xD3: // OUT (imm),A + pc++; + OUT( data + rg.a * 0x100, rg.a ); + goto loop; + + case 0x2E: // LD L,imm + pc++; + rg.l = data; + goto loop; + + case 0x3E: // LD A,imm + pc++; + rg.a = data; + goto loop; + + case 0x3A:{// LD A,(addr) + fuint16 addr = GET_ADDR(); + pc += 2; + rg.a = READ( addr ); + goto loop; + } + +// Conditional + +#define ZERO (flags & Z40) +#define CARRY (flags & C01) +#define EVEN (flags & P04) +#define MINUS (flags & S80) + +// JR +#define JR( cond ) {\ + int disp = (BOOST::int8_t) data;\ + pc++;\ + if ( !(cond) )\ + goto jr_not_taken;\ + pc += disp;\ + goto loop;\ +} + + case 0x20: JR( !ZERO ) // JR NZ,disp + case 0x28: JR( ZERO ) // JR Z,disp + case 0x30: JR( !CARRY ) // JR NC,disp + case 0x38: JR( CARRY ) // JR C,disp + case 0x18: JR( true ) // JR disp + + case 0x10:{// DJNZ disp + int temp = rg.b - 1; + rg.b = temp; + JR( temp ) + } + +// JP +#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; + + case 0xC2: JP( !ZERO ) // JP NZ,addr + case 0xCA: JP( ZERO ) // JP Z,addr + case 0xD2: JP( !CARRY ) // JP NC,addr + case 0xDA: JP( CARRY ) // JP C,addr + case 0xE2: JP( !EVEN ) // JP PO,addr + case 0xEA: JP( EVEN ) // JP PE,addr + case 0xF2: JP( !MINUS ) // JP P,addr + case 0xFA: JP( MINUS ) // JP M,addr + + case 0xC3: // JP addr + pc = GET_ADDR(); + goto loop; + + case 0xE9: // JP HL + pc = rp.hl; + goto loop; + +// RET +#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; + + case 0xC0: RET( !ZERO ) // RET NZ + case 0xC8: RET( ZERO ) // RET Z + case 0xD0: RET( !CARRY ) // RET NC + case 0xD8: RET( CARRY ) // RET C + case 0xE0: RET( !EVEN ) // RET PO + case 0xE8: RET( EVEN ) // RET PE + case 0xF0: RET( !MINUS ) // RET P + case 0xF8: RET( MINUS ) // RET M + + case 0xC9: // RET + ret_taken: + pc = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// CALL +#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; + + case 0xC4: CALL( !ZERO ) // CALL NZ,addr + case 0xCC: CALL( ZERO ) // CALL Z,addr + case 0xD4: CALL( !CARRY ) // CALL NC,addr + case 0xDC: CALL( CARRY ) // CALL C,addr + case 0xE4: CALL( !EVEN ) // CALL PO,addr + case 0xEC: CALL( EVEN ) // CALL PE,addr + case 0xF4: CALL( !MINUS ) // CALL P,addr + case 0xFC: CALL( MINUS ) // CALL M,addr + + case 0xCD:{// CALL addr + call_taken: + fuint16 addr = pc + 2; + pc = GET_ADDR(); + sp = uint16_t (sp - 2); + WRITE_WORD( sp, addr ); + goto loop; + } + + case 0xFF: // RST + if ( (pc - 1) > 0xFFFF ) + { + pc = uint16_t (pc - 1); + s_time -= 11; + goto loop; + } + CASE7( C7, CF, D7, DF, E7, EF, F7 ): + data = pc; + pc = opcode & 0x38; + goto push_data; + +// PUSH/POP + case 0xF5: // PUSH AF + data = rg.a * 0x100u + flags; + goto push_data; + + case 0xC5: // PUSH BC + case 0xD5: // PUSH DE + case 0xE5: // PUSH HL + data = R16( opcode, 4, 0xC5 ); + push_data: + sp = uint16_t (sp - 2); + WRITE_WORD( sp, data ); + goto loop; + + case 0xF1: // POP AF + flags = READ( sp ); + rg.a = READ( sp + 1 ); + sp = uint16_t (sp + 2); + goto loop; + + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL + R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// ADC/ADD/SBC/SUB + case 0x96: // SUB (HL) + case 0x86: // ADD (HL) + flags &= ~C01; + case 0x9E: // SBC (HL) + case 0x8E: // ADC (HL) + data = READ( rp.hl ); + goto adc_data; + + case 0xD6: // SUB A,imm + case 0xC6: // ADD imm + flags &= ~C01; + case 0xDE: // SBC A,imm + case 0xCE: // ADC imm + pc++; + goto adc_data; + + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r + flags &= ~C01; + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r + data = R8( opcode & 7, 0 ); + adc_data: { + int result = data + (flags & C01); + data ^= rg.a; + flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes + if ( flags ) + result = -result; + result += rg.a; + data ^= result; + flags |=(data & H10) | + ((data - -0x80) >> 6 & V04) | + SZ28C( result & 0x1FF ); + rg.a = result; + goto loop; + } + +// CP + case 0xBE: // CP (HL) + data = READ( rp.hl ); + goto cp_data; + + case 0xFE: // CP imm + pc++; + goto cp_data; + + CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r + data = R8( opcode, 0xB8 ); + cp_data: { + int result = rg.a - data; + flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); + data ^= rg.a; + flags |=(((result ^ rg.a) & data) >> 5 & V04) | + (((data & H10) ^ result) & (S80 | H10)); + if ( (uint8_t) result ) + goto loop; + flags |= Z40; + goto loop; + } + +// ADD HL,rp + + case 0x39: // ADD HL,SP + data = sp; + goto add_hl_data; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + data = R16( opcode, 4, 0x09 ); + add_hl_data: { + blargg_ulong sum = rp.hl + data; + data ^= rp.hl; + rp.hl = sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((data ^ sum) >> 8 & H10); + goto loop; + } + + case 0x27:{// DAA + int a = rg.a; + if ( a > 0x99 ) + flags |= C01; + + int adjust = 0x60 & -(flags & C01); + + if ( flags & H10 || (a & 0x0F) > 9 ) + adjust |= 0x06; + + if ( flags & N02 ) + adjust = -adjust; + a += adjust; + + flags = (flags & (C01 | N02)) | + ((rg.a ^ a) & H10) | + SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + /* + case 0x27:{// DAA + // more optimized, but probably not worth the obscurity + int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags + int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 + + if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 + adjust |= 0x06; + + if ( f & N02 ) + adjust = -adjust; + int a = rg.a + adjust; + + flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + */ + +// INC/DEC + case 0x34: // INC (HL) + data = READ( rp.hl ) + 1; + WRITE( rp.hl, data ); + goto inc_set_flags; + + CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r + data = ++R8( opcode >> 3, 0 ); + inc_set_flags: + flags = (flags & C01) | + (((data & 0x0F) - 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x80 ) + goto loop; + flags |= V04; + goto loop; + + case 0x35: // DEC (HL) + data = READ( rp.hl ) - 1; + WRITE( rp.hl, data ); + goto dec_set_flags; + + CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r + data = --R8( opcode >> 3, 0 ); + dec_set_flags: + flags = (flags & C01) | N02 | + (((data & 0x0F) + 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x7F ) + goto loop; + flags |= V04; + goto loop; + + case 0x03: // INC BC + case 0x13: // INC DE + case 0x23: // INC HL + R16( opcode, 4, 0x03 )++; + goto loop; + + case 0x33: // INC SP + sp = uint16_t (sp + 1); + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + R16( opcode, 4, 0x0B )--; + goto loop; + + case 0x3B: // DEC SP + sp = uint16_t (sp - 1); + goto loop; + +// AND + case 0xA6: // AND (HL) + data = READ( rp.hl ); + goto and_data; + + case 0xE6: // AND imm + pc++; + goto and_data; + + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r + data = R8( opcode, 0xA0 ); + and_data: + rg.a &= data; + flags = SZ28P( rg.a ) | H10; + goto loop; + +// OR + case 0xB6: // OR (HL) + data = READ( rp.hl ); + goto or_data; + + case 0xF6: // OR imm + pc++; + goto or_data; + + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r + data = R8( opcode, 0xB0 ); + or_data: + rg.a |= data; + flags = SZ28P( rg.a ); + goto loop; + +// XOR + case 0xAE: // XOR (HL) + data = READ( rp.hl ); + goto xor_data; + + case 0xEE: // XOR imm + pc++; + goto xor_data; + + CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r + data = R8( opcode, 0xA8 ); + xor_data: + rg.a ^= data; + flags = SZ28P( rg.a ); + goto loop; + +// LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r + WRITE( rp.hl, R8( opcode, 0x70 ) ); + goto loop; + + CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r + CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r + CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r + CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r + CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r + CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r + CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r + R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); + goto loop; + + CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm + R8( opcode >> 3, 0 ) = data; + pc++; + goto loop; + + case 0x36: // LD (HL),imm + pc++; + WRITE( rp.hl, data ); + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) + R8( opcode >> 3, 8 ) = READ( rp.hl ); + goto loop; + + case 0x01: // LD rp,imm + case 0x11: + case 0x21: + R16( opcode, 4, 0x01 ) = GET_ADDR(); + pc += 2; + goto loop; + + case 0x31: // LD sp,imm + sp = GET_ADDR(); + pc += 2; + goto loop; + + case 0x2A:{// LD HL,(addr) + fuint16 addr = GET_ADDR(); + pc += 2; + rp.hl = READ_WORD( addr ); + goto loop; + } + + case 0x32:{// LD (addr),A + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE( addr, rg.a ); + goto loop; + } + + case 0x22:{// LD (addr),HL + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, rp.hl ); + goto loop; + } + + case 0x02: // LD (BC),A + case 0x12: // LD (DE),A + WRITE( R16( opcode, 4, 0x02 ), rg.a ); + goto loop; + + case 0x0A: // LD A,(BC) + case 0x1A: // LD A,(DE) + rg.a = READ( R16( opcode, 4, 0x0A ) ); + goto loop; + + case 0xF9: // LD SP,HL + sp = rp.hl; + goto loop; + +// Rotate + + case 0x07:{// RLCA + fuint16 temp = rg.a; + temp = (temp << 1) | (temp >> 7); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08 | C01)); + rg.a = temp; + goto loop; + } + + case 0x0F:{// RRCA + fuint16 temp = rg.a; + flags = (flags & (S80 | Z40 | P04)) | + (temp & C01); + temp = (temp << 7) | (temp >> 1); + flags |= temp & (F20 | F08); + rg.a = temp; + goto loop; + } + + case 0x17:{// RLA + blargg_ulong temp = (rg.a << 1) | (flags & C01); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (temp >> 8); + rg.a = temp; + goto loop; + } + + case 0x1F:{// RRA + fuint16 temp = (flags << 7) | (rg.a >> 1); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (rg.a & C01); + rg.a = temp; + goto loop; + } + +// Misc + case 0x2F:{// CPL + fuint16 temp = ~rg.a; + flags = (flags & (S80 | Z40 | P04 | C01)) | + (temp & (F20 | F08)) | + (H10 | N02); + rg.a = temp; + goto loop; + } + + case 0x3F:{// CCF + flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | + (flags << 4 & H10) | + (rg.a & (F20 | F08)); + goto loop; + } + + case 0x37: // SCF + flags = (flags & (S80 | Z40 | P04)) | C01 | + (rg.a & (F20 | F08)); + goto loop; + + case 0xDB: // IN A,(imm) + pc++; + rg.a = IN( data + rg.a * 0x100 ); + goto loop; + + case 0xE3:{// EX (SP),HL + fuint16 temp = READ_WORD( sp ); + WRITE_WORD( sp, rp.hl ); + rp.hl = temp; + goto loop; + } + + case 0xEB:{// EX DE,HL + fuint16 temp = rp.hl; + rp.hl = rp.de; + rp.de = temp; + goto loop; + } + + case 0xD9:{// EXX DE,HL + fuint16 temp = r.alt.w.bc; + r.alt.w.bc = rp.bc; + rp.bc = temp; + + temp = r.alt.w.de; + r.alt.w.de = rp.de; + rp.de = temp; + + temp = r.alt.w.hl; + r.alt.w.hl = rp.hl; + rp.hl = temp; + goto loop; + } + + case 0xF3: // DI + r.iff1 = 0; + r.iff2 = 0; + goto loop; + + case 0xFB: // EI + r.iff1 = 1; + r.iff2 = 1; + // TODO: delayed effect + goto loop; + + case 0x76: // HALT + goto halt; + +//////////////////////////////////////// CB prefix + { + case 0xCB: + unsigned data2; + data2 = INSTR( 1 ); + pc++; + switch ( data ) + { + + // Rotate left + + #define RLC( read, write ) {\ + fuint8 result = read;\ + result = uint8_t (result << 1) | (result >> 7);\ + flags = SZ28P( result ) | (result & C01);\ + write;\ + goto loop;\ + } + + case 0x06: // RLC (HL) + s_time += 7; + data = rp.hl; + rlc_data_addr: + RLC( READ( data ), WRITE( data, result ) ) + + CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r + uint8_t& reg = R8( data, 0 ); + RLC( reg, reg = result ) + } + + #define RL( read, write ) {\ + fuint16 result = (read << 1) | (flags & C01);\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x16: // RL (HL) + s_time += 7; + data = rp.hl; + rl_data_addr: + RL( READ( data ), WRITE( data, result ) ) + + CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r + uint8_t& reg = R8( data, 0x10 ); + RL( reg, reg = result ) + } + + #define SLA( read, add, write ) {\ + fuint16 result = (read << 1) | add;\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x26: // SLA (HL) + s_time += 7; + data = rp.hl; + sla_data_addr: + SLA( READ( data ), 0, WRITE( data, result ) ) + + CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r + uint8_t& reg = R8( data, 0x20 ); + SLA( reg, 0, reg = result ) + } + + case 0x36: // SLL (HL) + s_time += 7; + data = rp.hl; + sll_data_addr: + SLA( READ( data ), 1, WRITE( data, result ) ) + + CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r + uint8_t& reg = R8( data, 0x30 ); + SLA( reg, 1, reg = result ) + } + + // Rotate right + + #define RRC( read, write ) {\ + fuint8 result = read;\ + flags = result & C01;\ + result = uint8_t (result << 7) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x0E: // RRC (HL) + s_time += 7; + data = rp.hl; + rrc_data_addr: + RRC( READ( data ), WRITE( data, result ) ) + + CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r + uint8_t& reg = R8( data, 0x08 ); + RRC( reg, reg = result ) + } + + #define RR( read, write ) {\ + fuint8 result = read;\ + fuint8 temp = result & C01;\ + result = uint8_t (flags << 7) | (result >> 1);\ + flags = SZ28P( result ) | temp;\ + write;\ + goto loop;\ + } + + case 0x1E: // RR (HL) + s_time += 7; + data = rp.hl; + rr_data_addr: + RR( READ( data ), WRITE( data, result ) ) + + CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r + uint8_t& reg = R8( data, 0x18 ); + RR( reg, reg = result ) + } + + #define SRA( read, write ) {\ + fuint8 result = read;\ + flags = result & C01;\ + result = (result & 0x80) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x2E: // SRA (HL) + data = rp.hl; + s_time += 7; + sra_data_addr: + SRA( READ( data ), WRITE( data, result ) ) + + CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r + uint8_t& reg = R8( data, 0x28 ); + SRA( reg, reg = result ) + } + + #define SRL( read, write ) {\ + fuint8 result = read;\ + flags = result & C01;\ + result >>= 1;\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x3E: // SRL (HL) + s_time += 7; + data = rp.hl; + srl_data_addr: + SRL( READ( data ), WRITE( data, result ) ) + + CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r + uint8_t& reg = R8( data, 0x38 ); + SRL( reg, reg = result ) + } + + // BIT + { + unsigned temp; + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) + s_time += 4; + temp = READ( rp.hl ); + flags &= C01; + goto bit_temp; + CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r + CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r + CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r + CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r + CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r + CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r + CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r + temp = R8( data & 7, 0 ); + flags = (flags & C01) | (temp & (F20 | F08)); + bit_temp: + int masked = temp & 1 << (data >> 3 & 7); + flags |=(masked & S80) | H10 | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + // SET/RES + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) + s_time += 7; + int temp = READ( rp.hl ); + int bit = 1 << (data >> 3 & 7); + temp |= bit; // SET + if ( !(data & 0x40) ) + temp ^= bit; // RES + WRITE( rp.hl, temp ); + goto loop; + } + + CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r + CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r + CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r + CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r + CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r + CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r + CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r + CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r + R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); + goto loop; + + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r + CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r + CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r + R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); + goto loop; + } + assert( false ); + } + +//////////////////////////////////////// ED prefix + { + case 0xED: + pc++; + s_time += ed_dd_timing [data] >> 4; + switch ( data ) + { + { + blargg_ulong temp; + case 0x72: // SBC HL,SP + case 0x7A: // ADC HL,SP + temp = sp; + if ( 0 ) + case 0x42: // SBC HL,BC + case 0x52: // SBC HL,DE + case 0x62: // SBC HL,HL + case 0x4A: // ADC HL,BC + case 0x5A: // ADC HL,DE + case 0x6A: // ADC HL,HL + temp = R16( data >> 3 & 6, 1, 0 ); + blargg_ulong sum = temp + (flags & C01); + flags = ~data >> 2 & N02; + if ( flags ) + sum = -sum; + sum += rp.hl; + temp ^= rp.hl; + temp ^= sum; + flags |=(sum >> 16 & C01) | + (temp >> 8 & H10) | + (sum >> 8 & (S80 | F20 | F08)) | + ((temp - -0x8000) >> 14 & V04); + rp.hl = sum; + if ( (uint16_t) sum ) + goto loop; + flags |= Z40; + goto loop; + } + + CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) + int temp = IN( rp.bc ); + R8( data >> 3, 8 ) = temp; + flags = (flags & C01) | SZ28P( temp ); + goto loop; + } + + case 0x71: // OUT (C),0 + rg.flags = 0; + CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r + OUT( rp.bc, R8( data >> 3, 8 ) ); + goto loop; + + { + unsigned temp; + case 0x73: // LD (ADDR),SP + temp = sp; + if ( 0 ) + case 0x43: // LD (ADDR),BC + case 0x53: // LD (ADDR),DE + temp = R16( data, 4, 0x43 ); + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, temp ); + goto loop; + } + + case 0x4B: // LD BC,(ADDR) + case 0x5B:{// LD DE,(ADDR) + fuint16 addr = GET_ADDR(); + pc += 2; + R16( data, 4, 0x4B ) = READ_WORD( addr ); + goto loop; + } + + case 0x7B:{// LD SP,(ADDR) + fuint16 addr = GET_ADDR(); + pc += 2; + sp = READ_WORD( addr ); + goto loop; + } + + case 0x67:{// RRD + fuint8 temp = READ( rp.hl ); + WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); + temp = (rg.a & 0xF0) | (temp & 0x0F); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + case 0x6F:{// RLD + fuint8 temp = READ( rp.hl ); + WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); + temp = (rg.a & 0xF0) | (temp >> 4); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG + opcode = 0x10; // flag to do SBC instead of ADC + flags &= ~C01; + data = rg.a; + rg.a = 0; + goto adc_data; + + { + int inc; + case 0xA9: // CPD + case 0xB9: // CPDR + inc = -1; + if ( 0 ) + case 0xA1: // CPI + case 0xB1: // CPIR + inc = +1; + fuint16 addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int result = rg.a - temp; + flags = (flags & C01) | N02 | + ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); + + if ( !(uint8_t) result ) flags |= Z40; + result -= (flags & H10) >> 4; + flags |= result & F08; + flags |= result << 4 & F20; + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( flags & Z40 || data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xA8: // LDD + case 0xB8: // LDDR + inc = -1; + if ( 0 ) + case 0xA0: // LDI + case 0xB0: // LDIR + inc = +1; + fuint16 addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + addr = rp.de; + rp.de = addr + inc; + WRITE( addr, temp ); + + temp += rg.a; + flags = (flags & (S80 | Z40 | C01)) | + (temp & F08) | (temp << 4 & F20); + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xAB: // OUTD + case 0xBB: // OTDR + inc = -1; + if ( 0 ) + case 0xA3: // OUTI + case 0xB3: // OTIR + inc = +1; + fuint16 addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + OUT( rp.bc, temp ); + goto loop; + } + + { + int inc; + case 0xAA: // IND + case 0xBA: // INDR + inc = -1; + if ( 0 ) + case 0xA2: // INI + case 0xB2: // INIR + inc = +1; + + fuint16 addr = rp.hl; + rp.hl = addr + inc; + + int temp = IN( rp.bc ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + WRITE( addr, temp ); + goto loop; + } + + case 0x47: // LD I,A + r.i = rg.a; + goto loop; + + case 0x4F: // LD R,A + SET_R( rg.a ); + dprintf( "LD R,A not supported\n" ); + warning = true; + goto loop; + + case 0x57: // LD A,I + rg.a = r.i; + goto ld_ai_common; + + case 0x5F: // LD A,R + rg.a = GET_R(); + dprintf( "LD A,R not supported\n" ); + warning = true; + ld_ai_common: + flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); + goto loop; + + CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN + r.iff1 = r.iff2; + goto ret_taken; + + case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 + r.im = 0; + goto loop; + + case 0x56: case 0x76: // IM 1 + r.im = 1; + goto loop; + + case 0x5E: case 0x7E: // IM 2 + r.im = 2; + goto loop; + + default: + dprintf( "Opcode $ED $%02X not supported\n", data ); + warning = true; + goto loop; + } + assert( false ); + } + +//////////////////////////////////////// DD/FD prefix + { + fuint16 ixy; + case 0xDD: + ixy = ix; + goto ix_prefix; + case 0xFD: + ixy = iy; + ix_prefix: + pc++; + unsigned data2 = READ_PROG( pc ); + s_time += ed_dd_timing [data] & 0x0F; + switch ( data ) + { + // TODO: more efficient way of avoid negative address + #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) + + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; + + // ADD/ADC/SUB/SBC + + case 0x96: // SUB (IXY+disp) + case 0x86: // ADD (IXY+disp) + flags &= ~C01; + case 0x9E: // SBC (IXY+disp) + case 0x8E: // ADC (IXY+disp) + pc++; + opcode = data; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto adc_data; + + case 0x94: // SUB HXY + case 0x84: // ADD HXY + flags &= ~C01; + case 0x9C: // SBC HXY + case 0x8C: // ADC HXY + opcode = data; + data = ixy >> 8; + goto adc_data; + + case 0x95: // SUB LXY + case 0x85: // ADD LXY + flags &= ~C01; + case 0x9D: // SBC LXY + case 0x8D: // ADC LXY + opcode = data; + data = (uint8_t) ixy; + goto adc_data; + + { + unsigned temp; + case 0x39: // ADD IXY,SP + temp = sp; + goto add_ixy_data; + + case 0x29: // ADD IXY,HL + temp = ixy; + goto add_ixy_data; + + case 0x09: // ADD IXY,BC + case 0x19: // ADD IXY,DE + temp = R16( data, 4, 0x09 ); + add_ixy_data: { + blargg_ulong sum = ixy + temp; + temp ^= ixy; + ixy = (uint16_t) sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((temp ^ sum) >> 8 & H10); + goto set_ixy; + } + } + + // AND + case 0xA6: // AND (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto and_data; + + case 0xA4: // AND HXY + data = ixy >> 8; + goto and_data; + + case 0xA5: // AND LXY + data = (uint8_t) ixy; + goto and_data; + + // OR + case 0xB6: // OR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto or_data; + + case 0xB4: // OR HXY + data = ixy >> 8; + goto or_data; + + case 0xB5: // OR LXY + data = (uint8_t) ixy; + goto or_data; + + // XOR + case 0xAE: // XOR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto xor_data; + + case 0xAC: // XOR HXY + data = ixy >> 8; + goto xor_data; + + case 0xAD: // XOR LXY + data = (uint8_t) ixy; + goto xor_data; + + // CP + case 0xBE: // CP (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto cp_data; + + case 0xBC: // CP HXY + data = ixy >> 8; + goto cp_data; + + case 0xBD: // CP LXY + data = (uint8_t) ixy; + goto cp_data; + + // LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r + data = R8( data, 0x70 ); + if ( 0 ) + case 0x36: // LD (IXY+disp),imm + pc++, data = READ_PROG( pc ); + pc++; + WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); + goto loop; + + CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY + R8( data >> 3, 8 ) = ixy >> 8; + goto loop; + + case 0x64: // LD HXY,HXY + case 0x6D: // LD LXY,LXY + goto loop; + + CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY + R8( data >> 3, 8 ) = ixy; + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) + pc++; + R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto loop; + + case 0x26: // LD HXY,imm + pc++; + goto ld_hxy_data; + + case 0x65: // LD HXY,LXY + data2 = (uint8_t) ixy; + goto ld_hxy_data; + + CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r + data2 = R8( data, 0x60 ); + ld_hxy_data: + ixy = (uint8_t) ixy | (data2 << 8); + goto set_ixy; + + case 0x2E: // LD LXY,imm + pc++; + goto ld_lxy_data; + + case 0x6C: // LD LXY,HXY + data2 = ixy >> 8; + goto ld_lxy_data; + + CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r + data2 = R8( data, 0x68 ); + ld_lxy_data: + ixy = (ixy & 0xFF00) | data2; + set_ixy: + if ( opcode == 0xDD ) + { + ix = ixy; + goto loop; + } + iy = ixy; + goto loop; + + case 0xF9: // LD SP,IXY + sp = ixy; + goto loop; + + case 0x22:{// LD (ADDR),IXY + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, ixy ); + goto loop; + } + + case 0x21: // LD IXY,imm + ixy = GET_ADDR(); + pc += 2; + goto set_ixy; + + case 0x2A:{// LD IXY,(addr) + fuint16 addr = GET_ADDR(); + ixy = READ_WORD( addr ); + pc += 2; + goto set_ixy; + } + + // DD/FD CB prefix + case 0xCB: { + data = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data2 = READ_PROG( pc ); + pc++; + switch ( data2 ) + { + case 0x06: goto rlc_data_addr; // RLC (IXY) + case 0x16: goto rl_data_addr; // RL (IXY) + case 0x26: goto sla_data_addr; // SLA (IXY) + case 0x36: goto sll_data_addr; // SLL (IXY) + case 0x0E: goto rrc_data_addr; // RRC (IXY) + case 0x1E: goto rr_data_addr; // RR (IXY) + case 0x2E: goto sra_data_addr; // SRA (IXY) + case 0x3E: goto srl_data_addr; // SRL (IXY) + + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) + fuint8 temp = READ( data ); + int masked = temp & 1 << (data2 >> 3 & 7); + flags = (flags & C01) | H10 | + (masked & S80) | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) + int temp = READ( data ); + int bit = 1 << (data2 >> 3 & 7); + temp |= bit; // SET + if ( !(data2 & 0x40) ) + temp ^= bit; // RES + WRITE( data, temp ); + goto loop; + } + + default: + dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); + warning = true; + goto loop; + } + assert( false ); + } + + // INC/DEC + case 0x23: // INC IXY + ixy = uint16_t (ixy + 1); + goto set_ixy; + + case 0x2B: // DEC IXY + ixy = uint16_t (ixy - 1); + goto set_ixy; + + case 0x34: // INC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) + 1; + WRITE( ixy, data ); + goto inc_set_flags; + + case 0x35: // DEC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) - 1; + WRITE( ixy, data ); + goto dec_set_flags; + + case 0x24: // INC HXY + ixy = uint16_t (ixy + 0x100); + data = ixy >> 8; + goto inc_xy_common; + + case 0x2C: // INC LXY + data = uint8_t (ixy + 1); + ixy = (ixy & 0xFF00) | data; + inc_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto inc_set_flags; + } + iy = ixy; + goto inc_set_flags; + + case 0x25: // DEC HXY + ixy = uint16_t (ixy - 0x100); + data = ixy >> 8; + goto dec_xy_common; + + case 0x2D: // DEC LXY + data = uint8_t (ixy - 1); + ixy = (ixy & 0xFF00) | data; + dec_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto dec_set_flags; + } + iy = ixy; + goto dec_set_flags; + + // PUSH/POP + case 0xE5: // PUSH IXY + data = ixy; + goto push_data; + + case 0xE1:{// POP IXY + ixy = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto set_ixy; + } + + // Misc + + case 0xE9: // JP (IXY) + pc = ixy; + goto loop; + + case 0xE3:{// EX (SP),IXY + fuint16 temp = READ_WORD( sp ); + WRITE_WORD( sp, ixy ); + ixy = temp; + goto set_ixy; + } + + default: + dprintf( "Unnecessary DD/FD prefix encountered\n" ); + warning = true; + pc--; + goto loop; + } + assert( false ); + } + + } + dprintf( "Unhandled main opcode: $%02X\n", opcode ); + assert( false ); + +halt: + s_time &= 3; // increment by multiple of 4 +out_of_time: + pc--; + + s.time = s_time; + rg.flags = flags; + r.ix = ix; + r.iy = iy; + r.sp = sp; + r.pc = pc; + this->r.b = rg; + this->state_ = s; + this->state = &this->state_; + + return warning; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h new file mode 100644 index 00000000..07241d5e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Cpu.h @@ -0,0 +1,92 @@ +// Z80 CPU emulator + +// Game_Music_Emu 0.5.2 +#ifndef AY_CPU_H +#define AY_CPU_H + +#include "blargg_endian.h" + +typedef blargg_long cpu_time_t; + +// must be defined by caller +void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data ); +int ay_cpu_in( class Ay_Cpu*, unsigned addr ); + +class Ay_Cpu { +public: + // Clear all registers and keep pointer to 64K memory passed in + void reset( void* mem_64k ); + + // Run until specified time is reached. Returns true if suspicious/unsupported + // instruction was encountered at any point during run. + bool run( cpu_time_t end_time ); + + // Time of beginning of next instruction + cpu_time_t time() const { return state->time + state->base; } + + // Alter current time. Not supported during run() call. + void set_time( cpu_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + typedef BOOST::uint8_t uint8_t; + typedef BOOST::uint16_t uint16_t; + + #if BLARGG_BIG_ENDIAN + struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; + #else + struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; + #endif + BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 ); + + struct pairs_t { uint16_t bc, de, hl, fa; }; + + // Registers are not updated until run() returns + struct registers_t { + uint16_t pc; + uint16_t sp; + uint16_t ix; + uint16_t iy; + union { + regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a + pairs_t w; // w.bc, w.de, w.hl. w.fa + }; + union { + regs_t b; + pairs_t w; + } alt; + uint8_t iff1; + uint8_t iff2; + uint8_t r; + uint8_t i; + uint8_t im; + }; + //registers_t r; (below for efficiency) + + // can read this far past end of memory + enum { cpu_padding = 0x100 }; + +public: + Ay_Cpu(); +private: + uint8_t szpc [0x200]; + uint8_t* mem; + cpu_time_t end_time_; + struct state_t { + cpu_time_t base; + cpu_time_t time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + void set_end_time( cpu_time_t t ); +public: + registers_t r; +}; + +inline void Ay_Cpu::set_end_time( cpu_time_t t ) +{ + cpu_time_t delta = state->base - t; + state->base = t; + state->time += delta; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp new file mode 100644 index 00000000..bdc82e9e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.cpp @@ -0,0 +1,404 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Ay_Emu.h" + +#include "blargg_endian.h" +#include + +/* 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" + +long const spectrum_clock = 3546900; +long const cpc_clock = 2000000; + +unsigned const ram_start = 0x4000; +int const osc_count = Ay_Apu::osc_count + 1; + +Ay_Emu::Ay_Emu() +{ + beeper_output = 0; + set_type( gme_ay_type ); + + static const char* const names [osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Beeper" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type | 0, wave_type | 1, wave_type | 2, mixed_type | 0 + }; + set_voice_types( types ); + set_silence_lookahead( 6 ); +} + +Ay_Emu::~Ay_Emu() { } + +// Track info + +static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int min_size ) +{ + long pos = ptr - (byte const*) file.header; + long file_size = file.end - (byte const*) file.header; + assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); + int offset = (BOOST::int16_t) get_be16( ptr ); + if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) ) + return 0; + return ptr + offset; +} + +static blargg_err_t parse_header( byte const* in, long size, Ay_Emu::file_t* out ) +{ + typedef Ay_Emu::header_t header_t; + out->header = (header_t const*) in; + out->end = in + size; + + if ( size < Ay_Emu::header_size ) + return gme_wrong_file_type; + + header_t const& h = *(header_t const*) in; + if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) + return gme_wrong_file_type; + + out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); + if ( !out->tracks ) + return "Missing track data"; + + return 0; +} + +static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int track ) +{ + Gme_File::copy_field_( out->song, (char const*) get_data( file, file.tracks + track * 4, 1 ) ); + byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 ); + if ( track_info ) + out->length = get_be16( track_info + 4 ) * (1000L / 50); // frames to msec + + Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) ); + Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) ); +} + +blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const +{ + copy_ay_fields( file, out, track ); + return 0; +} + +struct Ay_File : Gme_Info_ +{ + Ay_Emu::file_t file; + + Ay_File() { set_type( gme_ay_type ); } + + blargg_err_t load_mem_( byte const* begin, long size ) + { + RETURN_ERR( parse_header( begin, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + copy_ay_fields( file, out, track ); + return 0; + } +}; + +static Music_Emu* new_ay_emu () { return BLARGG_NEW Ay_Emu ; } +static Music_Emu* new_ay_file() { return BLARGG_NEW Ay_File; } + +gme_type_t_ const gme_ay_type [1] = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; + +// Setup + +blargg_err_t Ay_Emu::load_mem_( byte const* in, long size ) +{ + assert( offsetof (header_t,track_info [2]) == header_size ); + + RETURN_ERR( parse_header( in, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + + if ( file.header->vers > 2 ) + set_warning( "Unknown file version" ); + + set_voice_count( osc_count ); + apu.volume( gain() ); + + return setup_buffer( spectrum_clock ); +} + +void Ay_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); +} + +void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* ) +{ + if ( i >= Ay_Apu::osc_count ) + beeper_output = center; + else + apu.osc_output( i, center ); +} + +// Emulation + +void Ay_Emu::set_tempo_( double t ) +{ + play_period = blip_time_t (clock_rate() / 50 / t); +} + +blargg_err_t Ay_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET + memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 ); + memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start ); + memset( mem.padding1, 0xFF, sizeof mem.padding1 ); + memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 ); + + // locate data blocks + byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); + if ( !data ) return "File data missing"; + + byte const* const more_data = get_data( file, data + 10, 6 ); + if ( !more_data ) return "File data missing"; + + byte const* blocks = get_data( file, data + 12, 8 ); + if ( !blocks ) return "File data missing"; + + // initial addresses + cpu::reset( mem.ram ); + r.sp = get_be16( more_data ); + r.b.a = r.b.b = r.b.d = r.b.h = data [8]; + r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; + r.alt.w = r.w; + r.ix = r.iy = r.w.hl; + + unsigned addr = get_be16( blocks ); + if ( !addr ) return "File data missing"; + + unsigned init = get_be16( more_data + 2 ); + if ( !init ) + init = addr; + + // copy blocks into memory + do + { + blocks += 2; + unsigned len = get_be16( blocks ); blocks += 2; + if ( addr + len > 0x10000 ) + { + set_warning( "Bad data block size" ); + len = 0x10000 - addr; + } + check( len ); + byte const* in = get_data( file, blocks, 0 ); blocks += 2; + if ( len > blargg_ulong (file.end - in) ) + { + set_warning( "Missing file data" ); + len = file.end - in; + } + //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); + if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data + dprintf( "Block addr in ROM\n" ); + memcpy( mem.ram + addr, in, len ); + + if ( file.end - blocks < 8 ) + { + set_warning( "Missing file data" ); + break; + } + } + while ( (addr = get_be16( blocks )) != 0 ); + + // copy and configure driver + static byte const passive [] = { + 0xF3, // DI + 0xCD, 0, 0, // CALL init + 0xED, 0x5E, // LOOP: IM 2 + 0xFB, // EI + 0x76, // HALT + 0x18, 0xFA // JR LOOP + }; + static byte const active [] = { + 0xF3, // DI + 0xCD, 0, 0, // CALL init + 0xED, 0x56, // LOOP: IM 1 + 0xFB, // EI + 0x76, // HALT + 0xCD, 0, 0, // CALL play + 0x18, 0xF7 // JR LOOP + }; + memcpy( mem.ram, passive, sizeof passive ); + unsigned play_addr = get_be16( more_data + 4 ); + //dprintf( "Play: $%04X\n", play_addr ); + if ( play_addr ) + { + memcpy( mem.ram, active, sizeof active ); + mem.ram [ 9] = play_addr; + mem.ram [10] = play_addr >> 8; + } + mem.ram [2] = init; + mem.ram [3] = init >> 8; + + mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) + + memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh) + + beeper_delta = int (apu.amp_range * 0.65); + last_beeper = 0; + apu.reset(); + next_play = play_period; + + // start at spectrum speed + change_clock_rate( spectrum_clock ); + set_tempo( tempo() ); + + spectrum_mode = false; + cpc_mode = false; + cpc_latch = 0; + + return 0; +} + +// Emulation + +void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) +{ + if ( !cpc_mode ) + { + switch ( addr & 0xFEFF ) + { + case 0xFEFD: + spectrum_mode = true; + apu_addr = data & 0x0F; + return; + + case 0xBEFD: + spectrum_mode = true; + apu.write( time, apu_addr, data ); + return; + } + } + + if ( !spectrum_mode ) + { + switch ( addr >> 8 ) + { + case 0xF6: + switch ( data & 0xC0 ) + { + case 0xC0: + apu_addr = cpc_latch & 0x0F; + goto enable_cpc; + + case 0x80: + apu.write( time, apu_addr, 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; + change_clock_rate( cpc_clock ); + set_tempo( tempo() ); + } +} + +void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) +{ + Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu); + + if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode ) + { + int delta = emu.beeper_delta; + data &= 0x10; + if ( emu.last_beeper != data ) + { + emu.last_beeper = data; + emu.beeper_delta = -delta; + emu.spectrum_mode = true; + if ( emu.beeper_output ) + emu.apu.synth_.offset( time, delta, emu.beeper_output ); + } + } + else + { + emu.cpu_out_misc( time, addr, data ); + } +} + +int ay_cpu_in( Ay_Cpu*, unsigned 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; +} + +blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) +{ + set_time( 0 ); + if ( !(spectrum_mode | cpc_mode) ) + duration /= 2; // until mode is set, leave room for halved clock rate + + while ( time() < duration ) + { + cpu::run( min( duration, (blip_time_t) next_play ) ); + + if ( time() >= next_play ) + { + next_play += play_period; + + if ( r.iff1 ) + { + if ( mem.ram [r.pc] == 0x76 ) + r.pc++; + + r.iff1 = r.iff2 = 0; + + mem.ram [--r.sp] = uint8_t (r.pc >> 8); + mem.ram [--r.sp] = uint8_t (r.pc); + r.pc = 0x38; + cpu::adjust_time( 12 ); + if ( r.im == 2 ) + { + cpu::adjust_time( 6 ); + unsigned addr = r.i * 0x100u + 0xFF; + r.pc = mem.ram [(addr + 1) & 0xFFFF] * 0x100u + mem.ram [addr]; + } + } + } + } + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + adjust_time( -duration ); + + apu.end_frame( duration ); + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h new file mode 100644 index 00000000..ba8445d3 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ay_Emu.h @@ -0,0 +1,70 @@ +// Sinclair Spectrum AY music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef AY_EMU_H +#define AY_EMU_H + +#include "Classic_Emu.h" +#include "Ay_Apu.h" +#include "Ay_Cpu.h" + +class Ay_Emu : private Ay_Cpu, public Classic_Emu { + typedef Ay_Cpu cpu; +public: + // AY file header + enum { header_size = 0x14 }; + struct header_t + { + 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; } +public: + Ay_Emu(); + ~Ay_Emu(); + struct file_t { + header_t const* header; + byte const* end; + byte const* tracks; + }; +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); +private: + file_t file; + + unsigned play_addr; + cpu_time_t play_period; + cpu_time_t next_play; + Blip_Buffer* beeper_output; + int beeper_delta; + int last_beeper; + int apu_addr; + int cpc_latch; + bool spectrum_mode; + bool cpc_mode; + + // large items + struct { + byte padding1 [0x100]; + byte ram [0x10000 + 0x100]; + } mem; + Ay_Apu apu; + friend void ay_cpu_out( Ay_Cpu*, cpu_time_t, unsigned addr, int data ); + void cpu_out_misc( cpu_time_t, unsigned addr, int data ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp new file mode 100644 index 00000000..07e9d658 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.cpp @@ -0,0 +1,446 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" +#include "blargg_common.h" +#include +#include +#include +#include +#include + +/* 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 */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = 0x7fffffff; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + if ( msec != blip_max_length ) + { + long s = (new_rate * (msec + 1) + 999) / 1000; + if ( s < new_size ) + new_size = s; + else + assert( 0 ); // fails if requested buffer length exceeds limit + } + + if ( buffer_size_ != new_size ) + { + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return 0; // success +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const +{ + double ratio = (double) sample_rate_ / rate; + blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 ) + { + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } + } + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST + +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) + { + double angle = ((i - count) * 2 + 1) * to_angle; + double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); + double cos_nc_angle = cos( maxh * cutoff * angle ); + double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } +} + +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} + +void Blip_Synth_::adjust_impulse() +{ + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) + { + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); + } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; + + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); + + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size = this->impulses_size(); + for ( i = 0; i < impulses_size; i++ ) + { + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; + } + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); + } + } + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif + +long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) +{ + long count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } + } + else + { + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } + } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( count-- ) + { + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h new file mode 100644 index 00000000..73341b8c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Blip_Buffer.h @@ -0,0 +1,485 @@ +// Band-limited sound synthesis buffer + +// Blip_Buffer 0.4.1 +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +// internal +#include +#include +typedef int32_t blip_long; +typedef uint32_t blip_ulong; + +// Time unit at source clock rate +typedef blip_long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Count number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + + // not documented yet + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_ulong blip_resampled_time_t; + void remove_silence( long count ); + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); + + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_time_t buf_t_; + blip_ulong factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +#ifndef BLIP_BUFFER_ACCURACY + #define BLIP_BUFFER_ACCURACY 16 +#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #if BLIP_BUFFER_FAST + #define BLIP_PHASE_BITS 8 + #else + #define BLIP_PHASE_BITS 6 + #endif +#endif + + // Internal + typedef blip_ulong blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif +}; + +// Low-pass equalization parameters +class blip_eq_t { +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + +private: + double treble; + long rolloff_freq; + long sample_rate; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; +}; + +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; +public: + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); + + Silent_Blip_Buffer(); +}; + + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif + +// Optimized reading from Blip_Buffer, for use in custom sample output + +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ + +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) + +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; + +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { +public: + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; +}; + +// End of public interface + +#include + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +inline int Blip_Buffer::length() const { return length_; } +inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) +{ + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp new file mode 100644 index 00000000..063444fe --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.cpp @@ -0,0 +1,184 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Classic_Emu.h" + +#include "Multi_Buffer.h" +#include + +/* 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" + +Classic_Emu::Classic_Emu() +{ + buf = 0; + stereo_buffer = 0; + voice_types = 0; + + // 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; +} + +void Classic_Emu::set_equalizer_( equalizer_t const& eq ) +{ + Music_Emu::set_equalizer_( eq ); + update_eq( eq.treble ); + if ( buf ) + buf->bass_freq( equalizer().bass ); +} + +blargg_err_t Classic_Emu::set_sample_rate_( long 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, 0, 0, 0 ); + } + else + { + Multi_Buffer::channel_t ch = buf->channel( i, (voice_types ? voice_types [i] : 0) ); + 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( long rate ) +{ + clock_rate_ = rate; + buf->clock_rate( rate ); +} + +blargg_err_t Classic_Emu::setup_buffer( long rate ) +{ + change_clock_rate( rate ); + RETURN_ERR( buf->set_channel_count( voice_count() ) ); + set_equalizer( equalizer() ); + buf_changed_count = buf->channels_changed_count(); + return 0; +} + +blargg_err_t Classic_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + buf->clear(); + return 0; +} + +blargg_err_t Classic_Emu::play_( long count, sample_t* out ) +{ + long remain = count; + while ( remain ) + { + 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(); + } + int msec = buf->length(); + blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; + RETURN_ERR( run_clocks( clocks_emulated, msec ) ); + assert( clocks_emulated ); + buf->end_frame( clocks_emulated ); + } + } + return 0; +} + +// Rom_Data + +blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in, + int header_size, void* header_out, int fill, long pad_size ) +{ + long file_offset = pad_size - header_size; + + rom_addr = 0; + mask = 0; + size_ = 0; + rom.clear(); + + file_size_ = in.remain(); + if ( file_size_ <= header_size ) // <= because there must be data after header + return gme_wrong_file_type; + blargg_err_t err = rom.resize( file_offset + file_size_ + pad_size ); + if ( !err ) + err = in.read( rom.begin() + file_offset, file_size_ ); + if ( err ) + { + rom.clear(); + return err; + } + + file_size_ -= header_size; + memcpy( header_out, &rom [file_offset], header_size ); + + memset( rom.begin() , fill, pad_size ); + memset( rom.end() - pad_size, fill, pad_size ); + + return 0; +} + +void Rom_Data_::set_addr_( long addr, int unit ) +{ + rom_addr = addr - unit - pad_extra; + + long rounded = (addr + file_size_ + unit - 1) / unit * unit; + if ( rounded <= 0 ) + { + rounded = 0; + } + else + { + int shift = 0; + unsigned long max_addr = (unsigned long) (rounded - 1); + while ( max_addr >> shift ) + shift++; + mask = (1L << shift) - 1; + } + + if ( addr < 0 ) + addr = 0; + size_ = rounded; + if ( rom.resize( rounded - rom_addr + pad_extra ) ) { } // OK if shrink fails + + if ( 0 ) + { + dprintf( "addr: %X\n", addr ); + dprintf( "file_size: %d\n", file_size_ ); + dprintf( "rounded: %d\n", rounded ); + dprintf( "mask: $%X\n", mask ); + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h new file mode 100644 index 00000000..8cd822ca --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Classic_Emu.h @@ -0,0 +1,127 @@ +// Common aspects of emulators which use Blip_Buffer for sound output + +// Game_Music_Emu 0.5.2 +#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 { +public: + Classic_Emu(); + ~Classic_Emu(); + void set_buffer( Multi_Buffer* ); +protected: + // Services + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + void set_voice_types( int const* t ) { voice_types = t; } + blargg_err_t setup_buffer( long clock_rate ); + long clock_rate() const { return clock_rate_; } + void change_clock_rate( long ); // experimental + + // Overridable + virtual void set_voice( int index, Blip_Buffer* center, + Blip_Buffer* left, Blip_Buffer* right ) = 0; + virtual void update_eq( blip_eq_t const& ) = 0; + virtual blargg_err_t start_track_( int track ) = 0; + virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) = 0; +protected: + blargg_err_t set_sample_rate_( long sample_rate ); + void mute_voices_( int ); + void set_equalizer_( equalizer_t const& ); + blargg_err_t play_( long, sample_t* ); +private: + Multi_Buffer* buf; + Multi_Buffer* stereo_buffer; // NULL if using custom buffer + long 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; +} + +// ROM data handler, used by several Classic_Emu derivitives. Loads file data +// with padding on both sides, allowing direct use in bank mapping. The main purpose +// is to allow all file data to be loaded with only one read() call (for efficiency). + +class Rom_Data_ { +public: + typedef unsigned char byte; +protected: + enum { pad_extra = 8 }; + blargg_vector rom; + long file_size_; + blargg_long rom_addr; + blargg_long mask; + blargg_long size_; // TODO: eliminate + + blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out, + int fill, long pad_size ); + void set_addr_( long addr, int unit ); +}; + +template +class Rom_Data : public Rom_Data_ { + enum { pad_size = unit + pad_extra }; +public: + // Load file data, using already-loaded header 'h' if not NULL. Copy header + // from loaded file data into *out and fill unmapped bytes with 'fill'. + blargg_err_t load( Data_Reader& in, int header_size, void* header_out, int fill ) + { + return load_rom_data_( in, header_size, header_out, fill, pad_size ); + } + + // Size of file data read in (excluding header) + long file_size() const { return file_size_; } + + // Pointer to beginning of file data + byte* begin() const { return rom.begin() + pad_size; } + + // Set address that file data should start at + void set_addr( long addr ) { set_addr_( addr, unit ); } + + // Free data + void clear() { rom.clear(); } + + // Size of data + start addr, rounded to a multiple of unit + long size() const { return size_; } + + // Pointer to unmapped page filled with same value + byte* unmapped() { return rom.begin(); } + + // Mask address to nearest power of two greater than size() + blargg_long mask_addr( blargg_long addr ) const + { + #ifdef check + check( addr <= mask ); + #endif + return addr & mask; + } + + // Pointer to page starting at addr. Returns unmapped() if outside data. + byte* at_addr( blargg_long addr ) + { + blargg_ulong offset = mask_addr( addr ) - rom_addr; + if ( offset > blargg_ulong (rom.size() - pad_size) ) + offset = 0; // unmapped + return &rom [offset]; + } +}; + +#ifndef GME_APU_HOOK + #define GME_APU_HOOK( emu, addr, data ) ((void) 0) +#endif + +#ifndef GME_FRAME_HOOK + #define GME_FRAME_HOOK( emu ) ((void) 0) +#else + #define GME_FRAME_HOOK_DEFINED 1 +#endif + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp new file mode 100644 index 00000000..5bbfbf55 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.cpp @@ -0,0 +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 diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h new file mode 100644 index 00000000..00b53b9e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Data_Reader.h @@ -0,0 +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 diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp new file mode 100644 index 00000000..8644517c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.cpp @@ -0,0 +1,131 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Dual_Resampler.h" + +#include +#include + +/* 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" + +unsigned const resampler_extra = 256; + +Dual_Resampler::Dual_Resampler() { } + +Dual_Resampler::~Dual_Resampler() { } + +blargg_err_t Dual_Resampler::reset( int pairs ) +{ + // expand allocations a bit + RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) ); + resize( pairs ); + resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2); + return resampler.buffer_size( resampler_size ); +} + +void Dual_Resampler::resize( int pairs ) +{ + int new_sample_buf_size = pairs * 2; + 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.ratio()) * 2 + 2; + clear(); + } +} + +void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) +{ + long pair_count = sample_buf_size >> 1; + blip_time_t blip_time = blip_buf.count_clocks( pair_count ); + int sample_count = oversamples_per_frame - resampler.written(); + + int new_count = play_frame( blip_time, sample_count, resampler.buffer() ); + assert( new_count < resampler_size ); + + blip_buf.end_frame( blip_time ); + assert( blip_buf.samples_avail() == pair_count ); + + resampler.write( new_count ); + + long count = resampler.read( sample_buf.begin(), sample_buf_size ); + assert( count == (long) sample_buf_size ); + + mix_samples( blip_buf, out ); + blip_buf.remove_samples( pair_count ); +} + +void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_buf ) +{ + // empty extra buffer + long remain = sample_buf_size - buf_pos; + if ( remain ) + { + if ( remain > count ) + remain = count; + count -= remain; + memcpy( out, &sample_buf [buf_pos], remain * sizeof *out ); + out += remain; + buf_pos += remain; + } + + // entire frames + while ( count >= (long) sample_buf_size ) + { + play_frame_( blip_buf, out ); + out += sample_buf_size; + count -= sample_buf_size; + } + + // extra + if ( count ) + { + play_frame_( blip_buf, sample_buf.begin() ); + buf_pos = count; + memcpy( out, sample_buf.begin(), count * sizeof *out ); + out += count; + } +} + +void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out ) +{ + Blip_Reader sn; + int bass = sn.begin( blip_buf ); + const dsample_t* in = sample_buf.begin(); + + for ( int n = sample_buf_size >> 1; n--; ) + { + int s = sn.read(); + blargg_long l = (blargg_long) in [0] * 2 + s; + if ( (BOOST::int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + sn.next( bass ); + blargg_long r = (blargg_long) in [1] * 2 + s; + if ( (BOOST::int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + in += 2; + out [0] = l; + out [1] = r; + out += 2; + } + + sn.end( blip_buf ); +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h new file mode 100644 index 00000000..61beb8a0 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Dual_Resampler.h @@ -0,0 +1,50 @@ +// Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. + +// Game_Music_Emu 0.5.2 +#ifndef DUAL_RESAMPLER_H +#define DUAL_RESAMPLER_H + +#include "Fir_Resampler.h" +#include "Blip_Buffer.h" + +class Dual_Resampler { +public: + Dual_Resampler(); + virtual ~Dual_Resampler(); + + typedef short dsample_t; + + double setup( double oversample, double rolloff, double gain ); + blargg_err_t reset( int max_pairs ); + void resize( int pairs_per_frame ); + void clear(); + + void dual_play( long count, dsample_t* out, Blip_Buffer& ); + +protected: + virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; +private: + + blargg_vector sample_buf; + int sample_buf_size; + int oversamples_per_frame; + int buf_pos; + int resampler_size; + + Fir_Resampler<12> resampler; + void mix_samples( Blip_Buffer&, dsample_t* ); + void play_frame_( Blip_Buffer&, dsample_t* ); +}; + +inline double Dual_Resampler::setup( double oversample, double rolloff, double gain ) +{ + return resampler.time_ratio( oversample, rolloff, gain * 0.5 ); +} + +inline void Dual_Resampler::clear() +{ + buf_pos = sample_buf_size; + resampler.clear(); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp new file mode 100644 index 00000000..730f8e94 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.cpp @@ -0,0 +1,529 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Effects_Buffer.h" + +#include + +/* 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" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +typedef blargg_long fixed_t; + +#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) +#define FMUL( x, y ) (((x) * (y)) >> 15) + +const unsigned echo_size = 4096; +const unsigned echo_mask = echo_size - 1; +BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2 + +const unsigned reverb_size = 8192 * 2; +const unsigned reverb_mask = reverb_size - 1; +BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2 + +Effects_Buffer::config_t::config_t() +{ + pan_1 = -0.15f; + pan_2 = 0.15f; + reverb_delay = 88.0f; + reverb_level = 0.12f; + echo_delay = 61.0f; + echo_level = 0.10f; + delay_variance = 18.0f; + effects_enabled = false; +} + +void Effects_Buffer::set_depth( double d ) +{ + float f = (float) d; + config_t c; + c.pan_1 = -0.6f * f; + c.pan_2 = 0.6f * f; + c.reverb_delay = 880 * 0.1f; + c.echo_delay = 610 * 0.1f; + if ( f > 0.5 ) + f = 0.5; // TODO: more linear reduction of extreme reverb/echo + c.reverb_level = 0.5f * f; + c.echo_level = 0.30f * f; + c.delay_variance = 180 * 0.1f; + c.effects_enabled = (d > 0.0f); + config( c ); +} + +Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 ) +{ + buf_count = center_only ? max_buf_count - 4 : max_buf_count; + + echo_pos = 0; + reverb_pos = 0; + + stereo_remain = 0; + effect_remain = 0; + effects_enabled = false; + set_depth( 0 ); +} + +Effects_Buffer::~Effects_Buffer() { } + +blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) +{ + if ( !echo_buf.size() ) + RETURN_ERR( echo_buf.resize( echo_size ) ); + + if ( !reverb_buf.size() ) + RETURN_ERR( reverb_buf.resize( reverb_size ) ); + + for ( int i = 0; i < buf_count; i++ ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + + config( config_ ); + clear(); + + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Effects_Buffer::clock_rate( long rate ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clock_rate( rate ); +} + +void Effects_Buffer::bass_freq( int freq ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].bass_freq( freq ); +} + +void Effects_Buffer::clear() +{ + stereo_remain = 0; + effect_remain = 0; + if ( echo_buf.size() ) + memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); + + if ( reverb_buf.size() ) + memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); + + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clear(); +} + +inline int pin_range( int n, int max, int min = 0 ) +{ + if ( n < min ) + return min; + if ( n > max ) + return max; + return n; +} + +void Effects_Buffer::config( const config_t& cfg ) +{ + channels_changed(); + + // clear echo and reverb buffers + if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf.size() ) + { + memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); + memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); + } + + config_ = cfg; + + if ( config_.effects_enabled ) + { + // convert to internal format + + chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); + chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0]; + + chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 ); + chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; + + chans.reverb_level = TO_FIXED( config_.reverb_level ); + chans.echo_level = TO_FIXED( config_.echo_level ); + + int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); + + int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate()); + chans.reverb_delay_l = pin_range( reverb_size - + (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); + chans.reverb_delay_r = pin_range( reverb_size + 1 - + (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); + + int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); + chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset), + echo_size - 1 ); + chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), + echo_size - 1 ); + + chan_types [0].center = &bufs [0]; + chan_types [0].left = &bufs [3]; + chan_types [0].right = &bufs [4]; + + chan_types [1].center = &bufs [1]; + chan_types [1].left = &bufs [3]; + chan_types [1].right = &bufs [4]; + + chan_types [2].center = &bufs [2]; + chan_types [2].left = &bufs [5]; + chan_types [2].right = &bufs [6]; + assert( 2 < chan_types_count ); + } + else + { + // set up outputs + for ( unsigned i = 0; i < chan_types_count; i++ ) + { + channel_t& c = chan_types [i]; + c.center = &bufs [0]; + c.left = &bufs [1]; + c.right = &bufs [2]; + } + } + + if ( buf_count < max_buf_count ) + { + for ( int i = 0; i < chan_types_count; i++ ) + { + channel_t& c = chan_types [i]; + c.left = c.center; + c.right = c.center; + } + } +} + +Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type ) +{ + int out = 2; + if ( !type ) + { + out = i % 5; + if ( out > 2 ) + out = 2; + } + else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) + { + out = type & 1; + } + return chan_types [out]; +} + +void Effects_Buffer::end_frame( blip_time_t clock_count ) +{ + int bufs_used = 0; + for ( int i = 0; i < buf_count; i++ ) + { + bufs_used |= bufs [i].clear_modified() << i; + bufs [i].end_frame( clock_count ); + } + + int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); + if ( (bufs_used & stereo_mask) && buf_count == max_buf_count ) + stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); + + if ( effects_enabled || config_.effects_enabled ) + effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); + + effects_enabled = config_.effects_enabled; +} + +long Effects_Buffer::samples_avail() const +{ + return bufs [0].samples_avail() * 2; +} + +long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) +{ + require( total_samples % 2 == 0 ); // count must be even + + long remain = bufs [0].samples_avail(); + if ( remain > (total_samples >> 1) ) + remain = (total_samples >> 1); + total_samples = remain; + while ( remain ) + { + int active_bufs = buf_count; + long count = remain; + + // optimizing mixing to skip any channels which had nothing added + if ( effect_remain ) + { + if ( count > effect_remain ) + count = effect_remain; + + if ( stereo_remain ) + { + mix_enhanced( out, count ); + } + else + { + mix_mono_enhanced( out, count ); + active_bufs = 3; + } + } + else if ( stereo_remain ) + { + mix_stereo( out, count ); + active_bufs = 3; + } + else + { + mix_mono( out, count ); + active_bufs = 1; + } + + out += count * 2; + remain -= count; + + stereo_remain -= count; + if ( stereo_remain < 0 ) + stereo_remain = 0; + + effect_remain -= count; + if ( effect_remain < 0 ) + effect_remain = 0; + + for ( int i = 0; i < buf_count; i++ ) + { + if ( i < active_bufs ) + bufs [i].remove_samples( count ); + else + bufs [i].remove_silence( count ); // keep time synchronized + } + } + + return total_samples * 2; +} + +void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( c, bufs [0] ); + + // unrolled loop + for ( blargg_long n = count >> 1; n; --n ) + { + blargg_long cs0 = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + + blargg_long cs1 = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + + if ( (BOOST::int16_t) cs0 != cs0 ) + cs0 = 0x7FFF - (cs0 >> 24); + ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); + + if ( (BOOST::int16_t) cs1 != cs1 ) + cs1 = 0x7FFF - (cs1 >> 24); + ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); + out += 4; + } + + if ( count & 1 ) + { + int s = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + out [0] = s; + out [1] = s; + if ( (BOOST::int16_t) s != s ) + { + s = 0x7FFF - (s >> 24); + out [0] = s; + out [1] = s; + } + } + + BLIP_READER_END( c, bufs [0] ); +} + +void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( c, bufs [0] ); + BLIP_READER_BEGIN( l, bufs [1] ); + BLIP_READER_BEGIN( r, bufs [2] ); + + while ( count-- ) + { + int cs = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + int left = cs + BLIP_READER_READ( l ); + int right = cs + BLIP_READER_READ( r ); + BLIP_READER_NEXT( l, bass ); + BLIP_READER_NEXT( r, bass ); + + if ( (BOOST::int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + out [0] = left; + out [1] = right; + + out += 2; + + if ( (BOOST::int16_t) right != right ) + out [-1] = 0x7FFF - (right >> 24); + } + + BLIP_READER_END( r, bufs [2] ); + BLIP_READER_END( l, bufs [1] ); + BLIP_READER_END( c, bufs [0] ); +} + +void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [2] ); + BLIP_READER_BEGIN( center, bufs [2] ); + BLIP_READER_BEGIN( sq1, bufs [0] ); + BLIP_READER_BEGIN( sq2, bufs [1] ); + + blip_sample_t* const reverb_buf = this->reverb_buf.begin(); + blip_sample_t* const echo_buf = this->echo_buf.begin(); + int echo_pos = this->echo_pos; + int reverb_pos = this->reverb_pos; + + while ( count-- ) + { + int sum1_s = BLIP_READER_READ( sq1 ); + int sum2_s = BLIP_READER_READ( sq2 ); + + BLIP_READER_NEXT( sq1, bass ); + BLIP_READER_NEXT( sq2, bass ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; + + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + + FMUL( sum2_s, chans.pan_2_levels [1] ) + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = BLIP_READER_READ( center ); + BLIP_READER_NEXT( center, bass ); + + int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + echo_buf [echo_pos] = sum3_s; + echo_pos = (echo_pos + 1) & echo_mask; + + if ( (BOOST::int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + out [0] = left; + out [1] = right; + + out += 2; + + if ( (BOOST::int16_t) right != right ) + out [-1] = 0x7FFF - (right >> 24); + } + this->reverb_pos = reverb_pos; + this->echo_pos = echo_pos; + + BLIP_READER_END( sq1, bufs [0] ); + BLIP_READER_END( sq2, bufs [1] ); + BLIP_READER_END( center, bufs [2] ); +} + +void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [2] ); + BLIP_READER_BEGIN( center, bufs [2] ); + BLIP_READER_BEGIN( l1, bufs [3] ); + BLIP_READER_BEGIN( r1, bufs [4] ); + BLIP_READER_BEGIN( l2, bufs [5] ); + BLIP_READER_BEGIN( r2, bufs [6] ); + BLIP_READER_BEGIN( sq1, bufs [0] ); + BLIP_READER_BEGIN( sq2, bufs [1] ); + + blip_sample_t* const reverb_buf = this->reverb_buf.begin(); + blip_sample_t* const echo_buf = this->echo_buf.begin(); + int echo_pos = this->echo_pos; + int reverb_pos = this->reverb_pos; + + while ( count-- ) + { + int sum1_s = BLIP_READER_READ( sq1 ); + int sum2_s = BLIP_READER_READ( sq2 ); + + BLIP_READER_NEXT( sq1, bass ); + BLIP_READER_NEXT( sq2, bass ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; + + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + + FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + BLIP_READER_NEXT( l1, bass ); + BLIP_READER_NEXT( r1, bass ); + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = BLIP_READER_READ( center ); + BLIP_READER_NEXT( center, bass ); + + int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + BLIP_READER_NEXT( l2, bass ); + BLIP_READER_NEXT( r2, bass ); + + echo_buf [echo_pos] = sum3_s; + echo_pos = (echo_pos + 1) & echo_mask; + + if ( (BOOST::int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + out [0] = left; + out [1] = right; + + out += 2; + + if ( (BOOST::int16_t) right != right ) + out [-1] = 0x7FFF - (right >> 24); + } + this->reverb_pos = reverb_pos; + this->echo_pos = echo_pos; + + BLIP_READER_END( l1, bufs [3] ); + BLIP_READER_END( r1, bufs [4] ); + BLIP_READER_END( l2, bufs [5] ); + BLIP_READER_END( r2, bufs [6] ); + BLIP_READER_END( sq1, bufs [0] ); + BLIP_READER_END( sq2, bufs [1] ); + BLIP_READER_END( center, bufs [2] ); +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h new file mode 100644 index 00000000..eb0aa67a --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Effects_Buffer.h @@ -0,0 +1,86 @@ +// Multi-channel effects buffer with panning, echo and reverb + +// Game_Music_Emu 0.5.2 +#ifndef EFFECTS_BUFFER_H +#define EFFECTS_BUFFER_H + +#include "Multi_Buffer.h" + +// Effects_Buffer uses several buffers and outputs stereo sample pairs. +class Effects_Buffer : public Multi_Buffer { +public: + // If center_only is true, only center buffers are created and + // less memory is used. + Effects_Buffer( bool center_only = false ); + + // Channel Effect Center Pan + // --------------------------------- + // 0,5 reverb pan_1 + // 1,6 reverb pan_2 + // 2,7 echo - + // 3 echo - + // 4 echo - + + // Channel configuration + struct config_t { + double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right + double pan_2; + double echo_delay; // msec + double echo_level; // 0.0 to 1.0 + double reverb_delay; // msec + double delay_variance; // difference between left/right delays (msec) + double reverb_level; // 0.0 to 1.0 + bool effects_enabled; // if false, use optimized simple mixer + config_t(); + }; + + // Set configuration of buffer + virtual void config( const config_t& ); + void set_depth( double ); + +public: + ~Effects_Buffer(); + blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int, int ); + void end_frame( blip_time_t ); + long read_samples( blip_sample_t*, long ); + long samples_avail() const; +private: + typedef long fixed_t; + + enum { max_buf_count = 7 }; + Blip_Buffer bufs [max_buf_count]; + enum { chan_types_count = 3 }; + channel_t chan_types [3]; + config_t config_; + long stereo_remain; + long effect_remain; + int buf_count; + bool effects_enabled; + + blargg_vector reverb_buf; + blargg_vector echo_buf; + int reverb_pos; + int echo_pos; + + struct { + fixed_t pan_1_levels [2]; + fixed_t pan_2_levels [2]; + int echo_delay_l; + int echo_delay_r; + fixed_t echo_level; + int reverb_delay_l; + int reverb_delay_r; + fixed_t reverb_level; + } chans; + + void mix_mono( blip_sample_t*, blargg_long ); + void mix_stereo( blip_sample_t*, blargg_long ); + void mix_enhanced( blip_sample_t*, blargg_long ); + void mix_mono_enhanced( blip_sample_t*, blargg_long ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp new file mode 100644 index 00000000..4e0a4631 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.cpp @@ -0,0 +1,199 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Fir_Resampler.h" + +#include +#include +#include +#include + +/* Copyright (C) 2004-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" + +#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 ), + write_offset( width * stereo - stereo ), + impulses( impulses_ ) +{ + write_pos = 0; + res = 1; + imp_phase = 0; + skip_bits = 0; + step = stereo; + ratio_ = 1.0; +} + +Fir_Resampler_::~Fir_Resampler_() { } + +void Fir_Resampler_::clear() +{ + imp_phase = 0; + if ( buf.size() ) + { + write_pos = &buf [write_offset]; + memset( buf.begin(), 0, write_offset * sizeof buf [0] ); + } +} + +blargg_err_t Fir_Resampler_::buffer_size( int new_size ) +{ + RETURN_ERR( buf.resize( new_size + write_offset ) ); + clear(); + return 0; +} + +double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) +{ + ratio_ = new_factor; + + double fstep = 0.0; + { + double least_error = 2; + double pos = 0; + res = -1; + for ( int r = 1; r <= max_res; r++ ) + { + pos += ratio_; + double nearest = floor( pos + 0.5 ); + double error = fabs( pos - nearest ); + if ( error < least_error ) + { + res = r; + fstep = nearest / res; + least_error = error; + } + } + } + + skip_bits = 0; + + step = stereo * (int) floor( fstep ); + + ratio_ = fstep; + fstep = fmod( fstep, 1.0 ); + + double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + input_per_cycle = 0; + for ( int i = 0; i < res; i++ ) + { + gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, + double (0x7FFF * gain * filter), + (int) width_, impulses + i * width_ ); + + pos += fstep; + input_per_cycle += step; + if ( pos >= 0.9999999 ) + { + pos -= 1.0; + skip_bits |= 1 << i; + input_per_cycle++; + } + } + + clear(); + + return ratio_; +} + +int Fir_Resampler_::input_needed( blargg_long output_count ) const +{ + blargg_long input_count = 0; + + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( (output_count -= 2) > 0 ) + { + input_count += step + (skip & 1) * stereo; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count -= 2; + } + + long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); + if ( input_extra < 0 ) + input_extra = 0; + return input_extra; +} + +int Fir_Resampler_::avail_( blargg_long input_count ) const +{ + int cycle_count = input_count / input_per_cycle; + int output_count = cycle_count * res * stereo; + input_count -= cycle_count * input_per_cycle; + + blargg_ulong skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( input_count >= 0 ) + { + input_count -= step + (skip & 1) * stereo; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count += 2; + } + return output_count; +} + +int Fir_Resampler_::skip_input( long count ) +{ + int remain = write_pos - buf.begin(); + int max_count = remain - width_ * stereo; + if ( count > max_count ) + count = max_count; + + remain -= count; + write_pos = &buf [remain]; + memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); + + return count; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h new file mode 100644 index 00000000..339dfce3 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Fir_Resampler.h @@ -0,0 +1,171 @@ +// Finite impulse response (FIR) resampler with adjustable FIR size + +// Game_Music_Emu 0.5.2 +#ifndef FIR_RESAMPLER_H +#define FIR_RESAMPLER_H + +#include "blargg_common.h" +#include + +class Fir_Resampler_ { +public: + + // Use Fir_Resampler (below) + + // Set input/output resampling ratio and optionally low-pass rolloff and gain. + // Returns actual ratio used (rounded to internal precision). + double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); + + // Current input/output ratio + double ratio() const { return ratio_; } + +// Input + + typedef short sample_t; + + // Resize and clear input buffer + blargg_err_t buffer_size( int ); + + // Clear input buffer. At least two output samples will be available after + // two input samples are written. + void clear(); + + // Number of input samples that can be written + int max_write() const { return buf.end() - write_pos; } + + // Pointer to place to write input samples + sample_t* buffer() { return write_pos; } + + // Notify resampler that 'count' input samples have been written + void write( long count ); + + // Number of input samples in buffer + int written() const { return write_pos - &buf [write_offset]; } + + // Skip 'count' input samples. Returns number of samples actually skipped. + int skip_input( long count ); + +// Output + + // Number of extra input samples needed until 'count' output samples are available + int input_needed( blargg_long count ) const; + + // Number of output samples available + int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } + +public: + ~Fir_Resampler_(); +protected: + enum { stereo = 2 }; + enum { max_res = 32 }; + blargg_vector buf; + sample_t* write_pos; + int res; + int imp_phase; + int const width_; + int const write_offset; + blargg_ulong skip_bits; + int step; + int input_per_cycle; + double ratio_; + sample_t* impulses; + + Fir_Resampler_( int width, sample_t* ); + int avail_( blargg_long input_count ) const; +}; + +// Width is number of points in FIR. Must be even and 4 or more. More points give +// better quality and rolloff effectiveness, and take longer to calculate. +template +class Fir_Resampler : public Fir_Resampler_ { + BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 ); + short impulses [max_res] [width]; +public: + Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } + + // Read at most 'count' samples. Returns number of samples actually read. + typedef short sample_t; + int read( sample_t* out, blargg_long count ); +}; + +// End of public interface + +inline void Fir_Resampler_::write( long count ) +{ + write_pos += count; + assert( write_pos <= buf.end() ); +} + +template +int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) +{ + sample_t* out = out_begin; + const sample_t* in = buf.begin(); + sample_t* end_pos = write_pos; + blargg_ulong skip = skip_bits >> imp_phase; + sample_t const* imp = impulses [imp_phase]; + int remain = res - imp_phase; + int const step = this->step; + + count >>= 1; + + if ( end_pos - in >= width * stereo ) + { + end_pos -= width * stereo; + do + { + count--; + + // accumulate in extended precision + blargg_long l = 0; + blargg_long r = 0; + + const sample_t* i = in; + if ( count < 0 ) + break; + + for ( int n = width / 2; n; --n ) + { + int pt0 = imp [0]; + l += pt0 * i [0]; + r += pt0 * i [1]; + int pt1 = imp [1]; + imp += 2; + l += pt1 * i [2]; + r += pt1 * i [3]; + i += 4; + } + + remain--; + + l >>= 15; + r >>= 15; + + in += (skip * stereo) & stereo; + skip >>= 1; + in += step; + + if ( !remain ) + { + imp = impulses [0]; + skip = skip_bits; + remain = res; + } + + out [0] = (sample_t) l; + out [1] = (sample_t) r; + out += 2; + } + while ( in <= end_pos ); + } + + imp_phase = res - remain; + + int left = write_pos - in; + write_pos = &buf [left]; + memmove( buf.begin(), in, left * sizeof *in ); + + return out - out_begin; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp new file mode 100644 index 00000000..932ebb83 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.cpp @@ -0,0 +1,306 @@ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +#include + +/* 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" + +unsigned const vol_reg = 0xFF24; +unsigned const status_reg = 0xFF26; + +Gb_Apu::Gb_Apu() +{ + square1.synth = &square_synth; + square2.synth = &square_synth; + wave.synth = &other_synth; + noise.synth = &other_synth; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = &wave; + oscs [3] = &noise; + + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + osc.regs = ®s [i * 5]; + osc.output = 0; + osc.outputs [0] = 0; + osc.outputs [1] = 0; + osc.outputs [2] = 0; + osc.outputs [3] = 0; + } + + set_tempo( 1.0 ); + volume( 1.0 ); + reset(); +} + +void Gb_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth.treble_eq( eq ); + other_synth.treble_eq( eq ); +} + +void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + require( (unsigned) index < osc_count ); + require( (center && left && right) || (!center && !left && !right) ); + Gb_Osc& osc = *oscs [index]; + osc.outputs [1] = right; + osc.outputs [2] = left; + osc.outputs [3] = center; + osc.output = osc.outputs [osc.output_select]; +} + +void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, center, left, right ); +} + +void Gb_Apu::update_volume() +{ + // TODO: doesn't handle differing left/right global volume (support would + // require modification to all oscillator code) + int data = regs [vol_reg - start_addr]; + double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; + square_synth.volume( vol ); + other_synth.volume( vol ); +} + +static unsigned char const powerup_regs [0x20] = { + 0x80,0x3F,0x00,0xFF,0xBF, // square 1 + 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 + 0x7F,0xFF,0x9F,0xFF,0xBF, // wave + 0xFF,0xFF,0x00,0x00,0xBF, // noise + 0x00, // left/right enables + 0x77, // master volume + 0x80, // power + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 256; // 256 Hz + if ( t != 1.0 ) + frame_period = blip_time_t (frame_period / t); +} + +void Gb_Apu::reset() +{ + next_frame_time = 0; + last_time = 0; + frame_count = 0; + + square1.reset(); + square2.reset(); + wave.reset(); + noise.reset(); + noise.bits = 1; + wave.wave_pos = 0; + + // avoid click at beginning + regs [vol_reg - start_addr] = 0x77; + update_volume(); + + regs [status_reg - start_addr] = 0x01; // force power + write_register( 0, status_reg, 0x00 ); + + static unsigned char const initial_wave [] = { + 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table + 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA + }; + memcpy( wave.wave, initial_wave, sizeof wave.wave ); +} + +void Gb_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); // end_time must not be before previous time + if ( end_time == last_time ) + return; + + while ( true ) + { + blip_time_t time = next_frame_time; + if ( time > end_time ) + time = end_time; + + // run oscillators + for ( int i = 0; i < osc_count; ++i ) + { + Gb_Osc& osc = *oscs [i]; + if ( osc.output ) + { + osc.output->set_modified(); // TODO: misses optimization opportunities? + int playing = false; + if ( osc.enabled && osc.volume && + (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) + playing = -1; + switch ( i ) + { + case 0: square1.run( last_time, time, playing ); break; + case 1: square2.run( last_time, time, playing ); break; + case 2: wave .run( last_time, time, playing ); break; + case 3: noise .run( last_time, time, playing ); break; + } + } + } + last_time = time; + + if ( time == end_time ) + break; + + next_frame_time += frame_period; + + // 256 Hz actions + square1.clock_length(); + square2.clock_length(); + wave.clock_length(); + noise.clock_length(); + + frame_count = (frame_count + 1) & 3; + if ( frame_count == 0 ) + { + // 64 Hz actions + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } + + if ( frame_count & 1 ) + square1.clock_sweep(); // 128 Hz action + } +} + +void Gb_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + assert( next_frame_time >= end_time ); + next_frame_time -= end_time; + + assert( last_time >= end_time ); + last_time -= end_time; +} + +void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) +{ + require( (unsigned) data < 0x100 ); + + int reg = addr - start_addr; + if ( (unsigned) reg >= register_count ) + return; + + run_until( time ); + + int old_reg = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) + { + write_osc( reg / 5, reg, data ); + } + else if ( addr == vol_reg && data != old_reg ) // global volume + { + // return all oscs to 0 + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + int amp = osc.last_amp; + osc.last_amp = 0; + if ( amp && osc.enabled && osc.output ) + other_synth.offset( time, -amp, osc.output ); + } + + if ( wave.outputs [3] ) + other_synth.offset( time, 30, wave.outputs [3] ); + + update_volume(); + + if ( wave.outputs [3] ) + other_synth.offset( time, -30, wave.outputs [3] ); + + // oscs will update with new amplitude when next run + } + else if ( addr == 0xFF25 || addr == status_reg ) + { + int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; + int flags = regs [0xFF25 - start_addr] & mask; + + // left/right assignments + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + osc.enabled &= mask; + int bits = flags >> i; + Blip_Buffer* old_output = osc.output; + osc.output_select = (bits >> 3 & 2) | (bits & 1); + osc.output = osc.outputs [osc.output_select]; + if ( osc.output != old_output ) + { + int amp = osc.last_amp; + osc.last_amp = 0; + if ( amp && old_output ) + other_synth.offset( time, -amp, old_output ); + } + } + + if ( addr == status_reg && data != old_reg ) + { + if ( !(data & 0x80) ) + { + for ( unsigned i = 0; i < sizeof powerup_regs; i++ ) + { + if ( i != status_reg - start_addr ) + write_register( time, i + start_addr, powerup_regs [i] ); + } + } + else + { + //dprintf( "APU powered on\n" ); + } + } + } + else if ( addr >= 0xFF30 ) + { + int index = (addr & 0x0F) * 2; + wave.wave [index] = data >> 4; + wave.wave [index + 1] = data & 0x0F; + } +} + +int Gb_Apu::read_register( blip_time_t time, unsigned addr ) +{ + run_until( time ); + + int index = addr - start_addr; + require( (unsigned) index < register_count ); + int data = regs [index]; + + if ( addr == status_reg ) + { + data = (data & 0x80) | 0x70; + for ( int i = 0; i < osc_count; i++ ) + { + const Gb_Osc& osc = *oscs [i]; + if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) + data |= 1 << i; + } + } + + return data; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h new file mode 100644 index 00000000..e74ebc55 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Apu.h @@ -0,0 +1,90 @@ +// Nintendo Game Boy PAPU sound chip emulator + +// Gb_Snd_Emu 0.1.5 +#ifndef GB_APU_H +#define GB_APU_H + +#include "Gb_Oscs.h" + +class Gb_Apu { +public: + + // Set overall volume of all oscillators, where 1.0 is full volume + void volume( double ); + + // Set treble equalization + void treble_eq( const blip_eq_t& ); + + // Outputs can be assigned to a single buffer for mono output, or to three + // buffers for stereo output (using Stereo_Buffer to do the mixing). + + // Assign all oscillator outputs to specified buffer(s). If buffer + // is NULL, silences all oscillators. + void output( Blip_Buffer* mono ); + void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, + // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, + // silences oscillator. + enum { osc_count = 4 }; + void osc_output( int index, Blip_Buffer* mono ); + void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Reset oscillators and internal state + void reset(); + + // Reads and writes at addr must satisfy start_addr <= addr <= end_addr + enum { start_addr = 0xFF10 }; + enum { end_addr = 0xFF3F }; + enum { register_count = end_addr - start_addr + 1 }; + + // Write 'data' to address at specified time + void write_register( blip_time_t, unsigned addr, int data ); + + // Read from address at specified time + int read_register( blip_time_t, unsigned addr ); + + // Run all oscillators up to specified time, end current time frame, then + // start a new frame at time 0. + void end_frame( blip_time_t ); + + void set_tempo( double ); + +public: + Gb_Apu(); +private: + // noncopyable + Gb_Apu( const Gb_Apu& ); + Gb_Apu& operator = ( const Gb_Apu& ); + + Gb_Osc* oscs [osc_count]; + blip_time_t next_frame_time; + blip_time_t last_time; + blip_time_t frame_period; + double volume_unit; + int frame_count; + + Gb_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + BOOST::uint8_t regs [register_count]; + Gb_Square::Synth square_synth; // used by squares + Gb_Wave::Synth other_synth; // used by wave and noise + + void update_volume(); + void run_until( blip_time_t ); + void write_osc( int index, int reg, int data ); +}; + +inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } + +inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } + +inline void Gb_Apu::volume( double vol ) +{ + volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; + update_volume(); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp new file mode 100644 index 00000000..b1f22bd9 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.cpp @@ -0,0 +1,1056 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Gb_Cpu.h" + +#include + +//#include "gb_cpu_log.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 "gb_cpu_io.h" + +#include "blargg_source.h" + +// Common instructions: +// +// 365880 FA LD A,IND16 +// 355863 20 JR NZ +// 313655 21 LD HL,IMM +// 274580 28 JR Z +// 252878 FE CMP IMM +// 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) + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +inline void Gb_Cpu::set_code_page( int i, uint8_t* p ) +{ + state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size ); +} + +void Gb_Cpu::reset( void* unmapped ) +{ + check( state == &state_ ); + state = &state_; + + state_.remain = 0; + + for ( int i = 0; i < page_count + 1; i++ ) + set_code_page( i, (uint8_t*) unmapped ); + + memset( &r, 0, sizeof r ); + //interrupts_enabled = false; + + blargg_verify_byte_order(); +} + +void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + + unsigned first_page = start / page_size; + for ( unsigned i = size / page_size; i--; ) + set_code_page( first_page + i, (uint8_t*) data + i * page_size ); +} + +#define READ( addr ) CPU_READ( this, (addr), s.remain ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), s.remain );} +#define READ_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out ) +#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) + +unsigned const z_flag = 0x80; +unsigned const n_flag = 0x40; +unsigned const h_flag = 0x20; +unsigned const c_flag = 0x10; + +bool Gb_Cpu::run( blargg_long cycle_count ) +{ + state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; + state_t s; + this->state = &s; + memcpy( &s, &this->state_, sizeof s ); + + typedef BOOST::uint16_t uint16_t; + +#if BLARGG_BIG_ENDIAN + #define R8( n ) (r8_ [n]) +#elif BLARGG_LITTLE_ENDIAN + #define R8( n ) (r8_ [(n) ^ 1]) +#else + #error "Byte order of CPU must be known" +#endif + + union { + core_regs_t rg; // individual registers + + struct { + BOOST::uint16_t bc, de, hl, unused; // pairs + } rp; + + uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) + BOOST::uint16_t r16 [4]; // indexed pairs + }; + BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); + + rg = r; + unsigned pc = r.pc; + unsigned sp = r.sp; + unsigned flags = r.flags; + +loop: + + check( (unsigned long) pc < 0x10000 ); + check( (unsigned long) sp < 0x10000 ); + check( (flags & ~0xF0) == 0 ); + + uint8_t const* instr = s.code_map [pc >> page_shift]; + unsigned op; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + op = instr [pc]; + pc++; + instr += pc; + #else + instr += PAGE_OFFSET( pc ); + op = *instr++; + pc++; + #endif + +#define GET_ADDR() GET_LE16( instr ) + + if ( !--s.remain ) + goto stop; + + unsigned data; + data = *instr; + + #ifdef GB_CPU_LOG_H + gb_cpu_log( "new", pc - 1, op, data, instr [1] ); + #endif + + switch ( op ) + { + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + pc++;\ + int offset = (BOOST::int8_t) data;\ + if ( !(cond) ) goto loop;\ + pc = uint16_t (pc + offset);\ + goto loop;\ +} + +// Most Common + + case 0x20: // JR NZ + BRANCH( !(flags & z_flag) ) + + case 0x21: // LD HL,IMM (common) + rp.hl = GET_ADDR(); + pc += 2; + goto loop; + + case 0x28: // JR Z + BRANCH( flags & z_flag ) + + { + unsigned temp; + case 0xF0: // LD A,(0xFF00+imm) + temp = data | 0xFF00; + pc++; + goto ld_a_ind_comm; + + case 0xF2: // LD A,(0xFF00+C) + temp = rg.c | 0xFF00; + goto ld_a_ind_comm; + + 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; + } + + case 0xBE: // CMP (HL) + data = READ( rp.hl ); + goto cmp_comm; + + case 0xB8: // CMP B + case 0xB9: // CMP C + case 0xBA: // CMP D + case 0xBB: // CMP E + case 0xBC: // CMP H + case 0xBD: // CMP L + data = R8( op & 7 ); + goto cmp_comm; + + case 0xFE: // CMP IMM + pc++; + cmp_comm: + op = rg.a; + data = op - data; + sub_set_flags: + flags = ((op & 15) - (data & 15)) & h_flag; + flags |= (data >> 4) & c_flag; + flags |= n_flag; + if ( data & 0xFF ) + goto loop; + flags |= z_flag; + 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) + unsigned addr = rp.hl; + READ_FAST( addr, R8( (op >> 3) & 7 ) ); + goto loop; + } + + case 0xC4: // CNZ (next-most-common) + pc += 2; + if ( flags & z_flag ) + goto loop; + call: + pc -= 2; + case 0xCD: // CALL (most-common) + data = pc + 2; + pc = GET_ADDR(); + push: + sp = (sp - 1) & 0xFFFF; + WRITE( sp, data >> 8 ); + sp = (sp - 1) & 0xFFFF; + WRITE( sp, data & 0xFF ); + goto loop; + + case 0xC8: // RNZ (next-most-common) + if ( !(flags & z_flag) ) + goto loop; + case 0xC9: // RET (most common) + ret: + pc = READ( sp ); + pc += 0x100 * READ( sp + 1 ); + sp = (sp + 2) & 0xFFFF; + 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: + pc++; + // now data is the opcode + switch ( data ) { + + { + int temp; + case 0x46: // BIT b,(HL) + case 0x4E: + case 0x56: + case 0x5E: + case 0x66: + case 0x6E: + case 0x76: + case 0x7E: + { + unsigned addr = rp.hl; + READ_FAST( addr, temp ); + 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: + temp = R8( data & 7 ); + bit_comm: + int bit = (~data >> 3) & 7; + flags &= ~n_flag; + flags |= h_flag | z_flag; + flags ^= (temp << bit) & z_flag; + goto loop; + } + + case 0x86: // RES b,(HL) + case 0x8E: + case 0x96: + case 0x9E: + case 0xA6: + case 0xAE: + case 0xB6: + case 0xBE: + case 0xC6: // SET b,(HL) + case 0xCE: + case 0xD6: + case 0xDE: + case 0xE6: + case 0xEE: + case 0xF6: + case 0xFE: { + int temp = READ( rp.hl ); + int bit = 1 << ((data >> 3) & 7); + temp &= ~bit; + if ( !(data & 0x40) ) + bit = 0; + WRITE( rp.hl, temp | bit ); + 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; + + { + int temp; + case 0x36: // SWAP (HL) + temp = READ( 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 + temp = R8( data & 7 ); + swap_comm: + op = (temp >> 4) | (temp << 4); + flags = 0; + goto shift_comm; + } + +// Shift/Rotate + + case 0x06: // RLC (HL) + case 0x16: // RL (HL) + case 0x26: // SLA (HL) + op = READ( rp.hl ); + goto rl_comm; + + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A + op = R8( data & 7 ); + goto rl_comm; + + case 0x3E: // SRL (HL) + data += 0x10; // bump up to 0x4n to avoid preserving sign bit + case 0x1E: // RR (HL) + case 0x0E: // RRC (HL) + case 0x2E: // SRA (HL) + op = READ( rp.hl ); + goto rr_comm; + + case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A + data += 0x10; // bump up to 0x4n + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A + case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A + case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A + op = R8( data & 7 ); + goto rr_comm; + + } // CB op + assert( false ); // unhandled CB op + + case 0x07: // RLCA + case 0x17: // RLA + data = op; + op = rg.a; + rl_comm: + op <<= 1; + op |= ((data & flags) >> 4) & 1; // RL and carry is set + flags = (op >> 4) & c_flag; // C = bit shifted out + if ( data < 0x10 ) // RLC + op |= op >> 8; + // SLA doesn't fill lower bit + goto shift_comm; + + case 0x0F: // RRCA + case 0x1F: // RRA + data = op; + op = rg.a; + rr_comm: + op |= (data & flags) << 4; // RR and carry is set + flags = (op << 4) & c_flag; // C = bit shifted out + if ( data < 0x10 ) // RRC + op |= op << 8; + op >>= 1; + if ( data & 0x20 ) // SRA propagates sign bit + op |= (op << 1) & 0x80; + shift_comm: + data &= 7; + if ( !(op & 0xFF) ) + flags |= z_flag; + if ( data == 6 ) + goto write_hl_op_ff; + R8( data ) = op; + 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( rp.hl, op & 0xFF ); + 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( data, sp&0xFF ); + data++; + WRITE( 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 [op >> 4] = GET_ADDR(); + pc += 2; + goto loop; + + { + unsigned temp; + case 0xE0: // LD (0xFF00+imm),A + temp = data | 0xFF00; + pc++; + goto write_data_rg_a; + + case 0xE2: // LD (0xFF00+C),A + temp = rg.c | 0xFF00; + goto write_data_rg_a; + + 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( 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( 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 [op >> 4]++; + goto loop; + + case 0x33: // INC SP + sp = (sp + 1) & 0xFFFF; + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + r16 [op >> 4]--; + goto loop; + + case 0x3B: // DEC SP + sp = (sp - 1) & 0xFFFF; + goto loop; + + case 0x34: // INC (HL) + op = rp.hl; + data = READ( op ); + data++; + WRITE( op, data & 0xFF ); + 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; + R8( op ) = data = R8( op ) + 1; + inc_comm: + flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); + goto loop; + + case 0x35: // DEC (HL) + op = rp.hl; + data = READ( op ); + data--; + WRITE( op, data & 0xFF ); + 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: + flags = (flags & c_flag) | n_flag | (((data & 15) + 0x31) & h_flag); + if ( data & 0xFF ) + goto loop; + flags |= z_flag; + goto loop; + +// Add 16-bit + + { + blargg_ulong temp; // need more than 16 bits for carry + unsigned prev; + + case 0xF8: // LD HL,SP+imm + temp = BOOST::int8_t (data); // sign-extend to 16 bits + pc++; + flags = 0; + temp += sp; + prev = sp; + goto add_16_hl; + + case 0xE8: // ADD SP,IMM + temp = BOOST::int8_t (data); // sign-extend to 16 bits + pc++; + flags = 0; + temp += sp; + prev = sp; + sp = temp & 0xFFFF; + goto add_16_comm; + + case 0x39: // ADD HL,SP + temp = sp; + goto add_hl_comm; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + temp = r16 [op >> 4]; + add_hl_comm: + prev = rp.hl; + temp += prev; + flags &= z_flag; + add_16_hl: + rp.hl = temp; + add_16_comm: + flags |= (temp >> 12) & c_flag; + flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; + goto loop; + } + + case 0x86: // ADD (HL) + data = READ( 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: + flags = rg.a; + data += flags; + flags = ((data & 15) - (flags & 15)) & h_flag; + flags |= (data >> 4) & c_flag; + rg.a = data; + if ( data & 0xFF ) + goto loop; + flags |= z_flag; + goto loop; + +// Add/Subtract + + case 0x8E: // ADC (HL) + data = READ( 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: + data += (flags >> 4) & 1; + data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? + goto add_comm; + + case 0x96: // SUB (HL) + data = READ( 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: + op = rg.a; + data = op - data; + rg.a = data; + goto sub_set_flags; + + case 0x9E: // SBC (HL) + data = READ( 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: + data += (flags >> 4) & 1; + data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? + goto sub_comm; + +// 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( rp.hl ); + pc--; + case 0xE6: // AND IMM + pc++; + and_comm: + rg.a &= data; + case 0xA7: // AND A + flags = h_flag | (((rg.a - 1) >> 1) & z_flag); + 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( rp.hl ); + pc--; + case 0xF6: // OR IMM + pc++; + or_comm: + rg.a |= data; + case 0xB7: // OR A + flags = ((rg.a - 1) >> 1) & z_flag; + 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( rp.hl ); + pc--; + case 0xEE: // XOR IMM + pc++; + xor_comm: + data ^= rg.a; + rg.a = data; + data--; + flags = (data >> 1) & z_flag; + goto loop; + + case 0xAF: // XOR A + rg.a = 0; + flags = z_flag; + goto loop; + +// Stack + + case 0xF1: // POP FA + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL (common) + data = READ( sp ); + r16 [(op >> 4) & 3] = data + 0x100 * READ( sp + 1 ); + sp = (sp + 2) & 0xFFFF; + if ( op != 0xF1 ) + goto loop; + flags = rg.flags & 0xF0; + 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 FA + data = (flags << 8) | rg.a; + goto push; + +// Flow control + + case 0xFF: + if ( pc == idle_addr + 1 ) + goto stop; + case 0xC7: case 0xCF: case 0xD7: case 0xDF: // RST + case 0xE7: case 0xEF: case 0xF7: + data = pc; + pc = (op & 0x38) + rst_base; + goto push; + + case 0xCC: // CZ + pc += 2; + if ( flags & z_flag ) + goto call; + goto loop; + + case 0xD4: // CNC + pc += 2; + if ( !(flags & c_flag) ) + goto call; + goto loop; + + case 0xDC: // CC + pc += 2; + if ( flags & c_flag ) + goto call; + goto loop; + + case 0xD9: // RETI + //interrupts_enabled = 1; + goto ret; + + case 0xC0: // RZ + if ( !(flags & z_flag) ) + goto ret; + goto loop; + + case 0xD0: // RNC + if ( !(flags & c_flag) ) + goto ret; + goto loop; + + case 0xD8: // RC + if ( flags & c_flag ) + goto ret; + goto loop; + + case 0x18: // JR + BRANCH( true ) + + case 0x30: // JR NC + BRANCH( !(flags & c_flag) ) + + case 0x38: // JR C + BRANCH( flags & c_flag ) + + case 0xE9: // JP_HL + pc = rp.hl; + goto loop; + + case 0xC3: // JP (next-most-common) + pc = GET_ADDR(); + goto loop; + + case 0xC2: // JP NZ + pc += 2; + if ( !(flags & z_flag) ) + goto jp_taken; + goto loop; + + case 0xCA: // JP Z (most common) + pc += 2; + if ( !(flags & z_flag) ) + goto loop; + jp_taken: + pc -= 2; + pc = GET_ADDR(); + goto loop; + + case 0xD2: // JP NC + pc += 2; + if ( !(flags & c_flag) ) + goto jp_taken; + goto loop; + + case 0xDA: // JP C + pc += 2; + if ( flags & c_flag ) + goto jp_taken; + goto loop; + +// Flags + + case 0x2F: // CPL + rg.a = ~rg.a; + flags |= n_flag | h_flag; + goto loop; + + case 0x3F: // CCF + flags = (flags ^ c_flag) & ~(n_flag | h_flag); + goto loop; + + case 0x37: // SCF + flags = (flags | c_flag) & ~(n_flag | h_flag); + goto loop; + + case 0xF3: // DI + //interrupts_enabled = 0; + goto loop; + + case 0xFB: // EI + //interrupts_enabled = 1; + goto loop; + +// Special + + case 0xDD: case 0xD3: case 0xDB: case 0xE3: case 0xE4: // ? + case 0xEB: case 0xEC: case 0xF4: case 0xFD: case 0xFC: + case 0x10: // STOP + case 0x27: // DAA (I'll have to implement this eventually...) + case 0xBF: + case 0xED: // Z80 prefix + case 0x76: // HALT + s.remain++; + goto stop; + } + + // If this fails then the case above is missing an opcode + assert( false ); + +stop: + pc--; + + // copy state back + STATIC_CAST(core_regs_t&,r) = rg; + r.pc = pc; + r.sp = sp; + r.flags = flags; + + this->state = &state_; + memcpy( &this->state_, &s, sizeof this->state_ ); + + return s.remain > 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h new file mode 100644 index 00000000..953fbaf5 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Cpu.h @@ -0,0 +1,93 @@ +// Nintendo Game Boy CPU emulator +// Treats every instruction as taking 4 cycles + +// Game_Music_Emu 0.5.2 +#ifndef GB_CPU_H +#define GB_CPU_H + +#include "blargg_common.h" +#include "blargg_endian.h" + +typedef unsigned gb_addr_t; // 16-bit CPU address + +class Gb_Cpu { + enum { clocks_per_instr = 4 }; +public: + typedef BOOST::uint8_t uint8_t; + + // Clear registers and map all pages to unmapped + void reset( void* unmapped = 0 ); + + // Map code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. + enum { page_size = 0x2000 }; + void map_code( gb_addr_t start, unsigned size, void* code ); + + uint8_t* get_code( gb_addr_t ); + + // Push a byte on the stack + void push_byte( int ); + + // Game Boy Z80 registers. *Not* kept updated during a call to run(). + struct core_regs_t { + #if BLARGG_BIG_ENDIAN + uint8_t b, c, d, e, h, l, flags, a; + #else + uint8_t c, b, e, d, l, h, a, flags; + #endif + }; + + struct registers_t : core_regs_t { + long pc; // more than 16 bits to allow overflow detection + BOOST::uint16_t sp; + }; + registers_t r; + + // Interrupt enable flag set by EI and cleared by DI + //bool interrupts_enabled; // unused + + // Base address for RST vectors (normally 0) + gb_addr_t rst_base; + + // If CPU executes opcode 0xFF at this address, it treats as illegal instruction + enum { idle_addr = 0xF00D }; + + // Run CPU for at least 'count' cycles and return false, or return true if + // illegal instruction is encountered. + bool run( blargg_long count ); + + // Number of clock cycles remaining for most recent run() call + blargg_long remain() const { return state->remain * clocks_per_instr; } + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + +public: + Gb_Cpu() : rst_base( 0 ) { state = &state_; } + enum { page_shift = 13 }; + enum { page_count = 0x10000 >> page_shift }; +private: + // noncopyable + Gb_Cpu( const Gb_Cpu& ); + Gb_Cpu& operator = ( const Gb_Cpu& ); + + struct state_t { + uint8_t* code_map [page_count + 1]; + blargg_long remain; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + + void set_code_page( int, uint8_t* ); +}; + +inline BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) +{ + return state->code_map [addr >> page_shift] + addr + #if !BLARGG_NONPORTABLE + % (unsigned) page_size + #endif + ; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp new file mode 100644 index 00000000..735653fa --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.cpp @@ -0,0 +1,336 @@ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ + +#include "Gb_Apu.h" + +#include + +/* 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" + +// Gb_Osc + +void Gb_Osc::reset() +{ + delay = 0; + last_amp = 0; + length = 0; + output_select = 3; + output = outputs [output_select]; +} + +void Gb_Osc::clock_length() +{ + if ( (regs [4] & len_enabled_mask) && length ) + length--; +} + +// Gb_Env + +void Gb_Env::clock_envelope() +{ + if ( env_delay && !--env_delay ) + { + env_delay = regs [2] & 7; + int v = volume - 1 + (regs [2] >> 2 & 2); + if ( (unsigned) v < 15 ) + volume = v; + } +} + +bool Gb_Env::write_register( int reg, int data ) +{ + switch ( reg ) + { + case 1: + length = 64 - (regs [1] & 0x3F); + break; + + case 2: + if ( !(data >> 4) ) + enabled = false; + break; + + case 4: + if ( data & trigger ) + { + env_delay = regs [2] & 7; + volume = regs [2] >> 4; + enabled = true; + if ( length == 0 ) + length = 64; + return true; + } + } + return false; +} + +// Gb_Square + +void Gb_Square::reset() +{ + phase = 0; + sweep_freq = 0; + sweep_delay = 0; + Gb_Env::reset(); +} + +void Gb_Square::clock_sweep() +{ + int sweep_period = (regs [0] & period_mask) >> 4; + if ( sweep_period && sweep_delay && !--sweep_delay ) + { + sweep_delay = sweep_period; + regs [3] = sweep_freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); + + int offset = sweep_freq >> (regs [0] & shift_mask); + if ( regs [0] & 0x08 ) + offset = -offset; + sweep_freq += offset; + + if ( sweep_freq < 0 ) + { + sweep_freq = 0; + } + else if ( sweep_freq >= 2048 ) + { + sweep_delay = 0; // don't modify channel frequency any further + sweep_freq = 2048; // silence sound immediately + } + } +} + +void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + if ( sweep_freq == 2048 ) + playing = false; + + static unsigned char const table [4] = { 1, 2, 4, 6 }; + int const duty = table [regs [1] >> 6]; + int amp = volume & playing; + if ( phase >= duty ) + amp = -amp; + + int frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 + { + // really high frequency results in DC at half volume + amp = volume >> 1; + playing = false; + } + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + int const period = (2048 - frequency) * 4; + Blip_Buffer* const output = this->output; + int phase = this->phase; + int delta = amp * 2; + do + { + phase = (phase + 1) & 7; + if ( phase == 0 || phase == duty ) + { + delta = -delta; + synth->offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->phase = phase; + last_amp = delta >> 1; + } + delay = time - end_time; +} + +// Gb_Noise + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + int amp = volume & playing; + int tap = 13 - (regs [3] & 8); + if ( bits >> tap & 2 ) + amp = -amp; + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; + int period = table [regs [3] & 7] << (regs [3] >> 4); + + // keep parallel resampled time to eliminate time conversion in the loop + Blip_Buffer* const output = this->output; + const blip_resampled_time_t resampled_period = + output->resampled_duration( period ); + blip_resampled_time_t resampled_time = output->resampled_time( time ); + unsigned bits = this->bits; + int delta = amp * 2; + + do + { + unsigned changed = (bits >> tap) + 1; + time += period; + bits <<= 1; + if ( changed & 2 ) + { + delta = -delta; + bits |= 1; + synth->offset_resampled( resampled_time, delta, output ); + } + resampled_time += resampled_period; + } + while ( time < end_time ); + + this->bits = bits; + last_amp = delta >> 1; + } + delay = time - end_time; +} + +// Gb_Wave + +inline void Gb_Wave::write_register( int reg, int data ) +{ + switch ( reg ) + { + case 0: + if ( !(data & 0x80) ) + enabled = false; + break; + + case 1: + length = 256 - regs [1]; + break; + + case 2: + volume = data >> 5 & 3; + break; + + case 4: + if ( data & trigger & regs [0] ) + { + wave_pos = 0; + enabled = true; + if ( length == 0 ) + length = 256; + } + } +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) +{ + int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 + int frequency; + { + int amp = (wave [wave_pos] >> volume_shift & playing) * 2; + + frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 + { + amp = 30 >> volume_shift & playing; + playing = false; + } + + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + int const period = (2048 - frequency) * 2; + int wave_pos = (this->wave_pos + 1) & (wave_size - 1); + + do + { + int amp = (wave [wave_pos] >> volume_shift) * 2; + wave_pos = (wave_pos + 1) & (wave_size - 1); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->wave_pos = (wave_pos - 1) & (wave_size - 1); + } + delay = time - end_time; +} + +// Gb_Apu::write_osc + +void Gb_Apu::write_osc( int index, int reg, int data ) +{ + reg -= index * 5; + Gb_Square* sq = &square2; + switch ( index ) + { + case 0: + sq = &square1; + case 1: + if ( sq->write_register( reg, data ) && index == 0 ) + { + square1.sweep_freq = square1.frequency(); + if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) + { + square1.sweep_delay = 1; // cause sweep to recalculate now + square1.clock_sweep(); + } + } + break; + + case 2: + wave.write_register( reg, data ); + break; + + case 3: + if ( noise.write_register( reg, data ) ) + noise.bits = 0x7FFF; + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h new file mode 100644 index 00000000..d7f88ea1 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gb_Oscs.h @@ -0,0 +1,83 @@ +// Private oscillators used by Gb_Apu + +// Gb_Snd_Emu 0.1.5 +#ifndef GB_OSCS_H +#define GB_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct Gb_Osc +{ + enum { trigger = 0x80 }; + enum { len_enabled_mask = 0x40 }; + + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int output_select; + BOOST::uint8_t* regs; // osc's 5 registers + + int delay; + int last_amp; + int volume; + int length; + int enabled; + + void reset(); + void clock_length(); + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } +}; + +struct Gb_Env : Gb_Osc +{ + int env_delay; + + void reset(); + void clock_envelope(); + bool write_register( int, int ); +}; + +struct Gb_Square : Gb_Env +{ + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; + + typedef Blip_Synth Synth; + Synth const* synth; + int sweep_delay; + int sweep_freq; + int phase; + + void reset(); + void clock_sweep(); + void run( blip_time_t, blip_time_t, int playing ); +}; + +struct Gb_Noise : Gb_Env +{ + typedef Blip_Synth Synth; + Synth const* synth; + unsigned bits; + + void run( blip_time_t, blip_time_t, int playing ); +}; + +struct Gb_Wave : Gb_Osc +{ + typedef Blip_Synth Synth; + Synth const* synth; + int wave_pos; + enum { wave_size = 32 }; + BOOST::uint8_t wave [wave_size]; + + void write_register( int, int ); + void run( blip_time_t, blip_time_t, int playing ); +}; + +inline void Gb_Env::reset() +{ + env_delay = 0; + Gb_Osc::reset(); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp new file mode 100644 index 00000000..30a147e5 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.cpp @@ -0,0 +1,288 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Gbs_Emu.h" + +#include "blargg_endian.h" +#include + +/* 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" + +Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000 }; +Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 300 }; + +Gbs_Emu::Gbs_Emu() +{ + set_type( gme_gbs_type ); + + 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 | 0, mixed_type | 0 + }; + set_voice_types( types ); + + set_silence_lookahead( 6 ); + set_max_initial_silence( 21 ); + set_gain( 1.2 ); + + static equalizer_t const eq = { -1.0, 120 }; + set_equalizer( eq ); +} + +Gbs_Emu::~Gbs_Emu() { } + +void Gbs_Emu::unload() +{ + rom.clear(); + 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 ); +} + +blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const +{ + copy_gbs_fields( header_, out ); + return 0; +} + +static blargg_err_t check_gbs_header( void const* header ) +{ + if ( memcmp( header, "GBS", 3 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Gbs_File : Gme_Info_ +{ + Gbs_Emu::header_t h; + + Gbs_File() { set_type( gme_gbs_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + + set_track_count( h.track_count ); + return check_gbs_header( &h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_gbs_fields( h, out ); + return 0; + } +}; + +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 ) +{ + assert( offsetof (header_t,copyright [32]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); + + set_track_count( header_.track_count ); + RETURN_ERR( check_gbs_header( &header_ ) ); + + if ( header_.vers != 1 ) + set_warning( "Unknown file version" ); + + if ( header_.timer_mode & 0x78 ) + set_warning( "Invalid timer mode" ); + + unsigned 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" ); + + set_voice_count( Gb_Apu::osc_count ); + + apu.volume( gain() ); + + return setup_buffer( 4194304 ); +} + +void Gbs_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); +} + +void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + apu.osc_output( i, c, l, r ); +} + +// Emulation + +// see gb_cpu_io.h for read/write functions + +void Gbs_Emu::set_bank( int n ) +{ + blargg_long addr = rom.mask_addr( n * (blargg_long) bank_size ); + if ( addr == 0 && rom.size() > bank_size ) + { + // TODO: what is the correct behavior? Current Game & Watch Gallery + // rip requires that this have no effect or set to bank 1. + //dprintf( "Selected ROM bank 0\n" ); + return; + //n = 1; + } + cpu::map_code( bank_size, bank_size, rom.at_addr( addr ) ); +} + +void Gbs_Emu::update_timer() +{ + if ( header_.timer_mode & 0x04 ) + { + static byte const rates [4] = { 10, 4, 6, 8 }; + int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7); + play_period = (256L - ram [hi_page + 6]) << shift; + } + else + { + play_period = 70224; // 59.73 Hz + } + if ( tempo() != 1.0 ) + play_period = blip_time_t (play_period / tempo()); +} + +static BOOST::uint8_t const sound_data [Gb_Apu::register_count] = { + 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 + 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 + 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave + 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise + 0x77, 0xF3, 0xF1, // vin/volume, status, power mode + 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused + 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data + 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48 +}; + +void Gbs_Emu::cpu_jsr( gb_addr_t addr ) +{ + check( cpu::r.sp == get_le16( header_.stack_ptr ) ); + cpu::r.pc = addr; + cpu_write( --cpu::r.sp, idle_addr >> 8 ); + cpu_write( --cpu::r.sp, idle_addr&0xFF ); +} + +void Gbs_Emu::set_tempo_( double t ) +{ + apu.set_tempo( t ); + update_timer(); +} + +blargg_err_t Gbs_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + 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 + + apu.reset(); + for ( int i = 0; i < (int) sizeof sound_data; i++ ) + apu.write_register( 0, i + apu.start_addr, sound_data [i] ); + + cpu::reset( rom.unmapped() ); + + unsigned load_addr = get_le16( header_.load_addr ); + cpu::rst_base = load_addr; + rom.set_addr( load_addr ); + + 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 ); + + ram [hi_page + 6] = header_.timer_modulo; + ram [hi_page + 7] = header_.timer_mode; + update_timer(); + next_play = play_period; + + cpu::r.a = track; + cpu::r.pc = idle_addr; + cpu::r.sp = get_le16( header_.stack_ptr ); + cpu_time = 0; + cpu_jsr( get_le16( header_.init_addr ) ); + + return 0; +} + +blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) +{ + cpu_time = 0; + while ( cpu_time < duration ) + { + long count = duration - cpu_time; + cpu_time = duration; + bool result = cpu::run( count ); + cpu_time -= cpu::remain(); + + if ( result ) + { + if ( cpu::r.pc == idle_addr ) + { + if ( next_play > duration ) + { + cpu_time = duration; + break; + } + + if ( cpu_time < next_play ) + cpu_time = next_play; + next_play += play_period; + cpu_jsr( get_le16( header_.play_addr ) ); + GME_FRAME_HOOK( this ); + // TODO: handle timer rates different than 60 Hz + } + 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 $%.2x at $%.4x\n", + (int) *cpu::get_code( cpu::r.pc ), (int) cpu::r.pc ); + cpu::r.pc = (cpu::r.pc + 1) & 0xFFFF; + cpu_time += 6; + } + } + } + + duration = cpu_time; + next_play -= cpu_time; + if ( next_play < 0 ) // could go negative if routine is taking too long to return + next_play = 0; + apu.end_frame( cpu_time ); + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h new file mode 100644 index 00000000..93fe691e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gbs_Emu.h @@ -0,0 +1,88 @@ +// Nintendo Game Boy GBS music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef GBS_EMU_H +#define GBS_EMU_H + +#include "Classic_Emu.h" +#include "Gb_Apu.h" +#include "Gb_Cpu.h" + +class Gbs_Emu : private Gb_Cpu, public Classic_Emu { + typedef Gb_Cpu cpu; +public: + // Equalizer profiles for Game Boy Color speaker and headphones + static equalizer_t const handheld_eq; + static equalizer_t const headphones_eq; + + // GBS file header + enum { header_size = 112 }; + struct header_t + { + 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]; + char author [32]; + char copyright [32]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_gbs_type; } + +public: + // deprecated + Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + +public: + Gbs_Emu(); + ~Gbs_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +private: + // rom + enum { bank_size = 0x4000 }; + Rom_Data rom; + void set_bank( int ); + + // timer + blip_time_t cpu_time; + blip_time_t play_period; + blip_time_t next_play; + void update_timer(); + + header_t header_; + void cpu_jsr( gb_addr_t ); + +public: private: friend class Gb_Cpu; + blip_time_t clock() const { return cpu_time - cpu::remain(); } + + enum { joypad_addr = 0xFF00 }; + enum { ram_addr = 0xA000 }; + enum { hi_page = 0xFF00 - ram_addr }; + byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; + Gb_Apu apu; + + int cpu_read( gb_addr_t ); + void cpu_write( gb_addr_t, int ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp new file mode 100644 index 00000000..6821c3a5 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.cpp @@ -0,0 +1,216 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Gme_File.h" + +#include "blargg_endian.h" +#include + +/* 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" + +const char gme_wrong_file_type [] = "Wrong file type for this emulator"; + +void Gme_File::clear_playlist() +{ + playlist.clear(); + clear_playlist_(); + track_count_ = raw_track_count_; +} + +void Gme_File::unload() +{ + clear_playlist(); // *before* clearing track count + track_count_ = 0; + raw_track_count_ = 0; + file_data.clear(); +} + +Gme_File::Gme_File() +{ + type_ = 0; + user_data_ = 0; + user_cleanup_ = 0; + unload(); // clears fields + blargg_verify_byte_order(); // used by most emulator types, so save them the trouble +} + +Gme_File::~Gme_File() +{ + if ( user_cleanup_ ) + user_cleanup_( user_data_ ); +} + +blargg_err_t Gme_File::load_mem_( byte const* data, long size ) +{ + require( data != file_data.begin() ); // load_mem_() or load_() must be overridden + Mem_File_Reader in( data, size ); + return load_( in ); +} + +blargg_err_t Gme_File::load_( Data_Reader& in ) +{ + RETURN_ERR( file_data.resize( in.remain() ) ); + RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); + return load_mem_( file_data.begin(), file_data.size() ); +} + +// public load functions call this at beginning +void Gme_File::pre_load() { unload(); } + +void Gme_File::post_load_() { } + +// public load functions call this at end +blargg_err_t Gme_File::post_load( blargg_err_t err ) +{ + if ( !track_count() ) + set_track_count( type()->track_count ); + if ( !err ) + post_load_(); + else + unload(); + + return err; +} + +// Public load functions + +blargg_err_t Gme_File::load_mem( void const* in, long size ) +{ + pre_load(); + return post_load( load_mem_( (byte const*) in, size ) ); +} + +blargg_err_t Gme_File::load( Data_Reader& in ) +{ + pre_load(); + return post_load( load_( in ) ); +} + +blargg_err_t Gme_File::load_file( const char* path ) +{ + pre_load(); + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + return post_load( load_( in ) ); +} + +blargg_err_t Gme_File::load_remaining_( void const* h, long s, Data_Reader& in ) +{ + Remaining_Reader rem( h, s, &in ); + return load( rem ); +} + +// Track info + +void Gme_File::copy_field_( char* out, const char* in, int in_size ) +{ + if ( !in || !*in ) + return; + + // remove spaces/junk from beginning + while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) + { + in++; + in_size--; + } + + // truncate + if ( in_size > max_field_ ) + in_size = max_field_; + + // find terminator + int len = 0; + while ( len < in_size && in [len] ) + len++; + + // remove spaces/junk from end + while ( len && unsigned (in [len - 1]) <= ' ' ) + len--; + + // copy + out [len] = 0; + memcpy( out, in, len ); + + // strip out stupid fields that should have been left blank + if ( !strcmp( out, "?" ) || !strcmp( out, "" ) || !strcmp( out, "< ? >" ) ) + out [0] = 0; +} + +void Gme_File::copy_field_( char* out, const char* in ) +{ + copy_field_( out, in, max_field_ ); +} + +blargg_err_t Gme_File::remap_track_( int* track_io ) const +{ + if ( (unsigned) *track_io >= (unsigned) track_count() ) + return "Invalid track"; + + if ( (unsigned) *track_io < (unsigned) playlist.size() ) + { + M3u_Playlist::entry_t const& e = playlist [*track_io]; + *track_io = 0; + if ( e.track >= 0 ) + { + *track_io = e.track; + if ( !(type_->flags_ & 0x02) ) + *track_io -= e.decimal_track; + } + if ( *track_io >= raw_track_count_ ) + return "Invalid track in m3u playlist"; + } + else + { + check( !playlist.size() ); + } + return 0; +} + +blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const +{ + out->track_count = track_count(); + out->length = -1; + out->loop_length = -1; + out->intro_length = -1; + out->song [0] = 0; + + out->game [0] = 0; + out->author [0] = 0; + out->copyright [0] = 0; + out->comment [0] = 0; + out->dumper [0] = 0; + out->system [0] = 0; + + copy_field_( out->system, type()->system ); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + RETURN_ERR( track_info_( out, remapped ) ); + + // override with m3u info + if ( playlist.size() ) + { + M3u_Playlist::info_t const& i = playlist.info(); + copy_field_( out->game , i.title ); + copy_field_( out->author, i.engineer ); + copy_field_( out->author, i.composer ); + copy_field_( out->dumper, i.ripping ); + + M3u_Playlist::entry_t const& e = playlist [track]; + copy_field_( out->song, e.name ); + if ( e.length >= 0 ) out->length = e.length * 1000L; + if ( e.intro >= 0 ) out->intro_length = e.intro * 1000L; + if ( e.loop >= 0 ) out->loop_length = e.loop * 1000L; + } + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h new file mode 100644 index 00000000..a535633b --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gme_File.h @@ -0,0 +1,145 @@ +// Common interface to game music file loading and information + +// Game_Music_Emu 0.5.2 +#ifndef GME_FILE_H +#define GME_FILE_H + +#include "gme.h" +#include "blargg_common.h" +#include "Data_Reader.h" +#include "M3u_Playlist.h" + +// Error returned if file is wrong type +//extern const char gme_wrong_file_type []; // declared in gme.h + +struct Gme_File { +public: +// File loading + + // Each loads game music data from a file and returns an error if + // file is wrong type or is seriously corrupt. They also set warning + // string for minor problems. + + // Load from file on disk + blargg_err_t load_file( const char* path ); + + // Load from custom data source (see Data_Reader.h) + blargg_err_t load( Data_Reader& ); + + // Load from file already read into memory. Keeps pointer to data, so you + // must not free it until you're done with the file. + blargg_err_t load_mem( void const* data, long size ); + + // Load an m3u playlist. Must be done after loading main music file. + blargg_err_t load_m3u( const char* path ); + blargg_err_t load_m3u( Data_Reader& in ); + + // Clears any loaded m3u playlist and any internal playlist that the music + // format supports (NSFE for example). + void clear_playlist(); + +// Informational + + // Type of emulator. For example if this returns gme_nsfe_type, this object + // is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary. + gme_type_t type() const; + + // Most recent warning string, or NULL if none. Clears current warning after + // returning. + const char* warning(); + + // Number of tracks or 0 if no file has been loaded + int track_count() const; + + // Get information for a track (length, name, author, etc.) + // See gme.h for definition of struct track_info_t. + blargg_err_t track_info( track_info_t* out, int track ) const; + +// User data/cleanup + + // Set/get pointer to data you want to associate with this emulator. + // You can use this for whatever you want. + void set_user_data( void* p ) { user_data_ = p; } + void* user_data() const { return user_data_; } + + // Register cleanup function to be called when deleting emulator, or NULL to + // clear it. Passes user_data to cleanup function. + void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } + +public: + // deprecated + int error_count() const; // use warning() +public: + Gme_File(); + virtual ~Gme_File(); + BLARGG_DISABLE_NOTHROW + typedef BOOST::uint8_t byte; +protected: + // Services + void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } + void set_warning( const char* s ) { warning_ = s; } + void set_type( gme_type_t t ) { type_ = t; } + blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining ); + + // Overridable + virtual void unload(); // called before loading file and if loading fails + virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() + virtual blargg_err_t load_mem_( byte const* data, long size ); // use data in memory + virtual blargg_err_t track_info_( track_info_t* out, int track ) const = 0; + virtual void pre_load(); + virtual void post_load_(); + virtual void clear_playlist_() { } + +public: + blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu +private: + // noncopyable + Gme_File( const Gme_File& ); + Gme_File& operator = ( const Gme_File& ); + + gme_type_t type_; + int track_count_; + int raw_track_count_; + const char* warning_; + void* user_data_; + gme_user_cleanup_t user_cleanup_; + M3u_Playlist playlist; + char playlist_warning [64]; + blargg_vector file_data; // only if loaded into memory using default load + + blargg_err_t load_m3u_( blargg_err_t ); + blargg_err_t post_load( blargg_err_t err ); +public: + // track_info field copying + enum { max_field_ = 255 }; + static void copy_field_( char* out, const char* in ); + static void copy_field_( char* out, const char* in, int len ); +}; + +Music_Emu* gme_new_( Music_Emu*, long sample_rate ); + +#define GME_COPY_FIELD( in, out, name ) \ + { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } + +#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 gme_type_t Gme_File::type() const { return type_; } +inline int Gme_File::error_count() const { return warning_ != 0; } +inline int Gme_File::track_count() const { return track_count_; } + +inline const char* Gme_File::warning() +{ + const char* s = warning_; + warning_ = 0; + return s; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp new file mode 100644 index 00000000..499a9ca2 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.cpp @@ -0,0 +1,379 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Gym_Emu.h" + +#include "blargg_endian.h" +#include + +/* 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" + +double const min_tempo = 0.25; +double const oversample_factor = 5 / 3.0; +double const fm_gain = 3.0; + +const long base_clock = 53700300; +const long clock_rate = base_clock / 15; + +Gym_Emu::Gym_Emu() +{ + data = 0; + pos = 0; + set_type( gme_gym_type ); + + static const char* const names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + set_voice_names( names ); + set_silence_lookahead( 1 ); // tracks should already be trimmed +} + +Gym_Emu::~Gym_Emu() { } + +// Track info + +static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out ) +{ + if ( !memcmp( h.tag, "GYMX", 4 ) ) + { + length = length * 50 / 3; // 1000 / 60 + long loop = get_le32( h.loop_start ); + if ( loop ) + { + out->intro_length = loop * 50 / 3; + out->loop_length = length - out->intro_length; + } + else + { + out->length = length; + out->intro_length = length; // make it clear that track is no longer than length + out->loop_length = 0; + } + + // more stupidity where the field should have been left + if ( strcmp( h.song, "Unknown Song" ) ) + GME_COPY_FIELD( h, out, song ); + + if ( strcmp( h.game, "Unknown Game" ) ) + GME_COPY_FIELD( h, out, game ); + + if ( strcmp( h.copyright, "Unknown Publisher" ) ) + GME_COPY_FIELD( h, out, copyright ); + + if ( strcmp( h.dumper, "Unknown Person" ) ) + GME_COPY_FIELD( h, out, dumper ); + + if ( strcmp( h.comment, "Header added by YMAMP" ) ) + GME_COPY_FIELD( h, out, comment ); + } +} + +blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const +{ + get_gym_info( header_, track_length(), out ); + return 0; +} + +static long gym_track_length( byte const* p, byte const* end ) +{ + long time = 0; + while ( p < end ) + { + switch ( *p++ ) + { + case 0: + time++; + break; + + case 1: + case 2: + p += 2; + break; + + case 3: + p += 1; + break; + } + } + return time; +} + +long Gym_Emu::track_length() const { return gym_track_length( data, data_end ); } + +static blargg_err_t check_header( byte const* in, long size, int* data_offset = 0 ) +{ + if ( size < 4 ) + return gme_wrong_file_type; + + if ( memcmp( in, "GYMX", 4 ) == 0 ) + { + if ( size < Gym_Emu::header_size + 1 ) + return gme_wrong_file_type; + + if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) + return "Packed GYM file not supported"; + + if ( data_offset ) + *data_offset = Gym_Emu::header_size; + } + else if ( *in > 3 ) + { + return gme_wrong_file_type; + } + + return 0; +} + +struct Gym_File : Gme_Info_ +{ + byte const* file_begin; + byte const* file_end; + int data_offset; + + Gym_File() { set_type( gme_gym_type ); } + + blargg_err_t load_mem_( byte const* in, long size ) + { + file_begin = in; + file_end = in + size; + data_offset = 0; + return check_header( in, size, &data_offset ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + long length = gym_track_length( &file_begin [data_offset], file_end ); + get_gym_info( *(Gym_Emu::header_t const*) file_begin, length, out ); + return 0; + } +}; + +static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; } +static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } + +gme_type_t_ const gme_gym_type [1] = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }; + +// Setup + +blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate ) +{ + blip_eq_t eq( -32, 8000, sample_rate ); + apu.treble_eq( eq ); + dac_synth.treble_eq( eq ); + apu.volume( 0.135 * fm_gain * gain() ); + dac_synth.volume( 0.125 / 256 * fm_gain * gain() ); + double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() ); + fm_sample_rate = sample_rate * factor; + + RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); + blip_buf.clock_rate( clock_rate ); + + RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) ); + RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) ); + + return 0; +} + +void Gym_Emu::set_tempo_( double t ) +{ + if ( t < min_tempo ) + { + set_tempo( min_tempo ); + return; + } + + if ( blip_buf.sample_rate() ) + { + clocks_per_frame = long (clock_rate / 60 / tempo()); + Dual_Resampler::resize( long (sample_rate() / (60.0 * tempo())) ); + } +} + +void Gym_Emu::mute_voices_( int mask ) +{ + Music_Emu::mute_voices_( mask ); + fm.mute_voices( mask ); + dac_muted = (mask & 0x40) != 0; + apu.output( (mask & 0x80) ? 0 : &blip_buf ); +} + +blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) +{ + assert( offsetof (header_t,packed [4]) == header_size ); + int offset = 0; + RETURN_ERR( check_header( in, size, &offset ) ); + set_voice_count( 8 ); + + data = in + offset; + data_end = in + size; + loop_begin = 0; + + if ( offset ) + header_ = *(header_t const*) in; + else + memset( &header_, 0, sizeof header_ ); + + return 0; +} + +// Emulation + +blargg_err_t Gym_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + + pos = data; + loop_remain = get_le32( header_.loop_start ); + + prev_dac_count = 0; + dac_enabled = false; + dac_amp = -1; + + fm.reset(); + apu.reset(); + blip_buf.clear(); + Dual_Resampler::clear(); + return 0; +} + +void Gym_Emu::run_dac( int dac_count ) +{ + // Guess beginning and end of sample and adjust rate and buffer position accordingly. + + // count dac samples in next frame + int next_dac_count = 0; + const byte* p = this->pos; + int cmd; + while ( (cmd = *p++) != 0 ) + { + int data = *p++; + if ( cmd <= 2 ) + ++p; + if ( cmd == 1 && data == 0x2A ) + next_dac_count++; + } + + // detect beginning and end of sample + int rate_count = dac_count; + int start = 0; + if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) + { + rate_count = next_dac_count; + start = next_dac_count - dac_count; + } + else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) + { + rate_count = prev_dac_count; + } + + // Evenly space samples within buffer section being used + blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count; + + blip_resampled_time_t time = blip_buf.resampled_time( 0 ) + + period * start + (period >> 1); + + int dac_amp = this->dac_amp; + if ( dac_amp < 0 ) + dac_amp = dac_buf [0]; + + for ( int i = 0; i < dac_count; i++ ) + { + int delta = dac_buf [i] - dac_amp; + dac_amp += delta; + dac_synth.offset_resampled( time, delta, &blip_buf ); + time += period; + } + this->dac_amp = dac_amp; +} + +void Gym_Emu::parse_frame() +{ + int dac_count = 0; + const byte* pos = this->pos; + + if ( loop_remain && !--loop_remain ) + loop_begin = pos; // find loop on first time through sequence + + int cmd; + while ( (cmd = *pos++) != 0 ) + { + int data = *pos++; + if ( cmd == 1 ) + { + int data2 = *pos++; + if ( data != 0x2A ) + { + if ( data == 0x2B ) + dac_enabled = (data2 & 0x80) != 0; + + fm.write0( data, data2 ); + } + else if ( dac_count < (int) sizeof dac_buf ) + { + dac_buf [dac_count] = data2; + dac_count += dac_enabled; + } + } + else if ( cmd == 2 ) + { + fm.write1( data, *pos++ ); + } + else if ( cmd == 3 ) + { + apu.write_data( 0, data ); + } + else + { + // to do: many GYM streams are full of errors, and error count should + // reflect cases where music is really having problems + //log_error(); + --pos; // put data back + } + } + + // loop + if ( pos >= data_end ) + { + check( pos == data_end ); + + if ( loop_begin ) + pos = loop_begin; + else + set_track_ended(); + } + this->pos = pos; + + // dac + if ( dac_count && !dac_muted ) + run_dac( dac_count ); + prev_dac_count = dac_count; +} + +int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) +{ + if ( !track_ended() ) + parse_frame(); + + apu.end_frame( blip_time ); + + memset( buf, 0, sample_count * sizeof *buf ); + fm.run( sample_count >> 1, buf ); + + return sample_count; +} + +blargg_err_t Gym_Emu::play_( long count, sample_t* out ) +{ + Dual_Resampler::dual_play( count, out, blip_buf ); + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h new file mode 100644 index 00000000..05419ea2 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Gym_Emu.h @@ -0,0 +1,82 @@ +// Sega Genesis/Mega Drive GYM music file emulator +// Includes with PCM timing recovery to improve sample quality. + +// Game_Music_Emu 0.5.2 +#ifndef GYM_EMU_H +#define GYM_EMU_H + +#include "Dual_Resampler.h" +#include "Ym2612_Emu.h" +#include "Music_Emu.h" +#include "Sms_Apu.h" + +class Gym_Emu : public Music_Emu, private Dual_Resampler { +public: + // GYM file header + enum { header_size = 428 }; + struct header_t + { + char tag [4]; + char song [32]; + char game [32]; + char copyright [32]; + char emulator [32]; + char dumper [32]; + char comment [256]; + byte loop_start [4]; // in 1/60 seconds, 0 if not looped + byte packed [4]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_gym_type; } + +public: + // deprecated + Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + enum { gym_rate = 60 }; + long track_length() const; // use track_info() + +public: + Gym_Emu(); + ~Gym_Emu(); +protected: + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t set_sample_rate_( long sample_rate ); + blargg_err_t start_track_( int ); + blargg_err_t play_( long count, sample_t* ); + void mute_voices_( int ); + void set_tempo_( double ); + int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); +private: + // sequence data begin, loop begin, current position, end + const byte* data; + const byte* loop_begin; + const byte* pos; + const byte* data_end; + blargg_long loop_remain; // frames remaining until loop beginning has been located + header_t header_; + double fm_sample_rate; + blargg_long clocks_per_frame; + void parse_frame(); + + // dac (pcm) + int dac_amp; + int prev_dac_count; + bool dac_enabled; + bool dac_muted; + void run_dac( int ); + + // sound + Blip_Buffer blip_buf; + Ym2612_Emu fm; + Blip_Synth dac_synth; + Sms_Apu apu; + byte dac_buf [1024]; +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp new file mode 100644 index 00000000..22389121 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.cpp @@ -0,0 +1,315 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Hes_Apu.h" + +#include + +/* 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" + +bool const center_waves = true; // reduces asymmetry and clamping when starting notes + +Hes_Apu::Hes_Apu() +{ + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + osc->outputs [0] = 0; + osc->outputs [1] = 0; + osc->chans [0] = 0; + osc->chans [1] = 0; + osc->chans [2] = 0; + } + while ( osc != oscs ); + + reset(); +} + +void Hes_Apu::reset() +{ + latch = 0; + balance = 0xFF; + + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + memset( osc, 0, offsetof (Hes_Osc,outputs) ); + osc->noise_lfsr = 1; + osc->control = 0x40; + osc->balance = 0xFF; + } + while ( osc != oscs ); +} + +void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + require( (unsigned) index < osc_count ); + oscs [index].chans [0] = center; + oscs [index].chans [1] = left; + oscs [index].chans [2] = right; + + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + balance_changed( *osc ); + } + while ( osc != oscs ); +} + +void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) +{ + Blip_Buffer* const osc_outputs_0 = outputs [0]; // cache often-used values + if ( osc_outputs_0 && control & 0x80 ) + { + int dac = this->dac; + + int const volume_0 = volume [0]; + { + int delta = dac * volume_0 - last_amp [0]; + if ( delta ) + synth_.offset( last_time, delta, osc_outputs_0 ); + osc_outputs_0->set_modified(); + } + + Blip_Buffer* const osc_outputs_1 = outputs [1]; + int const volume_1 = volume [1]; + if ( osc_outputs_1 ) + { + int delta = dac * volume_1 - last_amp [1]; + if ( delta ) + synth_.offset( last_time, delta, osc_outputs_1 ); + osc_outputs_1->set_modified(); + } + + blip_time_t time = last_time + delay; + if ( time < end_time ) + { + if ( noise & 0x80 ) + { + if ( volume_0 | volume_1 ) + { + // noise + int const period = (32 - (noise & 0x1F)) * 64; // TODO: correct? + unsigned noise_lfsr = this->noise_lfsr; + do + { + int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); + // Implemented using "Galios configuration" + // TODO: find correct LFSR algorithm + noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); + //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + synth_.offset( time, delta * volume_0, osc_outputs_0 ); + if ( osc_outputs_1 ) + synth_.offset( time, delta * volume_1, osc_outputs_1 ); + } + time += period; + } + while ( time < end_time ); + + this->noise_lfsr = noise_lfsr; + assert( noise_lfsr ); + } + } + else if ( !(control & 0x40) ) + { + // wave + int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop + int period = this->period * 2; + if ( period >= 14 && (volume_0 | volume_1) ) + { + do + { + int new_dac = wave [phase]; + phase = (phase + 1) & 0x1F; + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + synth_.offset( time, delta * volume_0, osc_outputs_0 ); + if ( osc_outputs_1 ) + synth_.offset( time, delta * volume_1, osc_outputs_1 ); + } + time += period; + } + while ( time < end_time ); + } + else + { + if ( !period ) + { + // TODO: Gekisha Boy assumes that period = 0 silences wave + //period = 0x1000 * 2; + period = 1; + //if ( !(volume_0 | volume_1) ) + // dprintf( "Used period 0\n" ); + } + + // maintain phase when silent + blargg_long count = (end_time - time + period - 1) / period; + phase += count; // phase will be masked below + time += count * period; + } + this->phase = (phase - 1) & 0x1F; // undo pre-advance + } + } + time -= end_time; + if ( time < 0 ) + time = 0; + delay = time; + + this->dac = dac; + last_amp [0] = dac * volume_0; + last_amp [1] = dac * volume_1; + } + last_time = end_time; +} + +void Hes_Apu::balance_changed( Hes_Osc& osc ) +{ + static short const log_table [32] = { // ~1.5 db per step + #define ENTRY( factor ) short (factor * Hes_Osc::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; + + left = log_table [left ]; + right = log_table [right]; + + // optimizing for the common case of being centered also allows easy + // panning using Effects_Buffer + osc.outputs [0] = osc.chans [0]; // center + osc.outputs [1] = 0; + if ( left != right ) + { + osc.outputs [0] = osc.chans [1]; // left + osc.outputs [1] = osc.chans [2]; // right + } + + if ( center_waves ) + { + osc.last_amp [0] += (left - osc.volume [0]) * 16; + osc.last_amp [1] += (right - osc.volume [1]) * 16; + } + + osc.volume [0] = left; + osc.volume [1] = right; +} + +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; + + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + osc->run_until( synth, time ); + balance_changed( *oscs ); + } + while ( osc != oscs ); + } + } + else if ( latch < osc_count ) + { + Hes_Osc& osc = oscs [latch]; + osc.run_until( synth, 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: + if ( &osc >= &oscs [4] ) + 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 ) +{ + Hes_Osc* osc = &oscs [osc_count]; + do + { + osc--; + if ( end_time > osc->last_time ) + osc->run_until( synth, end_time ); + assert( osc->last_time >= end_time ); + osc->last_time -= end_time; + } + while ( osc != oscs ); +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h new file mode 100644 index 00000000..ca0c932f --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Apu.h @@ -0,0 +1,66 @@ +// Turbo Grafx 16 (PC Engine) PSG sound chip emulator + +// Game_Music_Emu 0.5.2 +#ifndef HES_APU_H +#define HES_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct Hes_Osc +{ + unsigned char wave [32]; + short volume [2]; + int last_amp [2]; + int delay; + int period; + unsigned char noise; + unsigned char phase; + unsigned char balance; + unsigned char dac; + blip_time_t last_time; + + Blip_Buffer* outputs [2]; + Blip_Buffer* chans [3]; + unsigned noise_lfsr; + unsigned char control; + + enum { amp_range = 0x8000 }; + typedef Blip_Synth synth_t; + + void run_until( synth_t& synth, blip_time_t ); +}; + +class Hes_Apu { +public: + void treble_eq( blip_eq_t const& ); + void volume( double ); + + enum { osc_count = 6 }; + void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + void reset(); + + enum { start_addr = 0x0800 }; + enum { end_addr = 0x0809 }; + void write_data( blip_time_t, int addr, int data ); + + void end_frame( blip_time_t ); + +public: + Hes_Apu(); +private: + Hes_Osc oscs [osc_count]; + int latch; + int balance; + Hes_Osc::synth_t synth; + + void balance_changed( Hes_Osc& ); + void recalc_chans(); +}; + +inline void Hes_Apu::volume( double v ) { synth.volume( 1.8 / osc_count / Hes_Osc::amp_range * v ); } + +inline void Hes_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp new file mode 100644 index 00000000..2615a0bb --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.cpp @@ -0,0 +1,1303 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Hes_Cpu.h" + +#include "blargg_endian.h" + +//#include "hes_cpu_log.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 */ + +// 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; + +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) + +#include "hes_cpu_io.h" + +#include "blargg_source.h" + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +// status flags +int const st_n = 0x80; +int const st_v = 0x40; +int const st_t = 0x20; +int const st_b = 0x10; +int const st_d = 0x08; +int const st_i = 0x04; +int const st_z = 0x02; +int const st_c = 0x01; + +void Hes_Cpu::reset() +{ + check( state == &state_ ); + state = &state_; + + state_.time = 0; + state_.base = 0; + irq_time_ = future_hes_time; + end_time_ = future_hes_time; + + r.status = st_i; + r.sp = 0; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + + blargg_verify_byte_order(); +} + +void Hes_Cpu::set_mmr( int reg, int bank ) +{ + assert( (unsigned) reg <= page_count ); // allow page past end to be set + assert( (unsigned) bank < 0x100 ); + mmr [reg] = bank; + uint8_t const* code = CPU_SET_MMR( this, reg, bank ); + state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift ); +} + +#define TIME (s_time + s.base) + +#define READ( addr ) CPU_READ( this, (addr), TIME ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} +#define READ_LOW( addr ) (ram [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) +#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +// even on x86, using short and unsigned char was slower +typedef int fint16; +typedef unsigned fuint16; +typedef unsigned fuint8; +typedef blargg_long fint32; + +bool Hes_Cpu::run( hes_time_t end_time ) +{ + bool illegal_encountered = false; + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + // even on x86, using s.time in place of s_time was slower + fint16 s_time = s.time; + + // registers + fuint16 pc = r.pc; + fuint8 a = r.a; + fuint8 x = r.x; + fuint8 y = r.y; + fuint16 sp; + SET_SP( r.sp ); + + #define IS_NEG (nz & 0x8080) + + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ + } while ( 0 ) + + fuint8 status; + fuint16 c; // carry set if (c & 0x100) != 0 + fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + fuint8 temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +branch_not_taken: + s_time -= 2; +loop: + + #ifndef NDEBUG + { + hes_time_t correct = end_time_; + if ( !(status & st_i) && correct > irq_time_ ) + correct = irq_time_; + check( s.base == correct ); + /* + static long count; + if ( count == 1844 ) Debugger(); + if ( s.base != correct ) dprintf( "%ld\n", count ); + count++; + */ + } + #endif + + check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + + uint8_t const* instr = s.code_map [pc >> page_shift]; + fuint8 opcode; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + opcode = instr [pc]; + pc++; + instr += pc; + #else + instr += PAGE_OFFSET( pc ); + opcode = *instr++; + pc++; + #endif + + // TODO: each reference lists slightly different timing values, ugh + static uint8_t 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,6,// 0 + 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1 + 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2 + 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3 + 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4 + 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5 + 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6 + 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7 + 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8 + 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9 + 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A + 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B + 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C + 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D + 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E + 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F + }; // 0x00 was 8 + + fuint16 data; + data = clock_table [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = *instr; + + #ifdef HES_CPU_LOG_H + log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], + instr [3], instr [4], instr [5] ); + //log_opcode( opcode ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// 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_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) +#define PAGE_CROSS_PENALTY( lsb ) + +// Branch + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + fint16 offset = (BOOST::int8_t) data;\ + pc++;\ + if ( !(cond) ) goto branch_not_taken;\ + pc = BOOST::uint16_t (pc + offset);\ + goto loop;\ +} + + case 0xF0: // BEQ + BRANCH( !((uint8_t) nz) ); + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x10: // BPL + BRANCH( !IS_NEG ); + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x80: // BRA + branch_taken: + BRANCH( true ); + + case 0xFF: + if ( pc == idle_addr + 1 ) + goto idle_done; + 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: { + fuint16 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_PROG( data ) ); + goto loop; + } + +// Subroutine + + case 0x44: // BSR + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, pc ); + goto branch_taken; + + case 0x20: { // JSR + fuint16 temp = pc + 1; + pc = GET_ADDR(); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x60: // RTS + pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); + pc += 1 + READ_LOW( sp ); + sp = (sp - 0xFE) | 0x100; + goto loop; + + case 0x00: // BRK + goto handle_brk; + +// Common + + case 0xBD:{// LDA abs,X + PAGE_CROSS_PENALTY( data + x ); + fuint16 addr = GET_ADDR() + x; + pc += 2; + CPU_READ_FAST( this, addr, TIME, nz ); + a = nz; + goto loop; + } + + case 0x9D:{// STA abs,X + fuint16 addr = GET_ADDR() + x; + pc += 2; + CPU_WRITE_FAST( this, addr, a, TIME ); + goto loop; + } + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xAE:{// LDX abs + fuint16 addr = GET_ADDR(); + pc += 2; + CPU_READ_FAST( this, addr, TIME, nz ); + x = nz; + goto loop; + } + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + +// Load/store + + { + fuint16 addr; + case 0x91: // STA (ind),Y + addr = 0x100 * READ_LOW( uint8_t (data + 1) ); + addr += READ_LOW( data ) + y; + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + data = uint8_t (data + x); + case 0x92: // STA (ind) + addr = 0x100 * READ_LOW( uint8_t (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: + CPU_WRITE_FAST( this, addr, a, TIME ); + goto loop; + } + + { + fuint16 addr; + case 0xA1: // LDA (ind,X) + data = uint8_t (data + x); + case 0xB2: // LDA (ind) + addr = 0x100 * READ_LOW( uint8_t (data + 1) ); + addr += READ_LOW( data ); + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + PAGE_CROSS_PENALTY( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + data += y; + PAGE_CROSS_PENALTY( data ); + case 0xAD: // LDA abs + addr = data + 0x100 * GET_MSB(); + pc += 2; + a_nz_read_addr: + CPU_READ_FAST( this, addr, TIME, nz ); + a = nz; + goto loop; + } + + case 0xBE:{// LDX abs,y + PAGE_CROSS_PENALTY( data + y ); + fuint16 addr = GET_ADDR() + y; + pc += 2; + FLUSH_TIME(); + x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( uint8_t (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 + fuint16 addr; + ADD_PAGE( addr ); + FLUSH_TIME(); + nz = READ( addr ); + CACHE_TIME(); + goto bit_common; + } + case 0x34: // BIT zp,x + data = uint8_t (data + x); + case 0x24: // BIT zp + data = READ_LOW( data ); + case 0x89: // BIT imm + nz = data; + bit_common: + pc++; + status &= ~st_v; + status |= nz & st_v; + 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; + + { + fuint16 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( addr ); + CACHE_TIME(); + goto tst_common; + } + + case 0xA3: // TST zp,x + nz = READ_LOW( uint8_t (GET_MSB() + x) ); + goto tst_common; + + case 0x83: // TST zp + nz = READ_LOW( GET_MSB() ); + tst_common: + pc += 2; + status &= ~st_v; + status |= nz & st_v; + 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; + + { + fuint16 addr; + case 0x0C: // TSB abs + case 0x1C: // TRB abs + addr = GET_ADDR(); + pc++; + goto txb_addr; + + // TODO: everyone lists different behaviors for the status flags, ugh + case 0x04: // TSB zp + case 0x14: // TRB zp + addr = data + ram_addr; + txb_addr: + FLUSH_TIME(); + nz = a | READ( addr ); + if ( opcode & 0x10 ) + nz ^= a; // bits from a will already be set, so this clears them + status &= ~st_v; + status |= nz & st_v; + pc++; + WRITE( 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( data, 0 ); + CACHE_TIME(); + goto loop; + + case 0x74: // STZ zp,x + data = uint8_t (data + x); + case 0x64: // STZ zp + pc++; + WRITE_LOW( data, 0 ); + goto loop; + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (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 = uint8_t (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_CROSS_PENALTY( data ); + case 0xAC:{// LDY abs + fuint16 addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + { + fuint8 temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + fuint16 addr = GET_ADDR(); + pc += 2; + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + fuint16 addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( 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 &= 0xFF; + goto loop; + + case 0xCC:{// CPY abs + fuint16 addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( 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 &= 0xFF; + goto loop; + +// Logical + +#define ARITH_ADDR_MODES( op )\ + case op - 0x04: /* (ind,x) */\ + data = uint8_t (data + x);\ + case op + 0x0D: /* (ind) */\ + data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ + goto ptr##op;\ + case op + 0x0C:{/* (ind),y */\ + fuint16 temp = READ_LOW( data ) + y;\ + PAGE_CROSS_PENALTY( temp );\ + data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + goto ptr##op;\ + }\ + case op + 0x10: /* zp,X */\ + data = uint8_t (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_CROSS_PENALTY( data );\ + case op + 0x08: /* abs */\ + ADD_PAGE( data );\ + ptr##op:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ + case op + 0x04: /* imm */\ + imm##op: + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xFF; + 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 ( status & st_d ) + dprintf( "Decimal mode not supported\n" ); + fint16 carry = c >> 8 & 1; + fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + status &= ~st_v; + status |= ov >> 2 & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) 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 = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + fint16 temp = c >> 8 & 1; + c = nz; + nz |= temp; + a = (uint8_t) 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( 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( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (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 = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (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_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + + case 0x1A: // INA + INC_DEC_AXY( a, +1 ) + + case 0xE8: // INX + INC_DEC_AXY( x, +1 ) + + case 0xC8: // INY + INC_DEC_AXY( y, +1 ) + + case 0x3A: // DEA + INC_DEC_AXY( a, -1 ) + + case 0xCA: // DEX + INC_DEC_AXY( x, -1 ) + + case 0x88: // DEY + INC_DEC_AXY( y, -1 ) + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = (unsigned) -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 = (unsigned) -1; + inc_common: + FLUSH_TIME(); + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xA8: // TAY + y = a; + nz = a; + goto loop; + + case 0x98: // TYA + a = y; + nz = y; + goto loop; + + case 0xAA: // TAX + x = a; + nz = a; + goto loop; + + case 0x8A: // TXA + a = x; + 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 ) {\ + fuint8 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 + PUSH( a ); + goto loop; + + case 0xDA: // PHX + PUSH( x ); + goto loop; + + case 0x5A: // PHY + PUSH( y ); + goto loop; + + case 0x40:{// RTI + fuint8 temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xFF) ); + pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; + sp = (sp - 0xFD) | 0x100; + data = status; + SET_STATUS( temp ); + this->r.status = status; // update externally-visible I flag + if ( (data ^ status) & st_i ) + { + hes_time_t new_time = end_time_; + if ( !(status & st_i) && new_time > irq_time_ ) + new_time = irq_time_; + blargg_long delta = s.base - new_time; + s.base = new_time; + s_time += delta; + } + goto loop; + } + + #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100 + + case 0x68: // PLA + a = nz = POP(); + goto loop; + + case 0xFA: // PLX + x = nz = POP(); + goto loop; + + case 0x7A: // PLY + y = nz = POP(); + goto loop; + + case 0x28:{// PLP + fuint8 temp = POP(); + fuint8 changed = status ^ temp; + SET_STATUS( temp ); + if ( !(changed & st_i) ) + goto loop; // I flag didn't change + if ( status & st_i ) + goto handle_sei; + goto handle_cli; + } + #undef POP + + case 0x08: { // PHP + fuint8 temp; + CALC_STATUS( temp ); + PUSH( temp | st_b ); + goto loop; + } + +// Flags + + case 0x38: // SEC + c = (unsigned) ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) + { + if ( TIME < irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = 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; + irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop + goto loop; + } + delayed_cli: + dprintf( "Delayed CLI not supported\n" ); // TODO: implement + goto loop; + } + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + dprintf( "Delayed SEI not supported\n" ); // TODO: implement + goto loop; + } + +// Special + + case 0x53:{// TAM + fuint8 const 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 = mmr; + do + { + if ( data & 1 ) + a = *in; + in++; + } + while ( (data >>= 1) != 0 ); + goto loop; + } + + case 0x03: // ST0 + case 0x13: // ST1 + case 0x23:{// ST2 + fuint16 addr = opcode >> 4; + if ( addr ) + addr++; + pc++; + FLUSH_TIME(); + CPU_WRITE_VDP( this, addr, data, TIME ); + 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 + //fuint16 operand = GET_MSB(); + dprintf( "SET not handled\n" ); + //switch ( data ) + //{ + //} + illegal_encountered = true; + goto loop; + } + +// Block transfer + + { + fuint16 in_alt; + fint16 in_inc; + fuint16 out_alt; + fint16 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: + fuint16 in = GET_LE16( instr + 0 ); + fuint16 out = GET_LE16( instr + 2 ); + int count = GET_LE16( instr + 4 ); + if ( !count ) + count = 0x10000; + pc += 6; + WRITE_LOW( 0x100 | (sp - 1), y ); + WRITE_LOW( 0x100 | (sp - 2), a ); + WRITE_LOW( 0x100 | (sp - 3), x ); + FLUSH_TIME(); + do + { + // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O + fuint8 t = READ( in ); + in += in_inc; + in &= 0xFFFF; + s.time += 6; + if ( in_alt ) + in_inc = -in_inc; + WRITE( out, t ); + out += out_inc; + out &= 0xFFFF; + if ( out_alt ) + out_inc = -out_inc; + } + while ( --count ); + CACHE_TIME(); + goto loop; + } + +// Illegal + + default: + assert( (unsigned) opcode <= 0xFF ); + dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); + illegal_encountered = true; + goto loop; + } + assert( false ); + + int result_; +handle_brk: + pc++; + result_ = 6; + +interrupt: + { + s_time += 7; + + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); + + sp = (sp - 3) | 0x100; + fuint8 temp; + CALC_STATUS( temp ); + if ( result_ == 6 ) + temp |= st_b; + WRITE_LOW( sp, temp ); + + status &= ~st_d; + status |= st_i; + this->r.status = status; // update externally-visible I flag + + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + goto loop; + } + +idle_done: + s_time = 0; +out_of_time: + pc--; + FLUSH_TIME(); + CPU_DONE( this, TIME, result_ ); + CACHE_TIME(); + if ( result_ > 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + + s.time = s_time; + + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + + { + fuint8 temp; + CALC_STATUS( temp ); + r.status = temp; + } + + this->state_ = s; + this->state = &this->state_; + + return illegal_encountered; +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h new file mode 100644 index 00000000..437d0908 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Cpu.h @@ -0,0 +1,125 @@ +// PC Engine CPU emulator for use with HES music files + +// Game_Music_Emu 0.5.2 +#ifndef HES_CPU_H +#define HES_CPU_H + +#include "blargg_common.h" +#include + +typedef blargg_long hes_time_t; // clock cycle count +typedef unsigned hes_addr_t; // 16-bit address +enum { future_hes_time = LONG_MAX / 2 + 1 }; + +class Hes_Cpu { +public: + typedef BOOST::uint8_t uint8_t; + + void reset(); + + enum { page_size = 0x2000 }; + enum { page_shift = 13 }; + enum { page_count = 8 }; + void set_mmr( int reg, int bank ); + + uint8_t const* get_code( hes_addr_t ); + + uint8_t ram [page_size]; + + // not kept updated during a call to run() + struct registers_t { + BOOST::uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + }; + registers_t r; + + // page mapping registers + uint8_t mmr [page_count + 1]; + + // Set end_time and run CPU from current time. Returns true if any illegal + // instructions were encountered. + bool run( hes_time_t end_time ); + + // Time of beginning of next instruction to be executed + hes_time_t time() const { return state->time + state->base; } + void set_time( hes_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + hes_time_t irq_time() const { return irq_time_; } + void set_irq_time( hes_time_t ); + + hes_time_t end_time() const { return end_time_; } + void set_end_time( hes_time_t ); + + void end_frame( hes_time_t ); + + // Attempt to execute instruction here results in CPU advancing time to + // lesser of irq_time() and end_time() (or end_time() if IRQs are + // disabled) + enum { idle_addr = 0x1FFF }; + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + +public: + Hes_Cpu() { state = &state_; } + enum { irq_inhibit = 0x04 }; +private: + // noncopyable + Hes_Cpu( const Hes_Cpu& ); + Hes_Cpu& operator = ( const Hes_Cpu& ); + + struct state_t { + uint8_t const* code_map [page_count + 1]; + hes_time_t base; + blargg_long time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + hes_time_t irq_time_; + hes_time_t end_time_; + + void set_code_page( int, void const* ); + inline int update_end_time( hes_time_t end, hes_time_t irq ); +}; + +inline BOOST::uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) +{ + return state->code_map [addr >> page_shift] + addr + #if !BLARGG_NONPORTABLE + % (unsigned) page_size + #endif + ; +} + +inline int Hes_Cpu::update_end_time( hes_time_t t, hes_time_t irq ) +{ + if ( irq < t && !(r.status & irq_inhibit) ) t = irq; + int delta = state->base - t; + state->base = t; + return delta; +} + +inline void Hes_Cpu::set_irq_time( hes_time_t t ) +{ + state->time += update_end_time( end_time_, (irq_time_ = t) ); +} + +inline void Hes_Cpu::set_end_time( hes_time_t t ) +{ + state->time += update_end_time( (end_time_ = t), irq_time_ ); +} + +inline void Hes_Cpu::end_frame( hes_time_t t ) +{ + assert( state == &state_ ); + state_.base -= t; + if ( irq_time_ < future_hes_time ) irq_time_ -= t; + if ( end_time_ < future_hes_time ) end_time_ -= t; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp new file mode 100644 index 00000000..fafb2666 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.cpp @@ -0,0 +1,529 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Hes_Emu.h" + +#include "blargg_endian.h" +#include + +/* 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 timer_mask = 0x04; +int const vdp_mask = 0x02; +int const i_flag_mask = 0x04; +int const unmapped = 0xFF; + +long const period_60hz = 262 * 455L; // scanlines * clocks per scanline + +Hes_Emu::Hes_Emu() +{ + timer.raw_load = 0; + set_type( gme_hes_type ); + + static const char* const names [Hes_Apu::osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2" + }; + set_voice_names( names ); + + static int const types [Hes_Apu::osc_count] = { + wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, + mixed_type | 0, mixed_type | 1 + }; + set_voice_types( types ); + set_silence_lookahead( 6 ); + set_gain( 1.11 ); +} + +Hes_Emu::~Hes_Emu() { } + +void Hes_Emu::unload() +{ + rom.clear(); + Music_Emu::unload(); +} + +// Track info + +static byte const* copy_field( byte const* in, char* out ) +{ + if ( in ) + { + int len = 0x20; + if ( in [0x1F] && !in [0x2F] ) + len = 0x30; // fields are sometimes 16 bytes longer (ugh) + + // since text fields are where any data could be, detect non-text + // and fields with data after zero byte terminator + + int i = 0; + for ( i = 0; i < len && in [i]; i++ ) + if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text + return 0; // non-ASCII found + + for ( ; i < len; i++ ) + if ( in [i] ) + return 0; // data after terminator + + Gme_File::copy_field_( out, (char const*) in, len ); + in += len; + } + return in; +} + +static void copy_hes_fields( byte const* in, track_info_t* out ) +{ + if ( *in >= ' ' ) + { + in = copy_field( in, out->game ); + in = copy_field( in, out->author ); + in = copy_field( in, out->copyright ); + } +} + +blargg_err_t Hes_Emu::track_info_( track_info_t* out, int ) const +{ + copy_hes_fields( rom.begin() + 0x20, out ); + return 0; +} + +static blargg_err_t check_hes_header( void const* header ) +{ + if ( memcmp( header, "HESM", 4 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Hes_File : Gme_Info_ +{ + struct header_t { + char header [Hes_Emu::header_size]; + char unused [0x20]; + byte fields [0x30 * 3]; + } h; + + Hes_File() { set_type( gme_hes_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 ); + blargg_err_t err = in.read( &h, sizeof h ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + return check_hes_header( &h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_hes_fields( h.fields, out ); + return 0; + } +}; + +static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } +static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } + +gme_type_t_ const gme_hes_type [1] = { "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }; + +// Setup + +blargg_err_t Hes_Emu::load_( Data_Reader& in ) +{ + assert( offsetof (header_t,unused [4]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) ); + + RETURN_ERR( check_hes_header( header_.tag ) ); + + 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. + + long addr = get_le32( header_.addr ); + long size = get_le32( header_.size ); + long const rom_max = 0x100000; + if ( addr & ~(rom_max - 1) ) + { + set_warning( "Invalid address" ); + addr &= rom_max - 1; + } + if ( (unsigned long) (addr + size) > (unsigned long) 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 ); + + set_voice_count( apu.osc_count ); + + apu.volume( gain() ); + + return setup_buffer( 7159091 ); +} + +void Hes_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); +} + +void Hes_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + apu.osc_output( i, center, left, right ); +} + +// Emulation + +void Hes_Emu::recalc_timer_load() +{ + timer.load = timer.raw_load * timer_base + 1; +} + +void Hes_Emu::set_tempo_( double t ) +{ + play_period = hes_time_t (period_60hz / t); + timer_base = int (1024 / t); + recalc_timer_load(); +} + +blargg_err_t Hes_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( ram, 0, sizeof ram ); // some HES music relies on zero fill + memset( sgx, 0, sizeof sgx ); + + apu.reset(); + cpu::reset(); + + for ( unsigned i = 0; i < sizeof header_.banks; i++ ) + set_mmr( i, header_.banks [i] ); + set_mmr( page_count, 0xFF ); // unmapped beyond end of address space + + irq.disables = timer_mask | vdp_mask; + irq.timer = future_hes_time; + irq.vdp = future_hes_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; + r.sp = 0xFD; + r.pc = get_le16( header_.init_addr ); + r.a = track; + + recalc_timer_load(); + last_frame_hook = 0; + + return 0; +} + +// Hardware + +void Hes_Emu::cpu_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( 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_Emu::cpu_write_( hes_addr_t addr, int data ) +{ + if ( unsigned (addr - apu.start_addr) <= apu.end_addr - apu.start_addr ) + { + GME_APU_HOOK( this, addr - apu.start_addr, data ); + // avoid going way past end when a long block xfer is writing to I/O space + hes_time_t t = min( time(), end_time() + 8 ); + apu.write_data( t, addr, data ); + return; + } + + hes_time_t time = this->time(); + switch ( addr ) + { + case 0x0000: + case 0x0002: + case 0x0003: + cpu_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_Emu::cpu_read_( hes_addr_t addr ) +{ + hes_time_t time = this->time(); + addr &= page_size - 1; + switch ( addr ) + { + case 0x0000: + if ( irq.vdp > time ) + return 0; + irq.vdp = future_hes_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; + } + + #ifndef NDEBUG + case 0x1000: // I/O port + case 0x180C: // CD-ROM + case 0x180D: + break; + + default: + dprintf( "unmapped read $%04X\n", addr ); + #endif + } + + return unmapped; +} + +// see hes_cpu_io.h for core read/write functions + +// Emulation + +void Hes_Emu::run_until( hes_time_t present ) +{ + while ( vdp.next_vbl < present ) + vdp.next_vbl += play_period; + + hes_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_Emu::irq_changed() +{ + hes_time_t present = time(); + + if ( irq.timer > present ) + { + irq.timer = future_hes_time; + if ( timer.enabled && !timer.fired ) + irq.timer = present + timer.count; + } + + if ( irq.vdp > present ) + { + irq.vdp = future_hes_time; + if ( vdp.control & 0x08 ) + irq.vdp = vdp.next_vbl; + } + + hes_time_t time = future_hes_time; + if ( !(irq.disables & timer_mask) ) time = irq.timer; + if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); + + set_irq_time( time ); +} + +int Hes_Emu::cpu_done() +{ + check( time() >= end_time() || + (!(r.status & i_flag_mask) && time() >= irq_time()) ); + + if ( !(r.status & i_flag_mask) ) + { + hes_time_t present = time(); + + if ( irq.timer <= present && !(irq.disables & timer_mask) ) + { + timer.fired = true; + irq.timer = future_hes_time; + irq_changed(); // overkill, but not worth writing custom code + #if GME_FRAME_HOOK_DEFINED + { + unsigned const threshold = period_60hz / 30; + unsigned long elapsed = present - last_frame_hook; + if ( elapsed - period_60hz + threshold / 2 < threshold ) + { + last_frame_hook = present; + GME_FRAME_HOOK( this ); + } + } + #endif + return 0x0A; + } + + if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) + { + // work around for bugs with music not acknowledging VDP + //run_until( present ); + //irq.vdp = future_hes_time; + //irq_changed(); + #if GME_FRAME_HOOK_DEFINED + last_frame_hook = present; + GME_FRAME_HOOK( this ); + #endif + return 0x08; + } + } + return 0; +} + +static void adjust_time( blargg_long& time, hes_time_t delta ) +{ + if ( time < future_hes_time ) + { + time -= delta; + if ( time < 0 ) + time = 0; + } +} + +blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) +{ + blip_time_t const duration = duration_; // cache + + if ( cpu::run( duration ) ) + set_warning( "Emulation error (illegal instruction)" ); + + check( 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; + #if GME_FRAME_HOOK_DEFINED + last_frame_hook -= duration; + #endif + cpu::end_frame( duration ); + ::adjust_time( irq.timer, duration ); + ::adjust_time( irq.vdp, duration ); + apu.end_frame( duration ); + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h new file mode 100644 index 00000000..9951eb6a --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Hes_Emu.h @@ -0,0 +1,94 @@ +// TurboGrafx-16/PC Engine HES music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef HES_EMU_H +#define HES_EMU_H + +#include "Classic_Emu.h" +#include "Hes_Apu.h" +#include "Hes_Cpu.h" + +class Hes_Emu : private Hes_Cpu, public Classic_Emu { + typedef Hes_Cpu cpu; +public: + // HES file header + enum { header_size = 0x20 }; + struct header_t + { + byte tag [4]; + byte vers; + byte first_track; + byte init_addr [2]; + byte banks [8]; + byte data_tag [4]; + byte size [4]; + byte addr [4]; + byte unused [4]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_hes_type; } + +public: + Hes_Emu(); + ~Hes_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +public: private: friend class Hes_Cpu; + byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space + + int cpu_read_( hes_addr_t ); + int cpu_read( hes_addr_t ); + void cpu_write_( hes_addr_t, int data ); + void cpu_write( hes_addr_t, int ); + void cpu_write_vdp( int addr, int data ); + byte const* cpu_set_mmr( int page, int bank ); + int cpu_done(); +private: + Rom_Data rom; + header_t header_; + hes_time_t play_period; + hes_time_t last_frame_hook; + int timer_base; + + struct { + hes_time_t last_time; + blargg_long count; + blargg_long load; + int raw_load; + byte enabled; + byte fired; + } timer; + + struct { + hes_time_t next_vbl; + byte latch; + byte control; + } vdp; + + struct { + hes_time_t timer; + hes_time_t vdp; + byte disables; + } irq; + + void recalc_timer_load(); + + // large items + Hes_Apu apu; + byte sgx [3 * page_size + cpu_padding]; + + void irq_changed(); + void run_until( hes_time_t ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp new file mode 100644 index 00000000..37c4a724 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.cpp @@ -0,0 +1,1706 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +/* +Last validated with zexall 2006.11.14 2:19 PM +* Doesn't implement the R register or immediate interrupt after EI. +* Address wrap-around isn't completely correct, but is prevented from crashing emulator. +*/ + +#include "Kss_Cpu.h" + +#include "blargg_endian.h" +#include + +//#include "z80_cpu_log.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 */ + +#define SYNC_TIME() (void) (s.time = s_time) +#define RELOAD_TIME() (void) (s_time = s.time) + +// Callbacks to emulator + +#define CPU_OUT( cpu, addr, data, time )\ + kss_cpu_out( this, time, addr, data ) + +#define CPU_IN( cpu, addr, time )\ + kss_cpu_in( this, time, addr ) + +#define CPU_WRITE( cpu, addr, data, time )\ + (SYNC_TIME(), kss_cpu_write( this, addr, data )) + +#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; + +#define SZ28P( n ) szpc [n] +#define SZ28PC( n ) szpc [n] +#define SZ28C( n ) (szpc [n] & ~P04) +#define SZ28( n ) SZ28C( n ) + +#define SET_R( n ) (void) (r.r = n) +#define GET_R() (r.r) + +Kss_Cpu::Kss_Cpu() +{ + state = &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 Kss_Cpu::set_page( int i, void* write, void const* read ) +{ + blargg_long offset = KSS_CPU_PAGE_OFFSET( i * (blargg_long) page_size ); + state->write [i] = (byte *) write - offset; + state->read [i] = (byte const*) read - offset; +} + +void Kss_Cpu::reset( void* unmapped_write, void const* unmapped_read ) +{ + check( state == &state_ ); + state = &state_; + state_.time = 0; + 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 Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ) +{ + // address range must begin and end on page boundaries + require( addr % page_size == 0 ); + require( size % page_size == 0 ); + + unsigned first_page = addr / page_size; + for ( unsigned i = size / page_size; i--; ) + { + blargg_long offset = i * (blargg_long) page_size; + set_page( first_page + i, (byte*) write + offset, (byte const*) read + offset ); + } +} + +#define TIME (s_time + s.base) +#define RW_MEM( addr, rw ) (s.rw [(addr) >> page_shift] [KSS_CPU_PAGE_OFFSET( addr )]) +#define READ_PROG( addr ) RW_MEM( addr, read ) +#define READ( addr ) READ_PROG( addr ) +//#define WRITE( addr, data ) (void) (RW_MEM( addr, write ) = data) +#define WRITE( addr, data ) CPU_WRITE( this, addr, data, TIME ) +#define READ_WORD( addr ) GET_LE16( &READ( addr ) ) +#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data ) +#define IN( addr ) CPU_IN( this, addr, TIME ) +#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) + +#if BLARGG_BIG_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [n]) +#elif BLARGG_LITTLE_ENDIAN + #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) +#else + #error "Byte order of CPU must be known" +#endif + +//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) + +// help compiler see that it can just adjust stack offset, saving an extra instruction +#define R16( n, shift, offset )\ + (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) + +#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e +#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f +#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g +#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h + +// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 +static byte const ed_dd_timing [0x100] = { +//0 1 2 3 4 5 6 7 8 9 A B C D E F +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, +0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, +0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, +0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, +0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, +0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +// even on x86, using short and unsigned char was slower +typedef int fint16; +typedef unsigned fuint16; +typedef unsigned fuint8; + +bool Kss_Cpu::run( cpu_time_t end_time ) +{ + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + bool warning = false; + + typedef BOOST::int8_t int8_t; + + union { + regs_t rg; + pairs_t rp; + uint8_t r8_ [8]; // indexed + uint16_t r16_ [4]; + }; + rg = this->r.b; + + cpu_time_t s_time = s.time; + fuint16 pc = r.pc; + fuint16 sp = r.sp; + fuint16 ix = r.ix; // TODO: keep in memory for direct access? + fuint16 iy = r.iy; + int flags = r.b.flags; + + goto loop; +jr_not_taken: + s_time -= 5; + goto loop; +call_not_taken: + s_time -= 7; +jp_not_taken: + pc += 2; +loop: + + check( (unsigned long) pc < 0x10000 ); + check( (unsigned long) sp < 0x10000 ); + check( (unsigned) flags < 0x100 ); + check( (unsigned) ix < 0x10000 ); + check( (unsigned) iy < 0x10000 ); + + uint8_t const* instr = s.read [pc >> page_shift]; +#define GET_ADDR() GET_LE16( instr ) + + fuint8 opcode; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + opcode = instr [pc]; + pc++; + instr += pc; + #else + instr += KSS_CPU_PAGE_OFFSET( pc ); + opcode = *instr++; + pc++; + #endif + + static byte const base_timing [0x100] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 + 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 + 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 + 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 + 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B + 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C + 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D + 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E + 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F + }; + + fuint16 data; + data = base_timing [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = READ_PROG( pc ); + + #ifdef Z80_CPU_LOG_H + //log_opcode( opcode, READ_PROG( pc ) ); + z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); + z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), + READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Common + + case 0x00: // NOP + CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. + goto loop; + + case 0x08:{// EX AF,AF' + int temp = r.alt.b.a; + r.alt.b.a = rg.a; + rg.a = temp; + + temp = r.alt.b.flags; + r.alt.b.flags = flags; + flags = temp; + goto loop; + } + + case 0xD3: // OUT (imm),A + pc++; + OUT( data + rg.a * 0x100, rg.a ); + goto loop; + + case 0x2E: // LD L,imm + pc++; + rg.l = data; + goto loop; + + case 0x3E: // LD A,imm + pc++; + rg.a = data; + goto loop; + + case 0x3A:{// LD A,(addr) + fuint16 addr = GET_ADDR(); + pc += 2; + rg.a = READ( addr ); + goto loop; + } + +// Conditional + +#define ZERO (flags & Z40) +#define CARRY (flags & C01) +#define EVEN (flags & P04) +#define MINUS (flags & S80) + +// JR +// TODO: more efficient way to handle negative branch that wraps PC around +#define JR( cond ) {\ + int offset = (BOOST::int8_t) data;\ + pc++;\ + if ( !(cond) )\ + goto jr_not_taken;\ + pc = uint16_t (pc + offset);\ + goto loop;\ +} + + case 0x20: JR( !ZERO ) // JR NZ,disp + case 0x28: JR( ZERO ) // JR Z,disp + case 0x30: JR( !CARRY ) // JR NC,disp + case 0x38: JR( CARRY ) // JR C,disp + case 0x18: JR( true ) // JR disp + + case 0x10:{// DJNZ disp + int temp = rg.b - 1; + rg.b = temp; + JR( temp ) + } + +// JP +#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; + + case 0xC2: JP( !ZERO ) // JP NZ,addr + case 0xCA: JP( ZERO ) // JP Z,addr + case 0xD2: JP( !CARRY ) // JP NC,addr + case 0xDA: JP( CARRY ) // JP C,addr + case 0xE2: JP( !EVEN ) // JP PO,addr + case 0xEA: JP( EVEN ) // JP PE,addr + case 0xF2: JP( !MINUS ) // JP P,addr + case 0xFA: JP( MINUS ) // JP M,addr + + case 0xC3: // JP addr + pc = GET_ADDR(); + goto loop; + + case 0xE9: // JP HL + pc = rp.hl; + goto loop; + +// RET +#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; + + case 0xC0: RET( !ZERO ) // RET NZ + case 0xC8: RET( ZERO ) // RET Z + case 0xD0: RET( !CARRY ) // RET NC + case 0xD8: RET( CARRY ) // RET C + case 0xE0: RET( !EVEN ) // RET PO + case 0xE8: RET( EVEN ) // RET PE + case 0xF0: RET( !MINUS ) // RET P + case 0xF8: RET( MINUS ) // RET M + + case 0xC9: // RET + ret_taken: + pc = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// CALL +#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; + + case 0xC4: CALL( !ZERO ) // CALL NZ,addr + case 0xCC: CALL( ZERO ) // CALL Z,addr + case 0xD4: CALL( !CARRY ) // CALL NC,addr + case 0xDC: CALL( CARRY ) // CALL C,addr + case 0xE4: CALL( !EVEN ) // CALL PO,addr + case 0xEC: CALL( EVEN ) // CALL PE,addr + case 0xF4: CALL( !MINUS ) // CALL P,addr + case 0xFC: CALL( MINUS ) // CALL M,addr + + case 0xCD:{// CALL addr + call_taken: + fuint16 addr = pc + 2; + pc = GET_ADDR(); + sp = uint16_t (sp - 2); + WRITE_WORD( sp, addr ); + goto loop; + } + + case 0xFF: // RST + if ( pc > idle_addr ) + goto hit_idle_addr; + CASE7( C7, CF, D7, DF, E7, EF, F7 ): + data = pc; + pc = opcode & 0x38; + goto push_data; + +// PUSH/POP + case 0xF5: // PUSH AF + data = rg.a * 0x100u + flags; + goto push_data; + + case 0xC5: // PUSH BC + case 0xD5: // PUSH DE + case 0xE5: // PUSH HL + data = R16( opcode, 4, 0xC5 ); + push_data: + sp = uint16_t (sp - 2); + WRITE_WORD( sp, data ); + goto loop; + + case 0xF1: // POP AF + flags = READ( sp ); + rg.a = READ( sp + 1 ); + sp = uint16_t (sp + 2); + goto loop; + + case 0xC1: // POP BC + case 0xD1: // POP DE + case 0xE1: // POP HL + R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto loop; + +// ADC/ADD/SBC/SUB + case 0x96: // SUB (HL) + case 0x86: // ADD (HL) + flags &= ~C01; + case 0x9E: // SBC (HL) + case 0x8E: // ADC (HL) + data = READ( rp.hl ); + goto adc_data; + + case 0xD6: // SUB A,imm + case 0xC6: // ADD imm + flags &= ~C01; + case 0xDE: // SBC A,imm + case 0xCE: // ADC imm + pc++; + goto adc_data; + + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r + flags &= ~C01; + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r + data = R8( opcode & 7, 0 ); + adc_data: { + int result = data + (flags & C01); + data ^= rg.a; + flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes + if ( flags ) + result = -result; + result += rg.a; + data ^= result; + flags |=(data & H10) | + ((data - -0x80) >> 6 & V04) | + SZ28C( result & 0x1FF ); + rg.a = result; + goto loop; + } + +// CP + case 0xBE: // CP (HL) + data = READ( rp.hl ); + goto cp_data; + + case 0xFE: // CP imm + pc++; + goto cp_data; + + CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r + data = R8( opcode, 0xB8 ); + cp_data: { + int result = rg.a - data; + flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); + data ^= rg.a; + flags |=(((result ^ rg.a) & data) >> 5 & V04) | + (((data & H10) ^ result) & (S80 | H10)); + if ( (uint8_t) result ) + goto loop; + flags |= Z40; + goto loop; + } + +// ADD HL,rp + + case 0x39: // ADD HL,SP + data = sp; + goto add_hl_data; + + case 0x09: // ADD HL,BC + case 0x19: // ADD HL,DE + case 0x29: // ADD HL,HL + data = R16( opcode, 4, 0x09 ); + add_hl_data: { + blargg_ulong sum = rp.hl + data; + data ^= rp.hl; + rp.hl = sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((data ^ sum) >> 8 & H10); + goto loop; + } + + case 0x27:{// DAA + int a = rg.a; + if ( a > 0x99 ) + flags |= C01; + + int adjust = 0x60 & -(flags & C01); + + if ( flags & H10 || (a & 0x0F) > 9 ) + adjust |= 0x06; + + if ( flags & N02 ) + adjust = -adjust; + a += adjust; + + flags = (flags & (C01 | N02)) | + ((rg.a ^ a) & H10) | + SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + /* + case 0x27:{// DAA + // more optimized, but probably not worth the obscurity + int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags + int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 + + if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 + adjust |= 0x06; + + if ( f & N02 ) + adjust = -adjust; + int a = rg.a + adjust; + + flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); + rg.a = a; + goto loop; + } + */ + +// INC/DEC + case 0x34: // INC (HL) + data = READ( rp.hl ) + 1; + WRITE( rp.hl, data ); + goto inc_set_flags; + + CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r + data = ++R8( opcode >> 3, 0 ); + inc_set_flags: + flags = (flags & C01) | + (((data & 0x0F) - 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x80 ) + goto loop; + flags |= V04; + goto loop; + + case 0x35: // DEC (HL) + data = READ( rp.hl ) - 1; + WRITE( rp.hl, data ); + goto dec_set_flags; + + CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r + data = --R8( opcode >> 3, 0 ); + dec_set_flags: + flags = (flags & C01) | N02 | + (((data & 0x0F) + 1) & H10) | + SZ28( (uint8_t) data ); + if ( data != 0x7F ) + goto loop; + flags |= V04; + goto loop; + + case 0x03: // INC BC + case 0x13: // INC DE + case 0x23: // INC HL + R16( opcode, 4, 0x03 )++; + goto loop; + + case 0x33: // INC SP + sp = uint16_t (sp + 1); + goto loop; + + case 0x0B: // DEC BC + case 0x1B: // DEC DE + case 0x2B: // DEC HL + R16( opcode, 4, 0x0B )--; + goto loop; + + case 0x3B: // DEC SP + sp = uint16_t (sp - 1); + goto loop; + +// AND + case 0xA6: // AND (HL) + data = READ( rp.hl ); + goto and_data; + + case 0xE6: // AND imm + pc++; + goto and_data; + + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r + data = R8( opcode, 0xA0 ); + and_data: + rg.a &= data; + flags = SZ28P( rg.a ) | H10; + goto loop; + +// OR + case 0xB6: // OR (HL) + data = READ( rp.hl ); + goto or_data; + + case 0xF6: // OR imm + pc++; + goto or_data; + + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r + data = R8( opcode, 0xB0 ); + or_data: + rg.a |= data; + flags = SZ28P( rg.a ); + goto loop; + +// XOR + case 0xAE: // XOR (HL) + data = READ( rp.hl ); + goto xor_data; + + case 0xEE: // XOR imm + pc++; + goto xor_data; + + CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r + data = R8( opcode, 0xA8 ); + xor_data: + rg.a ^= data; + flags = SZ28P( rg.a ); + goto loop; + +// LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r + WRITE( rp.hl, R8( opcode, 0x70 ) ); + goto loop; + + CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r + CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r + CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r + CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r + CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r + CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r + CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r + R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); + goto loop; + + CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm + R8( opcode >> 3, 0 ) = data; + pc++; + goto loop; + + case 0x36: // LD (HL),imm + pc++; + WRITE( rp.hl, data ); + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) + R8( opcode >> 3, 8 ) = READ( rp.hl ); + goto loop; + + case 0x01: // LD rp,imm + case 0x11: + case 0x21: + R16( opcode, 4, 0x01 ) = GET_ADDR(); + pc += 2; + goto loop; + + case 0x31: // LD sp,imm + sp = GET_ADDR(); + pc += 2; + goto loop; + + case 0x2A:{// LD HL,(addr) + fuint16 addr = GET_ADDR(); + pc += 2; + rp.hl = READ_WORD( addr ); + goto loop; + } + + case 0x32:{// LD (addr),A + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE( addr, rg.a ); + goto loop; + } + + case 0x22:{// LD (addr),HL + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, rp.hl ); + goto loop; + } + + case 0x02: // LD (BC),A + case 0x12: // LD (DE),A + WRITE( R16( opcode, 4, 0x02 ), rg.a ); + goto loop; + + case 0x0A: // LD A,(BC) + case 0x1A: // LD A,(DE) + rg.a = READ( R16( opcode, 4, 0x0A ) ); + goto loop; + + case 0xF9: // LD SP,HL + sp = rp.hl; + goto loop; + +// Rotate + + case 0x07:{// RLCA + fuint16 temp = rg.a; + temp = (temp << 1) | (temp >> 7); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08 | C01)); + rg.a = temp; + goto loop; + } + + case 0x0F:{// RRCA + fuint16 temp = rg.a; + flags = (flags & (S80 | Z40 | P04)) | + (temp & C01); + temp = (temp << 7) | (temp >> 1); + flags |= temp & (F20 | F08); + rg.a = temp; + goto loop; + } + + case 0x17:{// RLA + blargg_ulong temp = (rg.a << 1) | (flags & C01); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (temp >> 8); + rg.a = temp; + goto loop; + } + + case 0x1F:{// RRA + fuint16 temp = (flags << 7) | (rg.a >> 1); + flags = (flags & (S80 | Z40 | P04)) | + (temp & (F20 | F08)) | + (rg.a & C01); + rg.a = temp; + goto loop; + } + +// Misc + case 0x2F:{// CPL + fuint16 temp = ~rg.a; + flags = (flags & (S80 | Z40 | P04 | C01)) | + (temp & (F20 | F08)) | + (H10 | N02); + rg.a = temp; + goto loop; + } + + case 0x3F:{// CCF + flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | + (flags << 4 & H10) | + (rg.a & (F20 | F08)); + goto loop; + } + + case 0x37: // SCF + flags = (flags & (S80 | Z40 | P04)) | C01 | + (rg.a & (F20 | F08)); + goto loop; + + case 0xDB: // IN A,(imm) + pc++; + rg.a = IN( data + rg.a * 0x100 ); + goto loop; + + case 0xE3:{// EX (SP),HL + fuint16 temp = READ_WORD( sp ); + WRITE_WORD( sp, rp.hl ); + rp.hl = temp; + goto loop; + } + + case 0xEB:{// EX DE,HL + fuint16 temp = rp.hl; + rp.hl = rp.de; + rp.de = temp; + goto loop; + } + + case 0xD9:{// EXX DE,HL + fuint16 temp = r.alt.w.bc; + r.alt.w.bc = rp.bc; + rp.bc = temp; + + temp = r.alt.w.de; + r.alt.w.de = rp.de; + rp.de = temp; + + temp = r.alt.w.hl; + r.alt.w.hl = rp.hl; + rp.hl = temp; + goto loop; + } + + case 0xF3: // DI + r.iff1 = 0; + r.iff2 = 0; + goto loop; + + case 0xFB: // EI + r.iff1 = 1; + r.iff2 = 1; + // TODO: delayed effect + goto loop; + + case 0x76: // HALT + goto halt; + +//////////////////////////////////////// CB prefix + { + case 0xCB: + unsigned data2; + data2 = instr [1]; + pc++; + switch ( data ) + { + + // Rotate left + + #define RLC( read, write ) {\ + fuint8 result = read;\ + result = uint8_t (result << 1) | (result >> 7);\ + flags = SZ28P( result ) | (result & C01);\ + write;\ + goto loop;\ + } + + case 0x06: // RLC (HL) + s_time += 7; + data = rp.hl; + rlc_data_addr: + RLC( READ( data ), WRITE( data, result ) ) + + CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r + uint8_t& reg = R8( data, 0 ); + RLC( reg, reg = result ) + } + + #define RL( read, write ) {\ + fuint16 result = (read << 1) | (flags & C01);\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x16: // RL (HL) + s_time += 7; + data = rp.hl; + rl_data_addr: + RL( READ( data ), WRITE( data, result ) ) + + CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r + uint8_t& reg = R8( data, 0x10 ); + RL( reg, reg = result ) + } + + #define SLA( read, add, write ) {\ + fuint16 result = (read << 1) | add;\ + flags = SZ28PC( result );\ + write;\ + goto loop;\ + } + + case 0x26: // SLA (HL) + s_time += 7; + data = rp.hl; + sla_data_addr: + SLA( READ( data ), 0, WRITE( data, result ) ) + + CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r + uint8_t& reg = R8( data, 0x20 ); + SLA( reg, 0, reg = result ) + } + + case 0x36: // SLL (HL) + s_time += 7; + data = rp.hl; + sll_data_addr: + SLA( READ( data ), 1, WRITE( data, result ) ) + + CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r + uint8_t& reg = R8( data, 0x30 ); + SLA( reg, 1, reg = result ) + } + + // Rotate right + + #define RRC( read, write ) {\ + fuint8 result = read;\ + flags = result & C01;\ + result = uint8_t (result << 7) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x0E: // RRC (HL) + s_time += 7; + data = rp.hl; + rrc_data_addr: + RRC( READ( data ), WRITE( data, result ) ) + + CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r + uint8_t& reg = R8( data, 0x08 ); + RRC( reg, reg = result ) + } + + #define RR( read, write ) {\ + fuint8 result = read;\ + fuint8 temp = result & C01;\ + result = uint8_t (flags << 7) | (result >> 1);\ + flags = SZ28P( result ) | temp;\ + write;\ + goto loop;\ + } + + case 0x1E: // RR (HL) + s_time += 7; + data = rp.hl; + rr_data_addr: + RR( READ( data ), WRITE( data, result ) ) + + CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r + uint8_t& reg = R8( data, 0x18 ); + RR( reg, reg = result ) + } + + #define SRA( read, write ) {\ + fuint8 result = read;\ + flags = result & C01;\ + result = (result & 0x80) | (result >> 1);\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x2E: // SRA (HL) + data = rp.hl; + s_time += 7; + sra_data_addr: + SRA( READ( data ), WRITE( data, result ) ) + + CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r + uint8_t& reg = R8( data, 0x28 ); + SRA( reg, reg = result ) + } + + #define SRL( read, write ) {\ + fuint8 result = read;\ + flags = result & C01;\ + result >>= 1;\ + flags |= SZ28P( result );\ + write;\ + goto loop;\ + } + + case 0x3E: // SRL (HL) + s_time += 7; + data = rp.hl; + srl_data_addr: + SRL( READ( data ), WRITE( data, result ) ) + + CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r + uint8_t& reg = R8( data, 0x38 ); + SRL( reg, reg = result ) + } + + // BIT + { + unsigned temp; + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) + s_time += 4; + temp = READ( rp.hl ); + flags &= C01; + goto bit_temp; + CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r + CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r + CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r + CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r + CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r + CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r + CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r + temp = R8( data & 7, 0 ); + flags = (flags & C01) | (temp & (F20 | F08)); + bit_temp: + int masked = temp & 1 << (data >> 3 & 7); + flags |=(masked & S80) | H10 | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + // SET/RES + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) + s_time += 7; + int temp = READ( rp.hl ); + int bit = 1 << (data >> 3 & 7); + temp |= bit; // SET + if ( !(data & 0x40) ) + temp ^= bit; // RES + WRITE( rp.hl, temp ); + goto loop; + } + + CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r + CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r + CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r + CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r + CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r + CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r + CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r + CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r + R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); + goto loop; + + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r + CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r + CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r + CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r + CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r + R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); + goto loop; + } + assert( false ); + } + +#undef GET_ADDR +#define GET_ADDR() GET_LE16( instr + 1 ) + +//////////////////////////////////////// ED prefix + { + case 0xED: + pc++; + s_time += ed_dd_timing [data] >> 4; + switch ( data ) + { + { + blargg_ulong temp; + case 0x72: // SBC HL,SP + case 0x7A: // ADC HL,SP + temp = sp; + if ( 0 ) + case 0x42: // SBC HL,BC + case 0x52: // SBC HL,DE + case 0x62: // SBC HL,HL + case 0x4A: // ADC HL,BC + case 0x5A: // ADC HL,DE + case 0x6A: // ADC HL,HL + temp = R16( data >> 3 & 6, 1, 0 ); + blargg_ulong sum = temp + (flags & C01); + flags = ~data >> 2 & N02; + if ( flags ) + sum = -sum; + sum += rp.hl; + temp ^= rp.hl; + temp ^= sum; + flags |=(sum >> 16 & C01) | + (temp >> 8 & H10) | + (sum >> 8 & (S80 | F20 | F08)) | + ((temp - -0x8000) >> 14 & V04); + rp.hl = sum; + if ( (uint16_t) sum ) + goto loop; + flags |= Z40; + goto loop; + } + + CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) + int temp = IN( rp.bc ); + R8( data >> 3, 8 ) = temp; + flags = (flags & C01) | SZ28P( temp ); + goto loop; + } + + case 0x71: // OUT (C),0 + rg.flags = 0; + CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r + OUT( rp.bc, R8( data >> 3, 8 ) ); + goto loop; + + { + unsigned temp; + case 0x73: // LD (ADDR),SP + temp = sp; + if ( 0 ) + case 0x43: // LD (ADDR),BC + case 0x53: // LD (ADDR),DE + temp = R16( data, 4, 0x43 ); + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, temp ); + goto loop; + } + + case 0x4B: // LD BC,(ADDR) + case 0x5B:{// LD DE,(ADDR) + fuint16 addr = GET_ADDR(); + pc += 2; + R16( data, 4, 0x4B ) = READ_WORD( addr ); + goto loop; + } + + case 0x7B:{// LD SP,(ADDR) + fuint16 addr = GET_ADDR(); + pc += 2; + sp = READ_WORD( addr ); + goto loop; + } + + case 0x67:{// RRD + fuint8 temp = READ( rp.hl ); + WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); + temp = (rg.a & 0xF0) | (temp & 0x0F); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + case 0x6F:{// RLD + fuint8 temp = READ( rp.hl ); + WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); + temp = (rg.a & 0xF0) | (temp >> 4); + flags = (flags & C01) | SZ28P( temp ); + rg.a = temp; + goto loop; + } + + CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG + opcode = 0x10; // flag to do SBC instead of ADC + flags &= ~C01; + data = rg.a; + rg.a = 0; + goto adc_data; + + { + int inc; + case 0xA9: // CPD + case 0xB9: // CPDR + inc = -1; + if ( 0 ) + case 0xA1: // CPI + case 0xB1: // CPIR + inc = +1; + fuint16 addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int result = rg.a - temp; + flags = (flags & C01) | N02 | + ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); + + if ( !(uint8_t) result ) flags |= Z40; + result -= (flags & H10) >> 4; + flags |= result & F08; + flags |= result << 4 & F20; + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( flags & Z40 || data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xA8: // LDD + case 0xB8: // LDDR + inc = -1; + if ( 0 ) + case 0xA0: // LDI + case 0xB0: // LDIR + inc = +1; + fuint16 addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + addr = rp.de; + rp.de = addr + inc; + WRITE( addr, temp ); + + temp += rg.a; + flags = (flags & (S80 | Z40 | C01)) | + (temp & F08) | (temp << 4 & F20); + if ( !--rp.bc ) + goto loop; + + flags |= V04; + if ( data < 0xB0 ) + goto loop; + + pc -= 2; + s_time += 5; + goto loop; + } + + { + int inc; + case 0xAB: // OUTD + case 0xBB: // OTDR + inc = -1; + if ( 0 ) + case 0xA3: // OUTI + case 0xB3: // OTIR + inc = +1; + fuint16 addr = rp.hl; + rp.hl = addr + inc; + int temp = READ( addr ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + OUT( rp.bc, temp ); + goto loop; + } + + { + int inc; + case 0xAA: // IND + case 0xBA: // INDR + inc = -1; + if ( 0 ) + case 0xA2: // INI + case 0xB2: // INIR + inc = +1; + + fuint16 addr = rp.hl; + rp.hl = addr + inc; + + int temp = IN( rp.bc ); + + int b = --rg.b; + flags = (temp >> 6 & N02) | SZ28( b ); + if ( b && data >= 0xB0 ) + { + pc -= 2; + s_time += 5; + } + + WRITE( addr, temp ); + goto loop; + } + + case 0x47: // LD I,A + r.i = rg.a; + goto loop; + + case 0x4F: // LD R,A + SET_R( rg.a ); + dprintf( "LD R,A not supported\n" ); + warning = true; + goto loop; + + case 0x57: // LD A,I + rg.a = r.i; + goto ld_ai_common; + + case 0x5F: // LD A,R + rg.a = GET_R(); + dprintf( "LD A,R not supported\n" ); + warning = true; + ld_ai_common: + flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); + goto loop; + + CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN + r.iff1 = r.iff2; + goto ret_taken; + + case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 + r.im = 0; + goto loop; + + case 0x56: case 0x76: // IM 1 + r.im = 1; + goto loop; + + case 0x5E: case 0x7E: // IM 2 + r.im = 2; + goto loop; + + default: + dprintf( "Opcode $ED $%02X not supported\n", data ); + warning = true; + goto loop; + } + assert( false ); + } + +//////////////////////////////////////// DD/FD prefix + { + fuint16 ixy; + case 0xDD: + ixy = ix; + goto ix_prefix; + case 0xFD: + ixy = iy; + ix_prefix: + pc++; + unsigned data2 = READ_PROG( pc ); + s_time += ed_dd_timing [data] & 0x0F; + switch ( data ) + { + // TODO: more efficient way of avoid negative address + // TODO: avoid using this as argument to READ() since it is evaluated twice + #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) + + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; + + // ADD/ADC/SUB/SBC + + case 0x96: // SUB (IXY+disp) + case 0x86: // ADD (IXY+disp) + flags &= ~C01; + case 0x9E: // SBC (IXY+disp) + case 0x8E: // ADC (IXY+disp) + pc++; + opcode = data; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto adc_data; + + case 0x94: // SUB HXY + case 0x84: // ADD HXY + flags &= ~C01; + case 0x9C: // SBC HXY + case 0x8C: // ADC HXY + opcode = data; + data = ixy >> 8; + goto adc_data; + + case 0x95: // SUB LXY + case 0x85: // ADD LXY + flags &= ~C01; + case 0x9D: // SBC LXY + case 0x8D: // ADC LXY + opcode = data; + data = (uint8_t) ixy; + goto adc_data; + + { + unsigned temp; + case 0x39: // ADD IXY,SP + temp = sp; + goto add_ixy_data; + + case 0x29: // ADD IXY,HL + temp = ixy; + goto add_ixy_data; + + case 0x09: // ADD IXY,BC + case 0x19: // ADD IXY,DE + temp = R16( data, 4, 0x09 ); + add_ixy_data: { + blargg_ulong sum = ixy + temp; + temp ^= ixy; + ixy = (uint16_t) sum; + flags = (flags & (S80 | Z40 | V04)) | + (sum >> 16) | + (sum >> 8 & (F20 | F08)) | + ((temp ^ sum) >> 8 & H10); + goto set_ixy; + } + } + + // AND + case 0xA6: // AND (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto and_data; + + case 0xA4: // AND HXY + data = ixy >> 8; + goto and_data; + + case 0xA5: // AND LXY + data = (uint8_t) ixy; + goto and_data; + + // OR + case 0xB6: // OR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto or_data; + + case 0xB4: // OR HXY + data = ixy >> 8; + goto or_data; + + case 0xB5: // OR LXY + data = (uint8_t) ixy; + goto or_data; + + // XOR + case 0xAE: // XOR (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto xor_data; + + case 0xAC: // XOR HXY + data = ixy >> 8; + goto xor_data; + + case 0xAD: // XOR LXY + data = (uint8_t) ixy; + goto xor_data; + + // CP + case 0xBE: // CP (IXY+disp) + pc++; + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto cp_data; + + case 0xBC: // CP HXY + data = ixy >> 8; + goto cp_data; + + case 0xBD: // CP LXY + data = (uint8_t) ixy; + goto cp_data; + + // LD + CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r + data = R8( data, 0x70 ); + if ( 0 ) + case 0x36: // LD (IXY+disp),imm + pc++, data = READ_PROG( pc ); + pc++; + WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); + goto loop; + + CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY + R8( data >> 3, 8 ) = ixy >> 8; + goto loop; + + case 0x64: // LD HXY,HXY + case 0x6D: // LD LXY,LXY + goto loop; + + CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY + R8( data >> 3, 8 ) = ixy; + goto loop; + + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) + pc++; + R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); + goto loop; + + case 0x26: // LD HXY,imm + pc++; + goto ld_hxy_data; + + case 0x65: // LD HXY,LXY + data2 = (uint8_t) ixy; + goto ld_hxy_data; + + CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r + data2 = R8( data, 0x60 ); + ld_hxy_data: + ixy = (uint8_t) ixy | (data2 << 8); + goto set_ixy; + + case 0x2E: // LD LXY,imm + pc++; + goto ld_lxy_data; + + case 0x6C: // LD LXY,HXY + data2 = ixy >> 8; + goto ld_lxy_data; + + CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r + data2 = R8( data, 0x68 ); + ld_lxy_data: + ixy = (ixy & 0xFF00) | data2; + set_ixy: + if ( opcode == 0xDD ) + { + ix = ixy; + goto loop; + } + iy = ixy; + goto loop; + + case 0xF9: // LD SP,IXY + sp = ixy; + goto loop; + + case 0x22:{// LD (ADDR),IXY + fuint16 addr = GET_ADDR(); + pc += 2; + WRITE_WORD( addr, ixy ); + goto loop; + } + + case 0x21: // LD IXY,imm + ixy = GET_ADDR(); + pc += 2; + goto set_ixy; + + case 0x2A:{// LD IXY,(addr) + fuint16 addr = GET_ADDR(); + ixy = READ_WORD( addr ); + pc += 2; + goto set_ixy; + } + + // DD/FD CB prefix + case 0xCB: { + data = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data2 = READ_PROG( pc ); + pc++; + switch ( data2 ) + { + case 0x06: goto rlc_data_addr; // RLC (IXY) + case 0x16: goto rl_data_addr; // RL (IXY) + case 0x26: goto sla_data_addr; // SLA (IXY) + case 0x36: goto sll_data_addr; // SLL (IXY) + case 0x0E: goto rrc_data_addr; // RRC (IXY) + case 0x1E: goto rr_data_addr; // RR (IXY) + case 0x2E: goto sra_data_addr; // SRA (IXY) + case 0x3E: goto srl_data_addr; // SRL (IXY) + + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) + fuint8 temp = READ( data ); + int masked = temp & 1 << (data2 >> 3 & 7); + flags = (flags & C01) | H10 | + (masked & S80) | + ((masked - 1) >> 8 & (Z40 | P04)); + goto loop; + } + + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) + CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) + int temp = READ( data ); + int bit = 1 << (data2 >> 3 & 7); + temp |= bit; // SET + if ( !(data2 & 0x40) ) + temp ^= bit; // RES + WRITE( data, temp ); + goto loop; + } + + default: + dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); + warning = true; + goto loop; + } + assert( false ); + } + + // INC/DEC + case 0x23: // INC IXY + ixy = uint16_t (ixy + 1); + goto set_ixy; + + case 0x2B: // DEC IXY + ixy = uint16_t (ixy - 1); + goto set_ixy; + + case 0x34: // INC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) + 1; + WRITE( ixy, data ); + goto inc_set_flags; + + case 0x35: // DEC (IXY+disp) + ixy = IXY_DISP( ixy, (int8_t) data2 ); + pc++; + data = READ( ixy ) - 1; + WRITE( ixy, data ); + goto dec_set_flags; + + case 0x24: // INC HXY + ixy = uint16_t (ixy + 0x100); + data = ixy >> 8; + goto inc_xy_common; + + case 0x2C: // INC LXY + data = uint8_t (ixy + 1); + ixy = (ixy & 0xFF00) | data; + inc_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto inc_set_flags; + } + iy = ixy; + goto inc_set_flags; + + case 0x25: // DEC HXY + ixy = uint16_t (ixy - 0x100); + data = ixy >> 8; + goto dec_xy_common; + + case 0x2D: // DEC LXY + data = uint8_t (ixy - 1); + ixy = (ixy & 0xFF00) | data; + dec_xy_common: + if ( opcode == 0xDD ) + { + ix = ixy; + goto dec_set_flags; + } + iy = ixy; + goto dec_set_flags; + + // PUSH/POP + case 0xE5: // PUSH IXY + data = ixy; + goto push_data; + + case 0xE1:{// POP IXY + ixy = READ_WORD( sp ); + sp = uint16_t (sp + 2); + goto set_ixy; + } + + // Misc + + case 0xE9: // JP (IXY) + pc = ixy; + goto loop; + + case 0xE3:{// EX (SP),IXY + fuint16 temp = READ_WORD( sp ); + WRITE_WORD( sp, ixy ); + ixy = temp; + goto set_ixy; + } + + default: + dprintf( "Unnecessary DD/FD prefix encountered\n" ); + warning = true; + pc--; + goto loop; + } + assert( false ); + } + + } + dprintf( "Unhandled main opcode: $%02X\n", opcode ); + assert( false ); + +hit_idle_addr: + s_time -= 11; + goto out_of_time; +halt: + s_time &= 3; // increment by multiple of 4 +out_of_time: + pc--; + + s.time = s_time; + rg.flags = flags; + r.ix = ix; + r.iy = iy; + r.sp = sp; + r.pc = pc; + this->r.b = rg; + this->state_ = s; + this->state = &this->state_; + + return warning; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h new file mode 100644 index 00000000..6297d100 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Cpu.h @@ -0,0 +1,124 @@ +// Z80 CPU emulator + +// Game_Music_Emu 0.5.2 +#ifndef KSS_CPU_H +#define KSS_CPU_H + +#include "blargg_endian.h" + +typedef blargg_long cpu_time_t; + +// must be defined by caller +void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); +int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); +void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); + +class Kss_Cpu { +public: + typedef BOOST::uint8_t uint8_t; + + // Clear registers and map all pages to unmapped + void reset( void* unmapped_write, void const* unmapped_read ); + + // Map memory. Start and size must be multiple of page_size. + enum { page_size = 0x2000 }; + void map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ); + + // Map address to page + uint8_t* write( unsigned addr ); + uint8_t const* read( unsigned addr ); + + // Run until specified time is reached. Returns true if suspicious/unsupported + // instruction was encountered at any point during run. + bool run( cpu_time_t end_time ); + + // Time of beginning of next instruction + cpu_time_t time() const { return state->time + state->base; } + + // Alter current time. Not supported during run() call. + void set_time( cpu_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + typedef BOOST::uint16_t uint16_t; + + #if BLARGG_BIG_ENDIAN + struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; + #else + struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; + #endif + BOOST_STATIC_ASSERT( sizeof (regs_t) == 8 ); + + struct pairs_t { uint16_t bc, de, hl, fa; }; + + // Registers are not updated until run() returns + struct registers_t { + uint16_t pc; + uint16_t sp; + uint16_t ix; + uint16_t iy; + union { + regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a + pairs_t w; // w.bc, w.de, w.hl. w.fa + }; + union { + regs_t b; + pairs_t w; + } alt; + uint8_t iff1; + uint8_t iff2; + uint8_t r; + uint8_t i; + uint8_t im; + }; + //registers_t r; (below for efficiency) + + enum { idle_addr = 0xFFFF }; + + // can read this far past end of a page + enum { cpu_padding = 0x100 }; + +public: + Kss_Cpu(); + enum { page_shift = 13 }; + enum { page_count = 0x10000 >> page_shift }; +private: + uint8_t szpc [0x200]; + cpu_time_t end_time_; + struct state_t { + uint8_t const* read [page_count + 1]; + uint8_t * write [page_count + 1]; + cpu_time_t base; + cpu_time_t time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + void set_end_time( cpu_time_t t ); + void set_page( int i, void* write, void const* read ); +public: + registers_t r; +}; + +#if BLARGG_NONPORTABLE + #define KSS_CPU_PAGE_OFFSET( addr ) (addr) +#else + #define KSS_CPU_PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +inline BOOST::uint8_t* Kss_Cpu::write( unsigned addr ) +{ + return state->write [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); +} + +inline BOOST::uint8_t const* Kss_Cpu::read( unsigned addr ) +{ + return state->read [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); +} + +inline void Kss_Cpu::set_end_time( cpu_time_t t ) +{ + cpu_time_t delta = state->base - t; + state->base = t; + state->time += delta; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp new file mode 100644 index 00000000..1b26ef0b --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.cpp @@ -0,0 +1,414 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Kss_Emu.h" + +#include "blargg_endian.h" +#include + +/* 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" + +long const clock_rate = 3579545; +int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; + +Kss_Emu::Kss_Emu() +{ + sn = 0; + set_type( gme_kss_type ); + set_silence_lookahead( 6 ); + 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 | 0, wave_type | 1, wave_type | 2, + wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 + }; + set_voice_types( types ); + + memset( unmapped_read, 0xFF, sizeof unmapped_read ); +} + +Kss_Emu::~Kss_Emu() { unload(); } + +void Kss_Emu::unload() +{ + delete sn; + sn = 0; + Classic_Emu::unload(); +} + +// Track info + +static void copy_kss_fields( Kss_Emu::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"; + } + Gme_File::copy_field_( out->system, system ); +} + +blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const +{ + copy_kss_fields( header_, out ); + return 0; +} + +static blargg_err_t check_kss_header( void const* header ) +{ + if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Kss_File : Gme_Info_ +{ + Kss_Emu::header_t header_; + + Kss_File() { set_type( gme_kss_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + return check_kss_header( &header_ ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_kss_fields( header_, out ); + return 0; + } +}; + +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::update_gain() +{ + double g = gain() * 1.4; + if ( scc_accessed ) + g *= 1.5; + ay.volume( g ); + scc.volume( g ); + if ( sn ) + sn->volume( g ); +} + +blargg_err_t Kss_Emu::load_( Data_Reader& in ) +{ + memset( &header_, 0, sizeof header_ ); + assert( offsetof (header_t,device_flags) == header_size - 1 ); + assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); + RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) ); + + RETURN_ERR( check_kss_header( header_.tag ) ); + + 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 + { + ext_header_t& ext = header_; + memcpy( &ext, rom.begin(), min( (int) ext_header_size, (int) header_.extra_header ) ); + if ( header_.extra_header > 0x10 ) + set_warning( "Unknown data in header" ); + } + + if ( header_.device_flags & 0x09 ) + set_warning( "FM sound not supported" ); + + scc_enabled = 0xC000; + if ( header_.device_flags & 0x04 ) + scc_enabled = 0; + + if ( header_.device_flags & 0x02 && !sn ) + CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) ); + + set_voice_count( osc_count ); + + return setup_buffer( ::clock_rate ); +} + +void Kss_Emu::update_eq( blip_eq_t const& eq ) +{ + ay.treble_eq( eq ); + scc.treble_eq( eq ); + if ( sn ) + sn->treble_eq( eq ); +} + +void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + int i2 = i - ay.osc_count; + if ( i2 >= 0 ) + scc.osc_output( i2, center ); + else + ay.osc_output( i, center ); + if ( sn && i < sn->osc_count ) + sn->osc_output( i, center, left, right ); +} + +// Emulation + +void Kss_Emu::set_tempo_( double t ) +{ + blip_time_t period = + (header_.device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); + play_period = blip_time_t (period / t); +} + +blargg_err_t Kss_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( 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 + unsigned load_addr = get_le16( header_.load_addr ); + long orig_load_size = get_le16( header_.load_size ); + long load_size = min( orig_load_size, rom.file_size() ); + load_size = min( load_size, long (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 + blargg_long 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 ); + + ay.reset(); + scc.reset(); + if ( sn ) + sn->reset(); + r.sp = 0xF380; + ram [--r.sp] = idle_addr >> 8; + ram [--r.sp] = idle_addr & 0xFF; + r.b.a = track; + r.pc = get_le16( header_.init_addr ); + next_play = play_period; + scc_accessed = false; + gain_updated = false; + update_gain(); + ay_latch = 0; + + return 0; +} + +void Kss_Emu::set_bank( int logical, int physical ) +{ + unsigned const bank_size = this->bank_size(); + + unsigned 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 + { + long phys = physical * (blargg_long) bank_size; + for ( unsigned offset = 0; offset < bank_size; offset += page_size ) + cpu::map_mem( addr + offset, page_size, + unmapped_write, rom.at_addr( phys + offset ) ); + } +} + +void Kss_Emu::cpu_write( unsigned addr, int data ) +{ + data &= 0xFF; + switch ( addr ) + { + case 0x9000: + set_bank( 0, data ); + return; + + case 0xB000: + set_bank( 1, data ); + return; + } + + int scc_addr = (addr & 0xDFFF) ^ 0x9800; + if ( scc_addr < scc.reg_count ) + { + scc_accessed = true; + scc.write( time(), scc_addr, data ); + return; + } + + dprintf( "LD ($%04X),$%02X\n", addr, data ); +} + +void kss_cpu_write( Kss_Cpu* cpu, unsigned addr, int data ) +{ + *cpu->write( addr ) = data; + if ( (addr & STATIC_CAST(Kss_Emu&,*cpu).scc_enabled) == 0x8000 ) + STATIC_CAST(Kss_Emu&,*cpu).cpu_write( addr, data ); +} + +void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) +{ + data &= 0xFF; + Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); + switch ( addr & 0xFF ) + { + case 0xA0: + emu.ay_latch = data & 0x0F; + return; + + case 0xA1: + GME_APU_HOOK( &emu, emu.ay_latch, data ); + emu.ay.write( time, emu.ay_latch, data ); + return; + + case 0x06: + if ( emu.sn && (emu.header_.device_flags & 0x04) ) + { + emu.sn->write_ggstereo( time, data ); + return; + } + break; + + case 0x7E: + case 0x7F: + if ( emu.sn ) + { + GME_APU_HOOK( &emu, 16, data ); + emu.sn->write_data( time, data ); + return; + } + break; + + case 0xFE: + emu.set_bank( 0, data ); + return; + + #ifndef NDEBUG + case 0xF1: // FM data + if ( data ) + break; // trap non-zero data + case 0xF0: // FM addr + case 0xA8: // PPI + return; + #endif + } + + dprintf( "OUT $%04X,$%02X\n", addr, data ); +} + +int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr ) +{ + //Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); + //switch ( addr & 0xFF ) + //{ + //} + + dprintf( "IN $%04X\n", addr ); + return 0; +} + +// Emulation + +blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) +{ + while ( time() < duration ) + { + blip_time_t end = min( duration, next_play ); + cpu::run( min( duration, next_play ) ); + if ( r.pc == idle_addr ) + set_time( end ); + + if ( time() >= next_play ) + { + next_play += play_period; + if ( r.pc == idle_addr ) + { + if ( !gain_updated ) + { + gain_updated = true; + if ( scc_accessed ) + update_gain(); + } + + ram [--r.sp] = idle_addr >> 8; + ram [--r.sp] = idle_addr & 0xFF; + r.pc = get_le16( header_.play_addr ); + GME_FRAME_HOOK( this ); + } + } + } + + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + adjust_time( -duration ); + ay.end_frame( duration ); + scc.end_frame( duration ); + if ( sn ) + sn->end_frame( duration ); + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h new file mode 100644 index 00000000..4d8463ab --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Emu.h @@ -0,0 +1,96 @@ +// MSX computer KSS music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef KSS_EMU_H +#define KSS_EMU_H + +#include "Classic_Emu.h" +#include "Kss_Scc_Apu.h" +#include "Kss_Cpu.h" +#include "Sms_Apu.h" +#include "Ay_Apu.h" + +class Kss_Emu : private Kss_Cpu, public Classic_Emu { + typedef Kss_Cpu cpu; +public: + // KSS file header + enum { header_size = 0x10 }; + struct header_t + { + 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; + }; + + enum { ext_header_size = 0x10 }; + struct ext_header_t + { + byte data_size [4]; + byte unused [4]; + byte first_track [2]; + byte last_tack [2]; + byte psg_vol; + byte scc_vol; + byte msx_music_vol; + byte msx_audio_vol; + }; + + struct composite_header_t : header_t, ext_header_t { }; + + // Header for currently loaded file + composite_header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_kss_type; } +public: + Kss_Emu(); + ~Kss_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +private: + Rom_Data rom; + composite_header_t header_; + + bool scc_accessed; + bool gain_updated; + void update_gain(); + + unsigned scc_enabled; // 0 or 0xC000 + byte const* bank_data; + int bank_count; + void set_bank( int logical, int physical ); + blargg_long bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); } + + blip_time_t play_period; + blip_time_t next_play; + int ay_latch; + + friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); + friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); + void cpu_write( unsigned addr, int data ); + friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); + + // large items + enum { mem_size = 0x10000 }; + byte ram [mem_size + cpu_padding]; + + Ay_Apu ay; + Scc_Apu scc; + Sms_Apu* sn; + byte unmapped_read [0x100]; + byte unmapped_write [page_size]; +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp new file mode 100644 index 00000000..1660ac3d --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.cpp @@ -0,0 +1,97 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Kss_Scc_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" + +// Tones above this frequency are treated as disabled tone at half volume. +// Power of two is more efficient (avoids division). +unsigned const inaudible_freq = 16384; + +int const wave_size = 0x20; + +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; + output->set_modified(); + + blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 + + regs [0x80 + index * 2] + 1; + int volume = 0; + if ( regs [0x8F] & (1 << index) ) + { + blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() + + inaudible_freq * 32) / (inaudible_freq * 16); + if ( period > inaudible_period ) + volume = (regs [0x8A + 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 wave + { + int amp = wave [osc.phase] * volume; + int delta = amp - osc.last_amp; + if ( delta ) + { + osc.last_amp = amp; + synth.offset( last_time, delta, output ); + } + } + + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + if ( !volume ) + { + // maintain phase + blargg_long count = (end_time - time + period - 1) / period; + osc.phase = (osc.phase + count) & (wave_size - 1); + time += count * period; + } + else + { + + int phase = osc.phase; + int last_wave = wave [phase]; + phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop + + do + { + int amp = wave [phase]; + phase = (phase + 1) & (wave_size - 1); + int delta = amp - last_wave; + if ( delta ) + { + last_wave = amp; + synth.offset( time, delta * volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance + osc.last_amp = wave [phase] * volume; + } + } + osc.delay = time - end_time; + } + last_time = end_time; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h new file mode 100644 index 00000000..03a6a108 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Kss_Scc_Apu.h @@ -0,0 +1,106 @@ +// Konami SCC sound chip emulator + +// Game_Music_Emu 0.5.2 +#ifndef KSS_SCC_APU_H +#define KSS_SCC_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" +#include + +class Scc_Apu { +public: + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Reset sound chip + void reset(); + + // Write to register at specified time + enum { reg_count = 0x90 }; + void write( blip_time_t time, int reg, int data ); + + // Run sound to specified time, end current time frame, then start 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( blip_time_t length ); + +// Additional features + + // Set sound output of specific oscillator to buffer, where index is + // 0 to 4. If buffer is NULL, the specified oscillator is muted. + enum { osc_count = 5 }; + void osc_output( int index, Blip_Buffer* ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see documentation) + void treble_eq( blip_eq_t const& ); + +public: + Scc_Apu(); +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 synth; + + void run_until( blip_time_t ); +}; + +inline void Scc_Apu::volume( double v ) { synth.volume( 0.43 / osc_count / amp_range * v ); } + +inline void Scc_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +inline void Scc_Apu::osc_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 ); + run_until( time ); + 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 ); +} + +inline void Scc_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + oscs [i].output = buf; +} + +inline Scc_Apu::Scc_Apu() +{ + output( 0 ); +} + +inline void Scc_Apu::reset() +{ + last_time = 0; + + for ( int i = 0; i < osc_count; i++ ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); + + memset( regs, 0, sizeof regs ); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp new file mode 100644 index 00000000..0a1475db --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.cpp @@ -0,0 +1,426 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "M3u_Playlist.h" +#include "Music_Emu.h" + +#include + +/* 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" + +// gme functions defined here to avoid linking in m3u code unless it's used + +blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) +{ + require( raw_track_count_ ); // file must be loaded first + + if ( !err ) + { + if ( playlist.size() ) + track_count_ = playlist.size(); + + int line = playlist.first_error(); + if ( line ) + { + // avoid using bloated printf() + char* out = &playlist_warning [sizeof playlist_warning]; + *--out = 0; + do { + *--out = line % 10 + '0'; + } while ( (line /= 10) > 0 ); + + static const char str [] = "Problem in m3u at line "; + out -= sizeof str - 1; + memcpy( out, str, sizeof str - 1 ); + set_warning( out ); + } + } + return err; +} + +blargg_err_t Gme_File::load_m3u( const char* path ) { return load_m3u_( playlist.load( path ) ); } + +blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } + +gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } + +gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) +{ + Mem_File_Reader in( data, size ); + return me->load_m3u( in ); +} + + + +static char* skip_white( char* in ) +{ + while ( *in == ' ' ) + in++; + return in; +} + +inline unsigned from_dec( unsigned n ) { return n - '0'; } + +static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) +{ + entry.file = in; + entry.type = ""; + char* out = in; + while ( 1 ) + { + int c = *in; + if ( !c ) break; + in++; + + if ( c == ',' ) // commas in filename + { + char* p = skip_white( in ); + if ( *p == '$' || from_dec( *p ) <= 9 ) + { + in = p; + break; + } + } + + if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix + { + entry.type = ++in; + while ( (c = *in) != 0 && c != ',' ) + in++; + if ( c == ',' ) + { + *in++ = 0; // terminate type + in = skip_white( in ); + } + break; + } + + if ( c == '\\' ) // \ prefix for special characters + { + c = *in; + if ( !c ) break; + in++; + } + *out++ = (char) c; + } + *out = 0; // terminate string + return in; +} + +static char* next_field( char* in, int* result ) +{ + while ( 1 ) + { + in = skip_white( in ); + + if ( !*in ) + break; + + if ( *in == ',' ) + { + in++; + break; + } + + *result = 1; + in++; + } + return skip_white( in ); +} + +static char* parse_int_( char* in, int* out ) +{ + int n = 0; + while ( 1 ) + { + unsigned d = from_dec( *in ); + if ( d > 9 ) + break; + in++; + n = n * 10 + d; + *out = n; + } + return in; +} + +static char* parse_int( char* in, int* out, int* result ) +{ + return next_field( parse_int_( in, out ), result ); +} + +// Returns 16 or greater if not hex +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static char* parse_track( char* in, M3u_Playlist::entry_t& entry, int* result ) +{ + if ( *in == '$' ) + { + in++; + int n = 0; + while ( 1 ) + { + int h = from_hex_char( *in ); + if ( h > 15 ) + break; + in++; + n = n * 16 + h; + entry.track = n; + } + } + else + { + in = parse_int_( in, &entry.track ); + if ( entry.track >= 0 ) + entry.decimal_track = 1; + } + return next_field( in, result ); +} + +static char* parse_time_( char* in, int* out ) +{ + *out = -1; + int n = -1; + in = parse_int_( in, &n ); + if ( n >= 0 ) + { + *out = n; + if ( *in == ':' ) + { + n = -1; + in = parse_int_( in + 1, &n ); + if ( n >= 0 ) + *out = *out * 60 + n; + } + } + return in; +} + +static char* parse_time( char* in, int* out, int* result ) +{ + return next_field( parse_time_( in, out ), result ); +} + +static char* parse_name( char* in ) +{ + char* out = in; + while ( 1 ) + { + int c = *in; + if ( !c ) break; + in++; + + if ( c == ',' ) // commas in string + { + char* p = skip_white( in ); + if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 ) + { + in = p; + break; + } + } + + if ( c == '\\' ) // \ prefix for special characters + { + c = *in; + if ( !c ) break; + in++; + } + *out++ = (char) c; + } + *out = 0; // terminate string + return in; +} + +static int parse_line( char* in, M3u_Playlist::entry_t& entry ) +{ + int result = 0; + + // file + entry.file = in; + entry.type = ""; + in = parse_filename( in, entry ); + + // track + entry.track = -1; + entry.decimal_track = 0; + in = parse_track( in, entry, &result ); + + // name + entry.name = in; + in = parse_name( in ); + + // time + entry.length = -1; + in = parse_time( in, &entry.length, &result ); + + // loop + entry.intro = -1; + entry.loop = -1; + if ( *in == '-' ) + { + entry.loop = entry.length; + in++; + } + else + { + in = parse_time_( in, &entry.loop ); + if ( entry.loop >= 0 ) + { + entry.intro = 0; + if ( *in == '-' ) // trailing '-' means that intro length was specified + { + in++; + entry.intro = entry.loop; + entry.loop = entry.length - entry.intro; + } + } + } + in = next_field( in, &result ); + + // fade + entry.fade = -1; + in = parse_time( in, &entry.fade, &result ); + + // repeat + entry.repeat = -1; + in = parse_int( in, &entry.repeat, &result ); + + return result; +} + +static void parse_comment( char* in, M3u_Playlist::info_t& info, bool first ) +{ + in = skip_white( in + 1 ); + const char* field = in; + while ( *in && *in != ':' ) + in++; + + if ( *in == ':' ) + { + const char* text = skip_white( in + 1 ); + if ( *text ) + { + *in = 0; + if ( !strcmp( "Composer", field ) ) info.composer = text; + else if ( !strcmp( "Engineer", field ) ) info.engineer = text; + else if ( !strcmp( "Ripping" , field ) ) info.ripping = text; + else if ( !strcmp( "Tagging" , field ) ) info.tagging = text; + else + text = 0; + if ( text ) + return; + *in = ':'; + } + } + + if ( first ) + info.title = field; +} + +blargg_err_t M3u_Playlist::parse_() +{ + info_.title = ""; + info_.composer = ""; + info_.engineer = ""; + info_.ripping = ""; + info_.tagging = ""; + + int const CR = 13; + int const LF = 10; + + data.end() [-1] = LF; // terminate input + + first_error_ = 0; + bool first_comment = true; + int line = 0; + int count = 0; + char* in = data.begin(); + while ( in < data.end() ) + { + // find end of line and terminate it + line++; + char* begin = in; + while ( *in != CR && *in != LF ) + { + if ( !*in ) + return "Not an m3u playlist"; + in++; + } + if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line + *in++ = 0; + *in++ = 0; + + // parse line + if ( *begin == '#' ) + { + parse_comment( begin, info_, first_comment ); + first_comment = false; + } + else if ( *begin ) + { + if ( (int) entries.size() <= count ) + RETURN_ERR( entries.resize( count * 2 + 64 ) ); + + if ( !parse_line( begin, entries [count] ) ) + count++; + else if ( !first_error_ ) + first_error_ = line; + first_comment = false; + } + } + if ( count <= 0 ) + return "Not an m3u playlist"; + + if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) ) + info_.title = ""; + + return entries.resize( count ); +} + +blargg_err_t M3u_Playlist::parse() +{ + blargg_err_t err = parse_(); + if ( err ) + { + entries.clear(); + data.clear(); + } + return err; +} + +blargg_err_t M3u_Playlist::load( Data_Reader& in ) +{ + RETURN_ERR( data.resize( in.remain() + 1 ) ); + RETURN_ERR( in.read( data.begin(), data.size() - 1 ) ); + return parse(); +} + +blargg_err_t M3u_Playlist::load( const char* path ) +{ + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + return load( in ); +} + +blargg_err_t M3u_Playlist::load( void const* in, long size ) +{ + RETURN_ERR( data.resize( size + 1 ) ); + memcpy( data.begin(), in, size ); + return parse(); +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h new file mode 100644 index 00000000..eda0dc89 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/M3u_Playlist.h @@ -0,0 +1,67 @@ +// M3U playlist file parser, with support for subtrack information + +// Game_Music_Emu 0.5.2 +#ifndef M3U_PLAYLIST_H +#define M3U_PLAYLIST_H + +#include "blargg_common.h" +#include "Data_Reader.h" + +class M3u_Playlist { +public: + // Load playlist data + blargg_err_t load( const char* path ); + blargg_err_t load( Data_Reader& in ); + blargg_err_t load( void const* data, long size ); + + // Line number of first parse error, 0 if no error. Any lines with parse + // errors are ignored. + int first_error() const { return first_error_; } + + struct info_t + { + const char* title; + const char* composer; + const char* engineer; + const char* ripping; + const char* tagging; + }; + info_t const& info() const { return info_; } + + struct entry_t + { + const char* file; // filename without stupid ::TYPE suffix + const char* type; // if filename has ::TYPE suffix, this will be "TYPE". "" if none. + const char* name; + bool decimal_track; // true if track was specified in hex + // integers are -1 if not present + int track; // 1-based + int length; // seconds + int intro; + int loop; + int fade; + int repeat; // count + }; + entry_t const& operator [] ( int i ) const { return entries [i]; } + int size() const { return entries.size(); } + + void clear(); + +private: + blargg_vector entries; + blargg_vector data; + int first_error_; + info_t info_; + + blargg_err_t parse(); + blargg_err_t parse_(); +}; + +inline void M3u_Playlist::clear() +{ + first_error_ = 0; + entries.clear(); + data.clear(); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Makefile.am b/plugins/gme/Game_Music_Emu-0.5.2/gme/Makefile.am new file mode 100644 index 00000000..d728deea --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Makefile.am @@ -0,0 +1,66 @@ +noinst_LIBRARIES = libgme.a +libgme_a_SOURCES = Ay_Apu.cpp Gb_Apu.cpp Hes_Emu.cpp Nes_Fme7_Apu.cpp Sms_Apu.cpp\ +Ay_Cpu.cpp Gb_Cpu.cpp Kss_Cpu.cpp Nes_Namco_Apu.cpp Snes_Spc.cpp\ +Ay_Emu.cpp Gb_Oscs.cpp Kss_Emu.cpp Nes_Oscs.cpp Spc_Cpu.cpp\ +Blip_Buffer.cpp Gbs_Emu.cpp Kss_Scc_Apu.cpp Nes_Vrc6_Apu.cpp Spc_Dsp.cpp\ +Classic_Emu.cpp gme.cpp M3u_Playlist.cpp Nsfe_Emu.cpp Spc_Emu.cpp\ +Data_Reader.cpp Gme_File.cpp Multi_Buffer.cpp Nsf_Emu.cpp Vgm_Emu.cpp\ +Dual_Resampler.cpp Gym_Emu.cpp Music_Emu.cpp Sap_Apu.cpp Vgm_Emu_Impl.cpp\ +Effects_Buffer.cpp Hes_Apu.cpp Nes_Apu.cpp Sap_Cpu.cpp Ym2413_Emu.cpp\ +Fir_Resampler.cpp Hes_Cpu.cpp Nes_Cpu.cpp Sap_Emu.cpp Ym2612_Emu.cpp\ +Ay_Apu.h\ +Ay_Cpu.h\ +Ay_Emu.h\ +blargg_common.h\ +blargg_config.h\ +blargg_endian.h\ +blargg_source.h\ +Blip_Buffer.h\ +Classic_Emu.h\ +Data_Reader.h\ +Dual_Resampler.h\ +Effects_Buffer.h\ +Fir_Resampler.h\ +Gb_Apu.h\ +Gb_Cpu.h\ +gb_cpu_io.h\ +Gb_Oscs.h\ +Gbs_Emu.h\ +Gme_File.h\ +gme.h\ +Gym_Emu.h\ +Hes_Apu.h\ +Hes_Cpu.h\ +hes_cpu_io.h\ +Hes_Emu.h\ +Kss_Cpu.h\ +Kss_Emu.h\ +Kss_Scc_Apu.h\ +M3u_Playlist.h\ +Multi_Buffer.h\ +Music_Emu.h\ +Nes_Apu.h\ +Nes_Cpu.h\ +nes_cpu_io.h\ +Nes_Fme7_Apu.h\ +Nes_Namco_Apu.h\ +Nes_Oscs.h\ +Nes_Vrc6_Apu.h\ +Nsfe_Emu.h\ +Nsf_Emu.h\ +Sap_Apu.h\ +Sap_Cpu.h\ +sap_cpu_io.h\ +Sap_Emu.h\ +Sms_Apu.h\ +Sms_Oscs.h\ +Snes_Spc.h\ +Spc_Cpu.h\ +Spc_Dsp.h\ +Spc_Emu.h\ +Vgm_Emu.h\ +Vgm_Emu_Impl.h\ +Ym2413_Emu.h\ +Ym2612_Emu.h + +AM_CPPFLAGS = $(CXXFLAGS) -fPIC diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp new file mode 100644 index 00000000..ecd8f8ad --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.cpp @@ -0,0 +1,232 @@ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ + +#include "Multi_Buffer.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" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) +{ + length_ = 0; + sample_rate_ = 0; + channels_changed_count_ = 1; +} + +blargg_err_t Multi_Buffer::set_channel_count( int ) { return 0; } + +// Silent_Buffer + +Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse +{ + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = 0; + chan.center = 0; + chan.right = 0; +} + +// Mono_Buffer + +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) +{ + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; +} + +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) +{ + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); +} + +// Stereo_Buffer + +Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) +{ + chan.center = &bufs [0]; + chan.left = &bufs [1]; + chan.right = &bufs [2]; +} + +Stereo_Buffer::~Stereo_Buffer() { } + +blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) +{ + for ( int i = 0; i < buf_count; i++ ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Stereo_Buffer::clock_rate( long rate ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( unsigned i = 0; i < buf_count; i++ ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + stereo_added = 0; + was_stereo = false; + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t clock_count ) +{ + stereo_added = 0; + for ( unsigned i = 0; i < buf_count; i++ ) + { + stereo_added |= bufs [i].clear_modified() << i; + bufs [i].end_frame( clock_count ); + } +} + +long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) +{ + require( !(count & 1) ); // count must be even + count = (unsigned) count / 2; + + long avail = bufs [0].samples_avail(); + if ( count > avail ) + count = avail; + if ( count ) + { + int bufs_used = stereo_added | was_stereo; + //dprintf( "%X\n", bufs_used ); + if ( bufs_used <= 1 ) + { + mix_mono( out, count ); + bufs [0].remove_samples( count ); + bufs [1].remove_silence( count ); + bufs [2].remove_silence( count ); + } + else if ( bufs_used & 1 ) + { + mix_stereo( out, count ); + bufs [0].remove_samples( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); + } + else + { + mix_stereo_no_center( out, count ); + bufs [0].remove_silence( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); + } + + // to do: this might miss opportunities for optimization + if ( !bufs [0].samples_avail() ) + { + was_stereo = stereo_added; + stereo_added = 0; + } + } + + return count * 2; +} + +void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + BLIP_READER_BEGIN( center, bufs [0] ); + + for ( ; count; --count ) + { + int c = BLIP_READER_READ( center ); + blargg_long l = c + BLIP_READER_READ( left ); + blargg_long r = c + BLIP_READER_READ( right ); + if ( (BOOST::int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + BLIP_READER_NEXT( center, bass ); + if ( (BOOST::int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + + out [0] = l; + out [1] = r; + out += 2; + } + + BLIP_READER_END( center, bufs [0] ); + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); +} + +void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + + for ( ; count; --count ) + { + blargg_long l = BLIP_READER_READ( left ); + if ( (BOOST::int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + blargg_long r = BLIP_READER_READ( right ); + if ( (BOOST::int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + + out [0] = l; + out [1] = r; + out += 2; + } + + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); +} + +void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( center, bufs [0] ); + + for ( ; count; --count ) + { + blargg_long s = BLIP_READER_READ( center ); + if ( (BOOST::int16_t) s != s ) + s = 0x7FFF - (s >> 24); + + BLIP_READER_NEXT( center, bass ); + out [0] = s; + out [1] = s; + out += 2; + } + + BLIP_READER_END( center, bufs [0] ); +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h new file mode 100644 index 00000000..a39cca1a --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Multi_Buffer.h @@ -0,0 +1,156 @@ +// Multi-channel sound buffer interface, and basic mono and stereo buffers + +// Blip_Buffer 0.4.1 +#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: + Multi_Buffer( int samples_per_frame ); + virtual ~Multi_Buffer() { } + + // Set the number of channels available + virtual blargg_err_t set_channel_count( int ); + + // Get indexed channel, from 0 to channel count - 1 + struct channel_t { + Blip_Buffer* center; + Blip_Buffer* left; + Blip_Buffer* right; + }; + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual channel_t channel( int index, int type ) = 0; + + // See Blip_Buffer.h + virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; + virtual void clock_rate( long ) = 0; + virtual void bass_freq( int ) = 0; + virtual void clear() = 0; + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // See Blip_Buffer.h + virtual void end_frame( blip_time_t ) = 0; + + // 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 long read_samples( blip_sample_t*, long ) = 0; + virtual long samples_avail() const = 0; + +public: + BLARGG_DISABLE_NOTHROW +protected: + void channels_changed() { channels_changed_count_++; } +private: + // noncopyable + Multi_Buffer( const Multi_Buffer& ); + Multi_Buffer& operator = ( const Multi_Buffer& ); + + unsigned channels_changed_count_; + long sample_rate_; + int length_; + int const samples_per_frame_; +}; + +// Uses a single buffer and outputs mono samples. +class Mono_Buffer : public Multi_Buffer { + Blip_Buffer buf; + channel_t chan; +public: + // Buffer used for all channels + Blip_Buffer* center() { return &buf; } + +public: + Mono_Buffer(); + ~Mono_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); + void clock_rate( long rate ) { buf.clock_rate( rate ); } + void bass_freq( int freq ) { buf.bass_freq( freq ); } + void clear() { buf.clear(); } + long samples_avail() const { return buf.samples_avail(); } + long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t t ) { buf.end_frame( t ); } +}; + +// 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 [0]; } + Blip_Buffer* left() { return &bufs [1]; } + Blip_Buffer* right() { return &bufs [2]; } + +public: + Stereo_Buffer(); + ~Stereo_Buffer(); + blargg_err_t set_sample_rate( long, int msec = blip_default_length ); + void clock_rate( long ); + void bass_freq( int ); + void clear(); + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ); + + long samples_avail() const { return bufs [0].samples_avail() * 2; } + long read_samples( blip_sample_t*, long ); + +private: + enum { buf_count = 3 }; + Blip_Buffer bufs [buf_count]; + channel_t chan; + int stereo_added; + int was_stereo; + + void mix_stereo_no_center( blip_sample_t*, blargg_long ); + void mix_stereo( blip_sample_t*, blargg_long ); + void mix_mono( blip_sample_t*, blargg_long ); +}; + +// Silent_Buffer generates no samples, useful where no sound is wanted +class Silent_Buffer : public Multi_Buffer { + channel_t chan; +public: + Silent_Buffer(); + blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) + { + return Multi_Buffer::set_sample_rate( rate, msec ); + } + void clock_rate( long ) { } + void bass_freq( int ) { } + void clear() { } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ) { } + long samples_avail() const { return 0; } + long read_samples( blip_sample_t*, long ) { return 0; } +}; + + +inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +{ + sample_rate_ = rate; + length_ = msec; + return 0; +} + +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } + +inline long Multi_Buffer::sample_rate() const { return sample_rate_; } + +inline int Multi_Buffer::length() const { return length_; } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp new file mode 100644 index 00000000..31c7233c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.cpp @@ -0,0 +1,410 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Music_Emu.h" + +#include "Multi_Buffer.h" +#include + +/* 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" + +int const stereo = 2; // number of channels for stereo +int const silence_max = 6; // seconds +int const silence_threshold = 0x10; +long const fade_block_size = 512; +int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) + +Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 }; + +void Music_Emu::clear_track_vars() +{ + current_track_ = -1; + out_time = 0; + emu_time = 0; + emu_track_ended_ = true; + track_ended_ = true; + fade_start = LONG_MAX / 2 + 1; + fade_step = 1; + silence_time = 0; + silence_count = 0; + buf_remain = 0; + warning(); // clear warning +} + +void Music_Emu::unload() +{ + voice_count_ = 0; + clear_track_vars(); + Gme_File::unload(); +} + +Music_Emu::Music_Emu() +{ + effects_buffer = 0; + + sample_rate_ = 0; + mute_mask_ = 0; + tempo_ = 1.0; + gain_ = 1.0; + + // defaults + max_initial_silence = 2; + silence_lookahead = 3; + ignore_silence_ = false; + equalizer_.treble = -1.0; + equalizer_.bass = 60; + + static const char* const names [] = { + "Voice 1", "Voice 2", "Voice 3", "Voice 4", + "Voice 5", "Voice 6", "Voice 7", "Voice 8" + }; + set_voice_names( names ); + Music_Emu::unload(); // non-virtual +} + +Music_Emu::~Music_Emu() { delete effects_buffer; } + +blargg_err_t Music_Emu::set_sample_rate( long rate ) +{ + require( !sample_rate() ); // sample rate can't be changed once set + RETURN_ERR( set_sample_rate_( rate ) ); + RETURN_ERR( buf.resize( buf_size ) ); + sample_rate_ = rate; + return 0; +} + +void Music_Emu::pre_load() +{ + require( sample_rate() ); // set_sample_rate() must be called before loading a file + Gme_File::pre_load(); +} + +void Music_Emu::set_equalizer( equalizer_t const& eq ) +{ + equalizer_ = eq; + set_equalizer_( eq ); +} + +void Music_Emu::mute_voice( int index, bool mute ) +{ + require( (unsigned) index < (unsigned) voice_count() ); + int bit = 1 << index; + int mask = mute_mask_ | bit; + if ( !mute ) + mask ^= bit; + mute_voices( mask ); +} + +void Music_Emu::mute_voices( int mask ) +{ + require( sample_rate() ); // sample rate must be set first + mute_mask_ = mask; + mute_voices_( mask ); +} + +void Music_Emu::set_tempo( double t ) +{ + require( sample_rate() ); // sample rate must be set first + double const min = 0.02; + double const max = 4.00; + if ( t < min ) t = min; + if ( t > max ) t = max; + tempo_ = t; + set_tempo_( t ); +} + +void Music_Emu::post_load_() +{ + set_tempo( tempo_ ); + remute_voices(); +} + +blargg_err_t Music_Emu::start_track( int track ) +{ + clear_track_vars(); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + current_track_ = track; + RETURN_ERR( start_track_( remapped ) ); + + emu_track_ended_ = false; + track_ended_ = false; + + if ( !ignore_silence_ ) + { + // play until non-silence or end of track + for ( long end = max_initial_silence * stereo * sample_rate(); emu_time < end; ) + { + fill_buf(); + if ( buf_remain | (int) emu_track_ended_ ) + break; + } + + emu_time = buf_remain; + out_time = 0; + silence_time = 0; + silence_count = 0; + } + return track_ended() ? warning() : 0; +} + +void Music_Emu::end_track_if_error( blargg_err_t err ) +{ + if ( err ) + { + emu_track_ended_ = true; + set_warning( err ); + } +} + +// Tell/Seek + +blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const +{ + blargg_long sec = msec / 1000; + msec -= sec * 1000; + return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; +} + +long Music_Emu::tell() const +{ + blargg_long rate = sample_rate() * stereo; + blargg_long sec = out_time / rate; + return sec * 1000 + (out_time - sec * rate) * 1000 / rate; +} + +blargg_err_t Music_Emu::seek( long msec ) +{ + blargg_long time = msec_to_samples( msec ); + if ( time < out_time ) + RETURN_ERR( start_track( current_track_ ) ); + return skip( time - out_time ); +} + +blargg_err_t Music_Emu::skip( long count ) +{ + require( current_track() >= 0 ); // start_track() must have been called already + out_time += count; + + // remove from silence and buf first + { + long n = min( count, silence_count ); + silence_count -= n; + count -= n; + + n = min( count, buf_remain ); + buf_remain -= n; + count -= n; + } + + if ( count && !emu_track_ended_ ) + { + emu_time += count; + end_track_if_error( skip_( count ) ); + } + + if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended + track_ended_ |= emu_track_ended_; + + return 0; +} + +blargg_err_t Music_Emu::skip_( long count ) +{ + // for long skip, mute sound + const long threshold = 30000; + if ( count > threshold ) + { + int saved_mute = mute_mask_; + mute_voices( ~0 ); + + while ( count > threshold / 2 && !emu_track_ended_ ) + { + RETURN_ERR( play_( buf_size, buf.begin() ) ); + count -= buf_size; + } + + mute_voices( saved_mute ); + } + + while ( count && !emu_track_ended_ ) + { + long n = buf_size; + if ( n > count ) + n = count; + count -= n; + RETURN_ERR( play_( n, buf.begin() ) ); + } + return 0; +} + +// Fading + +void Music_Emu::set_fade( long start_msec, long length_msec ) +{ + fade_step = sample_rate() * length_msec / (fade_block_size * fade_shift * 1000 / stereo); + fade_start = msec_to_samples( start_msec ); +} + +// unit / pow( 2.0, (double) x / step ) +static int int_log( blargg_long x, int step, int unit ) +{ + int shift = x / step; + int fraction = (x - shift * step) * unit / step; + return ((unit - fraction) + (fraction >> 1)) >> shift; +} + +void Music_Emu::handle_fade( long out_count, sample_t* out ) +{ + for ( int i = 0; i < out_count; i += fade_block_size ) + { + int const shift = 14; + int const unit = 1 << shift; + int gain = int_log( (out_time + i - fade_start) / fade_block_size, + fade_step, unit ); + if ( gain < (unit >> fade_shift) ) + track_ended_ = emu_track_ended_ = true; + + sample_t* io = &out [i]; + for ( int count = min( fade_block_size, out_count - i ); count; --count ) + { + *io = sample_t ((*io * gain) >> shift); + ++io; + } + } +} + +// Silence detection + +void Music_Emu::emu_play( long count, sample_t* out ) +{ + check( current_track_ >= 0 ); + emu_time += count; + if ( current_track_ >= 0 && !emu_track_ended_ ) + end_track_if_error( play_( count, out ) ); + else + memset( out, 0, count * sizeof *out ); +} + +// number of consecutive silent samples at end +static long count_silence( Music_Emu::sample_t* begin, long size ) +{ + Music_Emu::sample_t first = *begin; + *begin = silence_threshold; // sentinel + Music_Emu::sample_t* p = begin + size; + while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } + *begin = first; + return size - (p - begin); +} + +// fill internal buffer and check it for silence +void Music_Emu::fill_buf() +{ + assert( !buf_remain ); + if ( !emu_track_ended_ ) + { + emu_play( buf_size, buf.begin() ); + long silence = count_silence( buf.begin(), buf_size ); + if ( silence < buf_size ) + { + silence_time = emu_time - silence; + buf_remain = buf_size; + return; + } + } + silence_count += buf_size; +} + +blargg_err_t Music_Emu::play( long out_count, sample_t* out ) +{ + if ( track_ended_ ) + { + memset( out, 0, out_count * sizeof *out ); + } + else + { + require( current_track() >= 0 ); + require( out_count % stereo == 0 ); + + assert( emu_time >= out_time ); + + // prints nifty graph of how far ahead we are when searching for silence + //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); + + long pos = 0; + if ( silence_count ) + { + // during a run of silence, run emulator at >=2x speed so it gets ahead + long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time; + while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) ) + fill_buf(); + + // fill with silence + pos = min( silence_count, out_count ); + memset( out, 0, pos * sizeof *out ); + silence_count -= pos; + + if ( emu_time - silence_time > silence_max * stereo * sample_rate() ) + { + track_ended_ = emu_track_ended_ = true; + silence_count = 0; + buf_remain = 0; + } + } + + if ( buf_remain ) + { + // empty silence buf + long n = min( buf_remain, out_count - pos ); + memcpy( &out [pos], buf.begin() + (buf_size - buf_remain), n * sizeof *out ); + buf_remain -= n; + pos += n; + } + + // generate remaining samples normally + long remain = out_count - pos; + if ( remain ) + { + emu_play( remain, out + pos ); + track_ended_ |= emu_track_ended_; + + if ( !ignore_silence_ || out_time > fade_start ) + { + // check end for a new run of silence + long silence = count_silence( out + pos, remain ); + if ( silence < remain ) + silence_time = emu_time - silence; + + if ( emu_time - silence_time >= buf_size ) + fill_buf(); // cause silence detection on next play() + } + } + + if ( out_time > fade_start ) + handle_fade( out_count, out ); + } + out_time += out_count; + return 0; +} + +// Gme_Info_ + +blargg_err_t Gme_Info_::set_sample_rate_( long ) { return 0; } +void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu +void Gme_Info_::post_load_() { Gme_File::post_load_(); } // skip Music_Emu +void Gme_Info_::set_equalizer_( equalizer_t const& ){ check( false ); } +void Gme_Info_::mute_voices_( int ) { check( false ); } +void Gme_Info_::set_tempo_( double ) { } +blargg_err_t Gme_Info_::start_track_( int ) { return "Use full emulator for playback"; } +blargg_err_t Gme_Info_::play_( long, sample_t* ) { return "Use full emulator for playback"; } diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h new file mode 100644 index 00000000..573403ce --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Music_Emu.h @@ -0,0 +1,211 @@ +// Common interface to game music file emulators + +// Game_Music_Emu 0.5.2 +#ifndef MUSIC_EMU_H +#define MUSIC_EMU_H + +#include "Gme_File.h" +class Multi_Buffer; + +struct Music_Emu : public Gme_File { +public: +// Basic functionality (see Gme_File.h for file loading/track info functions) + + // Set output sample rate. Must be called only once before loading file. + blargg_err_t set_sample_rate( long sample_rate ); + + // Start a track, where 0 is the first track. Also clears warning string. + blargg_err_t start_track( int ); + + // Generate 'count' samples info 'buf'. Output is in stereo. Any emulation + // errors set warning string, and major errors also end track. + typedef short sample_t; + blargg_err_t play( long count, sample_t* buf ); + +// Informational + + // Sample rate sound is generated at + long sample_rate() const; + + // Index of current track or -1 if one hasn't been started + int current_track() const; + + // Number of voices used by currently loaded file + int voice_count() const; + + // Names of voices + const char** voice_names() const; + +// Track status/control + + // Number of milliseconds (1000 msec = 1 second) played since beginning of track + long tell() const; + + // Seek to new time in track. Seeking backwards or far forward can take a while. + blargg_err_t seek( long msec ); + + // Skip n samples + blargg_err_t skip( long n ); + + // True if a track has reached its end + bool track_ended() const; + + // Set start time and length of track fade out. Once fade ends track_ended() returns + // true. Fade time can be changed while track is playing. + void set_fade( long start_msec, long length_msec = 8000 ); + + // Disable automatic end-of-track detection and skipping of silence at beginning + void ignore_silence( bool disable = true ); + + // Info for current track + Gme_File::track_info; + blargg_err_t track_info( track_info_t* out ) const; + +// Sound customization + + // Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. + // Track length as returned by track_info() assumes a tempo of 1.0. + void set_tempo( double ); + + // Mute/unmute voice i, where voice 0 is first voice + void mute_voice( int index, bool mute = true ); + + // Set muting state of all voices at once using a bit mask, where -1 mutes them all, + // 0 unmutes them all, 0x01 mutes just the first voice, etc. + void mute_voices( int mask ); + + // Change overall output amplitude, where 1.0 results in minimal clamping. + // Must be called before set_sample_rate(). + void set_gain( double ); + + // Request use of custom multichannel buffer. Only supported by "classic" emulators; + // on others this has no effect. Should be called only once *before* set_sample_rate(). + virtual void set_buffer( Multi_Buffer* ) { } + +// Sound equalization (treble/bass) + + // Frequency equalizer parameters (see gme.txt) + // See gme.h for definition of struct gme_equalizer_t. + typedef gme_equalizer_t equalizer_t; + + // Current frequency equalizater parameters + equalizer_t const& equalizer() const; + + // Set frequency equalizer parameters + void set_equalizer( equalizer_t const& ); + + // Equalizer settings for TV speaker + static equalizer_t const tv_eq; + +public: + Music_Emu(); + ~Music_Emu(); +protected: + void set_max_initial_silence( int n ) { max_initial_silence = n; } + void set_silence_lookahead( int n ) { silence_lookahead = n; } + void set_voice_count( int n ) { voice_count_ = n; } + void set_voice_names( const char* const* names ); + void set_track_ended() { emu_track_ended_ = true; } + double gain() const { return gain_; } + double tempo() const { return tempo_; } + void remute_voices(); + + virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; + virtual void set_equalizer_( equalizer_t const& ) { }; + virtual void mute_voices_( int mask ) = 0; + virtual void set_tempo_( double ) = 0; + virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this + virtual blargg_err_t play_( long count, sample_t* out ) = 0; + virtual blargg_err_t skip_( long count ); +protected: + virtual void unload(); + virtual void pre_load(); + virtual void post_load_(); +private: + // general + equalizer_t equalizer_; + int max_initial_silence; + const char** voice_names_; + int voice_count_; + int mute_mask_; + double tempo_; + double gain_; + + long sample_rate_; + blargg_long msec_to_samples( blargg_long msec ) const; + + // track-specific + int current_track_; + blargg_long out_time; // number of samples played since start of track + blargg_long emu_time; // number of samples emulator has generated since start of track + bool emu_track_ended_; // emulator has reached end of track + volatile bool track_ended_; + void clear_track_vars(); + void end_track_if_error( blargg_err_t ); + + // fading + blargg_long fade_start; + int fade_step; + void handle_fade( long count, sample_t* out ); + + // silence detection + int silence_lookahead; // speed to run emulator when looking ahead for silence + bool ignore_silence_; + long silence_time; // number of samples where most recent silence began + long silence_count; // number of samples of silence to play before using buf + long buf_remain; // number of samples left in silence buffer + enum { buf_size = 2048 }; + blargg_vector buf; + void fill_buf(); + void emu_play( long count, sample_t* out ); + + Multi_Buffer* effects_buffer; + friend Music_Emu* gme_new_emu( gme_type_t, long ); + friend void gme_set_stereo_depth( Music_Emu*, double ); +}; + +// base class for info-only derivations +struct Gme_Info_ : Music_Emu +{ + virtual blargg_err_t set_sample_rate_( long sample_rate ); + virtual void set_equalizer_( equalizer_t const& ); + virtual void mute_voices_( int mask ); + virtual void set_tempo_( double ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( long count, sample_t* out ); + virtual void pre_load(); + virtual void post_load_(); +}; + +inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const +{ + return track_info( out, current_track_ ); +} + +inline long Music_Emu::sample_rate() const { return sample_rate_; } +inline const char** Music_Emu::voice_names() const { return voice_names_; } +inline int Music_Emu::voice_count() const { return voice_count_; } +inline int Music_Emu::current_track() const { return current_track_; } +inline bool Music_Emu::track_ended() const { return track_ended_; } +inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } + +inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; } +inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } +inline void Music_Emu::ignore_silence( bool b ) { ignore_silence_ = b; } +inline blargg_err_t Music_Emu::start_track_( int ) { return 0; } + +inline void Music_Emu::set_voice_names( const char* const* names ) +{ + // Intentional removal of const, so users don't have to remember obscure const in middle + voice_names_ = (const char**) names; +} + +inline void Music_Emu::mute_voices_( int ) { } + +inline void Music_Emu::set_gain( double g ) +{ + assert( !sample_rate() ); // you must set gain before setting sample rate + gain_ = g; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp new file mode 100644 index 00000000..8daf5d0e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.cpp @@ -0,0 +1,391 @@ +// Nes_Snd_Emu 0.1.8. 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" + +int const amp_range = 15; + +Nes_Apu::Nes_Apu() : + square1( &square_synth ), + square2( &square_synth ) +{ + tempo_ = 1.0; + dmc.apu = this; + dmc.prg_reader = NULL; + irq_notifier_ = NULL; + + oscs [0] = &square1; + oscs [1] = &square2; + oscs [2] = ▵ + oscs [3] = &noise; + oscs [4] = &dmc; + + output( NULL ); + 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 v ) +{ + dmc.nonlinear = true; + square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); + + const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); + triangle.synth.volume( 3.0 * tnd ); + noise.synth.volume( 2.0 * tnd ); + 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 ) +{ + dmc.nonlinear = false; + square_synth.volume( 0.1128 / amp_range * v ); + triangle.synth.volume( 0.12765 / amp_range * v ); + noise.synth.volume( 0.0741 / amp_range * v ); + dmc.synth.volume( 0.42545 / 127 * v ); +} + +void Nes_Apu::output( Blip_Buffer* buffer ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_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; + earliest_irq_ = no_irq; + frame_delay = 1; + write_register( 0, 0x4017, 0x00 ); + write_register( 0, 0x4015, 0x00 ); + + for ( nes_addr_t addr = start_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() +{ + nes_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_ ) + irq_notifier_( irq_data ); + } +} + +// frames + +void Nes_Apu::run_until( nes_time_t end_time ) +{ + require( end_time >= last_dmc_time ); + if ( end_time > next_dmc_read_time() ) + { + nes_time_t start = last_dmc_time; + last_dmc_time = end_time; + dmc.run( start, end_time ); + } +} + +void Nes_Apu::run_until_( nes_time_t end_time ) +{ + require( end_time >= last_time ); + + if ( end_time == last_time ) + return; + + if ( last_dmc_time < end_time ) + { + nes_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 + nes_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, nes_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( nes_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( nes_time_t time, nes_addr_t 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 - start_addr) > end_addr - start_addr ) + return; + + run_until_( time ); + + if ( addr < 0x4014 ) + { + // Write to channel + int osc_index = (addr - start_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 + 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( nes_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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h new file mode 100644 index 00000000..dbd8484c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Apu.h @@ -0,0 +1,179 @@ +// NES 2A03 APU sound chip emulator + +// Nes_Snd_Emu 0.1.8 +#ifndef NES_APU_H +#define NES_APU_H + +#include "blargg_common.h" + +typedef blargg_long nes_time_t; // CPU clock cycle count +typedef unsigned nes_addr_t; // 16-bit memory address + +#include "Nes_Oscs.h" + +struct apu_state_t; +class Nes_Buffer; + +class Nes_Apu { +public: + // Set buffer to generate all sound into, or disable sound if NULL + void output( Blip_Buffer* ); + + // Set 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, nes_addr_t ), void* user_data = NULL ); + + // 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 ). + + // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) + enum { start_addr = 0x4000 }; + enum { end_addr = 0x4017 }; + void write_register( nes_time_t, nes_addr_t, int data ); + + // Read from status register at 0x4015 + enum { status_addr = 0x4015 }; + int read_status( nes_time_t ); + + // Run all oscillators up to specified time, end current time frame, then + // start 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 ); + +// Additional optional features (can be ignored without any problem) + + // Reset internal frame counter, registers, and all oscillators. + // Use PAL timing if pal_timing is true, otherwise use NTSC timing. + // Set 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 ); + + // Adjust frame period + void set_tempo( double ); + + // Save/load exact emulation state + void save_state( apu_state_t* out ) const; + void load_state( apu_state_t const& ); + + // Set overall volume (default is 1.0) + void volume( double ); + + // Set treble equalization (see notes.txt) + void treble_eq( const blip_eq_t& ); + + // Set sound output of specific oscillator to buffer. If buffer is NULL, + // the specified oscillator is muted and emulation accuracy is reduced. + // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, + // 2) Triangle, 3) Noise, 4) DMC. + enum { osc_count = 5 }; + void osc_output( int index, Blip_Buffer* buffer ); + + // Set 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 ); + + // Get 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 = LONG_MAX / 2 + 1 }; + enum { irq_waiting = 0 }; + nes_time_t earliest_irq( nes_time_t ) const; + + // Count 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; + + // Run 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 ); + +public: + Nes_Apu(); + BLARGG_DISABLE_NOTHROW +private: + friend class Nes_Nonlinearizer; + void enable_nonlinear( double volume ); + static double nonlinear_tnd_gain() { return 0.75; } +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; + void (*irq_notifier_)( void* user_data ); + void* irq_data; + 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::osc_output( int osc, Blip_Buffer* buf ) +{ + assert( (unsigned) osc < osc_count ); + oscs [osc]->output = buf; +} + +inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const +{ + return earliest_irq_; +} + +inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) +{ + dmc.prg_reader_data = user_data; + dmc.prg_reader = func; +} + +inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) +{ + irq_notifier_ = func; + irq_data = user_data; +} + +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_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 + long (bits_remain - 1) * period; +} + +inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp new file mode 100644 index 00000000..480b4aa4 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.cpp @@ -0,0 +1,1084 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Nes_Cpu.h" + +#include "blargg_endian.h" +#include + +#define BLARGG_CPU_X86 1 + +/* 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 */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) + +#include "nes_cpu_io.h" + +#include "blargg_source.h" + +#ifndef CPU_DONE + #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } +#endif + +#ifndef CPU_READ_PPU + #define CPU_READ_PPU( cpu, addr, out, time )\ + {\ + FLUSH_TIME();\ + out = CPU_READ( cpu, addr, time );\ + CACHE_TIME();\ + } +#endif + +#if BLARGG_NONPORTABLE + #define PAGE_OFFSET( addr ) (addr) +#else + #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) +#endif + +inline void Nes_Cpu::set_code_page( int i, void const* p ) +{ + state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size ); +} + +int const st_n = 0x80; +int const st_v = 0x40; +int const st_r = 0x20; +int const st_b = 0x10; +int const st_d = 0x08; +int const st_i = 0x04; +int const st_z = 0x02; +int const st_c = 0x01; + +void Nes_Cpu::reset( void const* unmapped_page ) +{ + check( state == &state_ ); + state = &state_; + r.status = st_i; + r.sp = 0xFF; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + state_.time = 0; + state_.base = 0; + irq_time_ = future_nes_time; + end_time_ = future_nes_time; + error_count_ = 0; + + assert( page_size == 0x800 ); // assumes this + set_code_page( page_count, unmapped_page ); + map_code( 0x2000, 0xE000, unmapped_page, true ); + map_code( 0x0000, 0x2000, low_mem, true ); + + blargg_verify_byte_order(); +} + +void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool mirror ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + + unsigned page = start / page_size; + for ( unsigned n = size / page_size; n; --n ) + { + set_code_page( page++, data ); + if ( !mirror ) + data = (char const*) data + page_size; + } +} + +#define TIME (s_time + s.base) +#define READ_LIKELY_PPU( addr, out ) {CPU_READ_PPU( this, (addr), out, TIME );} +#define READ( addr ) CPU_READ( this, (addr), TIME ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} +#define READ_LOW( addr ) (low_mem [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) +#define READ_PROG( addr ) (s.code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +// even on x86, using short and unsigned char was slower +typedef int fint16; +typedef unsigned fuint16; +typedef unsigned fuint8; + +bool Nes_Cpu::run( nes_time_t end_time ) +{ + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + // even on x86, using s.time in place of s_time was slower + fint16 s_time = s.time; + + // registers + fuint16 pc = r.pc; + fuint8 a = r.a; + fuint8 x = r.x; + fuint8 y = r.y; + fuint16 sp; + SET_SP( r.sp ); + + // status flags + #define IS_NEG (nz & 0x8080) + + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ + } while ( 0 ) + + fuint8 status; + fuint16 c; // carry set if (c & 0x100) != 0 + fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + fuint8 temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +dec_clock_loop: + s_time--; +loop: + + check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) pc < 0x10000 ); + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + check( -32768 <= s_time && s_time < 32767 ); + + uint8_t const* instr = s.code_map [pc >> page_bits]; + fuint8 opcode; + + // TODO: eliminate this special case + #if BLARGG_NONPORTABLE + opcode = instr [pc]; + pc++; + instr += pc; + #else + instr += PAGE_OFFSET( pc ); + opcode = *instr++; + pc++; + #endif + + static uint8_t const clock_table [256] = + {// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 + 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 + 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 + 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 + 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A + 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E + 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F + }; // 0x00 was 7 and 0xF2 was 2 + + fuint16 data; + +#if !BLARGG_CPU_X86 + if ( s_time >= 0 ) + goto out_of_time; + s_time += clock_table [opcode]; + + data = *instr; + + switch ( opcode ) + { +#else + + data = clock_table [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = *instr; + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; +#endif + +// Macros + +#define GET_MSB() (instr [1]) +#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) +#define GET_ADDR() GET_LE16( instr ) + +#define NO_PAGE_CROSSING( lsb ) +#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; + +#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + +#define IND_Y( cross, out ) {\ + fuint16 temp = READ_LOW( data ) + y;\ + out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + cross( temp );\ + } + +#define IND_X( out ) {\ + fuint16 temp = data + x;\ + out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ + } + +#define ARITH_ADDR_MODES( op )\ +case op - 0x04: /* (ind,x) */\ + IND_X( data )\ + goto ptr##op;\ +case op + 0x0C: /* (ind),y */\ + IND_Y( HANDLE_PAGE_CROSSING, data )\ + goto ptr##op;\ +case op + 0x10: /* zp,X */\ + data = uint8_t (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:\ + HANDLE_PAGE_CROSSING( data );\ +case op + 0x08: /* abs */\ + ADD_PAGE();\ +ptr##op:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ +case op + 0x04: /* imm */\ +imm##op: + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + fint16 offset = (BOOST::int8_t) data;\ + fuint16 extra_clock = (++pc & 0xFF) + offset;\ + if ( !(cond) ) goto dec_clock_loop;\ + pc = BOOST::uint16_t (pc + offset);\ + s_time += extra_clock >> 8 & 1;\ + goto loop;\ +} + +// Often-Used + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( uint8_t (data + x) ); + pc++; + goto loop; + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x20: { // JSR + fuint16 temp = pc + 1; + pc = GET_ADDR(); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x4C: // JMP abs + pc = GET_ADDR(); + goto loop; + + case 0xE8: // INX + INC_DEC_XY( x, 1 ) + + case 0x10: // BPL + BRANCH( !IS_NEG ) + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ); + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xC8: // INY + INC_DEC_XY( y, 1 ) + + case 0xA8: // TAY + y = a; + nz = a; + goto loop; + + case 0x98: // TYA + a = y; + nz = y; + goto loop; + + case 0xAD:{// LDA abs + unsigned addr = GET_ADDR(); + pc += 2; + READ_LIKELY_PPU( addr, nz ); + a = nz; + goto loop; + } + + case 0x60: // RTS + pc = 1 + READ_LOW( sp ); + pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); + sp = (sp - 0xFE) | 0x100; + goto loop; + + { + fuint16 addr; + + case 0x99: // STA abs,Y + addr = y + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x8D: // STA abs + addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x9D: // STA abs,X (slightly more common than STA abs) + addr = x + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + sta_ptr: + FLUSH_TIME(); + WRITE( addr, a ); + CACHE_TIME(); + goto loop; + + case 0x91: // STA (ind),Y + IND_Y( NO_PAGE_CROSSING, addr ) + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + IND_X( addr ) + pc++; + goto sta_ptr; + + } + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + + // common read instructions + { + fuint16 addr; + + case 0xA1: // LDA (ind,X) + IND_X( addr ) + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + HANDLE_PAGE_CROSSING( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + HANDLE_PAGE_CROSSING( data + y ); + addr = GET_ADDR() + y; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xBD: // LDA abs,X + HANDLE_PAGE_CROSSING( data + x ); + addr = GET_ADDR() + x; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + a_nz_read_addr: + FLUSH_TIME(); + a = nz = READ( addr ); + CACHE_TIME(); + goto loop; + + } + +// Branch + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + +// Load/store + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (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 = uint8_t (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; + HANDLE_PAGE_CROSSING( data ); + case 0xAC:{// LDY abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xBE: // LDX abs,y + data += y; + HANDLE_PAGE_CROSSING( data ); + case 0xAE:{// LDX abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + { + fuint8 temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + unsigned addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, temp ); + goto loop; + } + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( 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 &= 0xFF; + goto loop; + + case 0xCC:{// CPY abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( 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 &= 0xFF; + goto loop; + +// Logical + + 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; + + case 0x2C:{// BIT abs + unsigned addr = GET_ADDR(); + pc += 2; + status &= ~st_v; + READ_LIKELY_PPU( addr, nz ); + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + } + + case 0x24: // BIT zp + nz = READ_LOW( data ); + pc++; + status &= ~st_v; + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + case 0xEB: // unofficial equivalent + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + fint16 carry = c >> 8 & 1; + fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + status &= ~st_v; + status |= ov >> 2 & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) 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 = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + fint16 temp = c >> 8 & 1; + c = nz; + nz |= temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + ADD_PAGE(); + FLUSH_TIME(); + int temp = READ( 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(); + nz = c >> 8 & 1; + FLUSH_TIME(); + nz |= (c = READ( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (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 = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (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 + + case 0xCA: // DEX + INC_DEC_XY( x, -1 ) + + case 0x88: // DEY + INC_DEC_XY( y, -1 ) + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = (unsigned) -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 = (unsigned) -1; + inc_common: + FLUSH_TIME(); + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xAA: // TAX + x = a; + nz = a; + goto loop; + + case 0x8A: // TXA + a = x; + 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; + +// Stack + + case 0x48: // PHA + PUSH( a ); // verified + goto loop; + + case 0x68: // PLA + a = nz = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + goto loop; + + case 0x40:{// RTI + fuint8 temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xFF) ); + pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; + sp = (sp - 0xFD) | 0x100; + data = status; + SET_STATUS( temp ); + if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) goto loop; + if ( status & st_i ) goto loop; + s_time += delta; + s.base = irq_time_; + goto loop; + } + + case 0x28:{// PLP + fuint8 temp = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + fuint8 changed = status ^ temp; + SET_STATUS( temp ); + if ( !(changed & st_i) ) + goto loop; // I flag didn't change + if ( status & st_i ) + goto handle_sei; + goto handle_cli; + } + + case 0x08: { // PHP + fuint8 temp; + CALC_STATUS( temp ); + PUSH( temp | (st_b | st_r) ); + goto loop; + } + + case 0x6C:{// JMP (ind) + data = GET_ADDR(); + check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space + uint8_t const* page = s.code_map [data >> page_bits]; + pc = page [PAGE_OFFSET( data )]; + data = (data & 0xFF00) | ((data + 1) & 0xFF); + pc |= page [PAGE_OFFSET( data )] << 8; + goto loop; + } + + case 0x00: // BRK + goto handle_brk; + +// Flags + + case 0x38: // SEC + c = (unsigned) ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: { + //dprintf( "CLI at %d\n", TIME ); + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) + { + if ( TIME < irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = irq_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + if ( delta >= s_time + 1 ) + { + s.base += s_time + 1; + s_time = -1; + goto loop; + } + + // TODO: implement + delayed_cli: + dprintf( "Delayed CLI not emulated\n" ); + goto loop; + } + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + + dprintf( "Delayed SEI not emulated\n" ); + goto loop; + } + +// Unofficial + + // SKW - Skip word + case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: + HANDLE_PAGE_CROSSING( data + x ); + case 0x0C: + pc++; + // SKB - Skip byte + case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: + case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: + pc++; + goto loop; + + // NOP + case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: + goto loop; + + case bad_opcode: // HLT + pc--; + if ( pc > 0xFFFF ) + { + // handle wrap-around (assumes caller has put page of HLT at 0x10000) + pc &= 0xFFFF; + goto loop; + } + case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: + case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: + goto stop; + +// Unimplemented + + case 0xFF: // force 256-entry jump table for optimization purposes + c |= 1; + default: + check( (unsigned) opcode <= 0xFF ); + // skip over proper number of bytes + static unsigned char const illop_lens [8] = { + 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0 + }; + fuint8 opcode = instr [-1]; + fint16 len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; + if ( opcode == 0x9C ) + len = 2; + pc += len; + error_count_++; + + if ( (opcode >> 4) == 0x0B ) + { + if ( opcode == 0xB3 ) + data = READ_LOW( data ); + if ( opcode != 0xB7 ) + HANDLE_PAGE_CROSSING( data + y ); + } + goto loop; + } + assert( false ); + + int result_; +handle_brk: + pc++; + result_ = 4; + +interrupt: + { + s_time += 7; + + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); + + sp = (sp - 3) | 0x100; + fuint8 temp; + CALC_STATUS( temp ); + temp |= st_r; + if ( result_ ) + temp |= st_b; // TODO: incorrectly sets B flag for IRQ + WRITE_LOW( sp, temp ); + + this->r.status = status |= st_i; + blargg_long delta = s.base - end_time_; + if ( delta >= 0 ) goto loop; + s_time += delta; + s.base = end_time_; + goto loop; + } + +out_of_time: + pc--; + FLUSH_TIME(); + CPU_DONE( this, TIME, result_ ); + CACHE_TIME(); + if ( result_ >= 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + +stop: + + s.time = s_time; + + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + + { + fuint8 temp; + CALC_STATUS( temp ); + r.status = temp; + } + + this->state_ = s; + this->state = &this->state_; + + return s_time < 0; +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h new file mode 100644 index 00000000..d303b57c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Cpu.h @@ -0,0 +1,114 @@ +// NES 6502 CPU emulator + +// Game_Music_Emu 0.5.2 +#ifndef NES_CPU_H +#define NES_CPU_H + +#include "blargg_common.h" + +typedef blargg_long nes_time_t; // clock cycle count +typedef unsigned nes_addr_t; // 16-bit address +enum { future_nes_time = LONG_MAX / 2 + 1 }; + +class Nes_Cpu { +public: + typedef BOOST::uint8_t uint8_t; + + // Clear registers, map low memory and its three mirrors to address 0, + // and mirror unmapped_page in remaining memory + void reset( void const* unmapped_page = 0 ); + + // Map code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. If mirror is true, repeats code page + // throughout address range. + enum { page_size = 0x800 }; + void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); + + // Access emulated memory as CPU does + uint8_t const* get_code( nes_addr_t ); + + // 2KB of RAM at address 0 + uint8_t low_mem [0x800]; + + // NES 6502 registers. Not kept updated during a call to run(). + struct registers_t { + BOOST::uint16_t pc; + BOOST::uint8_t a; + BOOST::uint8_t x; + BOOST::uint8_t y; + BOOST::uint8_t status; + BOOST::uint8_t sp; + }; + registers_t r; + + // Set end_time and run CPU from current time. Returns true if execution + // stopped due to encountering bad_opcode. + bool run( nes_time_t end_time ); + + // Time of beginning of next instruction to be executed + nes_time_t time() const { return state->time + state->base; } + void set_time( nes_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + nes_time_t irq_time() const { return irq_time_; } + void set_irq_time( nes_time_t ); + + nes_time_t end_time() const { return end_time_; } + void set_end_time( nes_time_t ); + + // Number of undefined instructions encountered and skipped + void clear_error_count() { error_count_ = 0; } + unsigned long error_count() const { return error_count_; } + + // CPU invokes bad opcode handler if it encounters this + enum { bad_opcode = 0xF2 }; + +public: + Nes_Cpu() { state = &state_; } + enum { page_bits = 11 }; + enum { page_count = 0x10000 >> page_bits }; + enum { irq_inhibit = 0x04 }; +private: + struct state_t { + uint8_t const* code_map [page_count + 1]; + nes_time_t base; + int time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + nes_time_t irq_time_; + nes_time_t end_time_; + unsigned long error_count_; + + void set_code_page( int, void const* ); + inline int update_end_time( nes_time_t end, nes_time_t irq ); +}; + +inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) +{ + return state->code_map [addr >> page_bits] + addr + #if !BLARGG_NONPORTABLE + % (unsigned) page_size + #endif + ; +} + +inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) +{ + if ( irq < t && !(r.status & irq_inhibit) ) t = irq; + int delta = state->base - t; + state->base = t; + return delta; +} + +inline void Nes_Cpu::set_irq_time( nes_time_t t ) +{ + state->time += update_end_time( end_time_, (irq_time_ = t) ); +} + +inline void Nes_Cpu::set_end_time( nes_time_t t ) +{ + state->time += update_end_time( (end_time_ = t), irq_time_ ); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp new file mode 100644 index 00000000..c058f6b1 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.cpp @@ -0,0 +1,121 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Nes_Fme7_Apu.h" + +#include + +/* 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; + osc_output->set_modified(); + + // 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; + synth.offset( last_time, delta, osc_output ); + } + } + + blip_time_t time = last_time + delays [index]; + if ( time < end_time ) + { + int delta = amp * 2 - volume; + 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 += (blargg_long) count * period; + } + } + + delays [index] = time - end_time; + } + + last_time = end_time; +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h new file mode 100644 index 00000000..eb60af03 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Fme7_Apu.h @@ -0,0 +1,131 @@ +// Sunsoft FME-7 sound emulator + +// Game_Music_Emu 0.5.2 +#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 output( Blip_Buffer* ); + enum { osc_count = 3 }; + void osc_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 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::osc_output( int i, Blip_Buffer* buf ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = buf; +} + +inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +inline Nes_Fme7_Apu::Nes_Fme7_Apu() +{ + 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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp new file mode 100644 index 00000000..f3235b38 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.cpp @@ -0,0 +1,145 @@ +// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ + +#include "Nes_Namco_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_Namco_Apu::Nes_Namco_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +void Nes_Namco_Apu::reset() +{ + last_time = 0; + addr_reg = 0; + + int i; + for ( i = 0; i < reg_count; i++ ) + reg [i] = 0; + + for ( i = 0; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + osc.delay = 0; + osc.last_amp = 0; + osc.wave_pos = 0; + } +} + +void Nes_Namco_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +/* +void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) +{ + reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); + + static const char hex [17] = "0123456789ABCDEF"; + int i; + for ( i = 0; i < reg_count; i++ ) + reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); + + for ( i = 0; i < osc_count; i++ ) + { + reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); + reflect_int16( data, BLARGG_4CHAR('P','O','S','0') + i, &oscs [i].wave_pos ); + } +} +*/ + +void Nes_Namco_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) +{ + int active_oscs = (reg [0x7F] >> 4 & 7) + 1; + for ( int i = osc_count - active_oscs; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + Blip_Buffer* output = osc.output; + if ( !output ) + continue; + output->set_modified(); + + blip_resampled_time_t time = + output->resampled_time( last_time ) + osc.delay; + blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); + osc.delay = 0; + if ( time < end_time ) + { + const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; + if ( !(osc_reg [4] & 0xE0) ) + continue; + + int volume = osc_reg [7] & 15; + if ( !volume ) + continue; + + blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; + if ( freq < 64 * active_oscs ) + continue; // prevent low frequencies from excessively delaying freq changes + blip_resampled_time_t period = + output->resampled_duration( 983040 ) / freq * active_oscs; + + int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; + if ( !wave_size ) + continue; + + int last_amp = osc.last_amp; + int wave_pos = osc.wave_pos; + + do + { + // read wave sample + int addr = wave_pos + osc_reg [6]; + int sample = reg [addr >> 1] >> (addr << 2 & 4); + wave_pos++; + sample = (sample & 15) * volume; + + // output impulse if amplitude changed + int delta = sample - last_amp; + if ( delta ) + { + last_amp = sample; + synth.offset_resampled( time, delta, output ); + } + + // next sample + time += period; + if ( wave_pos >= wave_size ) + wave_pos = 0; + } + while ( time < end_time ); + + osc.wave_pos = wave_pos; + osc.last_amp = last_amp; + } + osc.delay = time - end_time; + } + + last_time = nes_end_time; +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h new file mode 100644 index 00000000..db5fea4b --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Namco_Apu.h @@ -0,0 +1,102 @@ +// Namco 106 sound chip emulator + +// Nes_Snd_Emu 0.1.8 +#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 output( Blip_Buffer* ); + enum { osc_count = 8 }; + void osc_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 { + blargg_long 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 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 * 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::osc_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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp new file mode 100644 index 00000000..1ad3f59c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.cpp @@ -0,0 +1,551 @@ +// Nes_Snd_Emu 0.1.8. 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_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 += (blargg_long) 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; + } + + output->set_modified(); + + 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 ) { + 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; + + { + 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_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 += (blargg_long) 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; + } + + output->set_modified(); + + // 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 ) + 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; + } + + 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 byte const dac_table [128] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, + 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, + 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, + 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, + 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, + 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, + 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, + 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, +}; + +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 ) + { + int old_dac = dac; + dac = data & 0x7F; + + // adjust last_amp so that "pop" amplitude will be properly non-linear + // with respect to change in dac + int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); + if ( !nonlinear ) + last_amp = faked_nonlinear; + } +} + +void Nes_Dmc::start() +{ + reload_sample(); + fill_buffer(); + recalc_irq(); +} + +void Nes_Dmc::fill_buffer() +{ + if ( !buf_full && length_counter ) + { + require( prg_reader ); // prg_reader must be set + buf = prg_reader( prg_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( dac ); + if ( !output ) + { + silence = true; + } + else + { + output->set_modified(); + if ( delta ) + 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; + + do + { + if ( !silence ) + { + int step = (bits & 1) * 4 - 2; + bits >>= 1; + if ( unsigned (dac + step) <= 0x7F ) { + dac += step; + synth.offset_inline( time, step, 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->last_amp = 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; + } + + output->set_modified(); + + const int volume = this->volume(); + int amp = (noise & 1) ? volume : 0; + { + int delta = update_amp( amp ); + if ( delta ) + 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); + + 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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h new file mode 100644 index 00000000..b675bfb4 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Oscs.h @@ -0,0 +1,147 @@ +// Private oscillators used by Nes_Apu + +// Nes_Snd_Emu 0.1.8 +#ifndef NES_OSCS_H +#define NES_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Nes_Apu; + +struct Nes_Osc +{ + 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 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 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 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; + + int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function + void* prg_reader_data; + + Nes_Apu* apu; + + Blip_Synth synth; + + 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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp new file mode 100644 index 00000000..d178407c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.cpp @@ -0,0 +1,215 @@ +// Nes_Snd_Emu 0.1.8. 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" + +Nes_Vrc6_Apu::Nes_Vrc6_Apu() +{ + output( NULL ); + volume( 1.0 ); + reset(); +} + +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; + } +} + +void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, buf ); +} + +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; + output->set_modified(); + + 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; + 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; + + 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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h new file mode 100644 index 00000000..18722233 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nes_Vrc6_Apu.h @@ -0,0 +1,95 @@ +// Konami VRC6 sound chip emulator + +// Nes_Snd_Emu 0.1.8 +#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 output( Blip_Buffer* ); + enum { osc_count = 3 }; + void osc_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) * 0x100L + regs [1] + 1; + } + }; + + Vrc6_Osc oscs [osc_count]; + blip_time_t last_time; + + Blip_Synth saw_synth; + Blip_Synth 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::osc_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/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp new file mode 100644 index 00000000..678bddb2 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.cpp @@ -0,0 +1,557 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Nsf_Emu.h" + +#include "blargg_endian.h" +#include +#include + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" + #include "Nes_Vrc6_Apu.h" + #include "Nes_Fme7_Apu.h" +#endif + +/* 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" + +int const vrc6_flag = 0x01; +int const namco_flag = 0x10; +int const fme7_flag = 0x20; + +long const clock_divisor = 12; + +Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80 }; +Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80 }; + +int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr ) +{ + return *((Nsf_Emu*) emu)->cpu::get_code( addr ); +} + +Nsf_Emu::Nsf_Emu() +{ + vrc6 = 0; + namco = 0; + fme7 = 0; + + set_type( gme_nsf_type ); + set_silence_lookahead( 6 ); + apu.dmc_reader( pcm_read, this ); + Music_Emu::set_equalizer( nes_eq ); + set_gain( 1.4 ); + memset( unmapped_code, Nes_Cpu::bad_opcode, sizeof unmapped_code ); +} + +Nsf_Emu::~Nsf_Emu() { unload(); } + +void Nsf_Emu::unload() +{ + #if !NSF_EMU_APU_ONLY + { + delete vrc6; + vrc6 = 0; + + delete namco; + namco = 0; + + delete fme7; + fme7 = 0; + } + #endif + + rom.clear(); + Music_Emu::unload(); +} + +// Track info + +static void copy_nsf_fields( Nsf_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 ); + if ( h.chip_flags ) + Gme_File::copy_field_( out->system, "Famicom" ); +} + +blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const +{ + copy_nsf_fields( header_, out ); + return 0; +} + +static blargg_err_t check_nsf_header( void const* header ) +{ + if ( memcmp( header, "NESM\x1A", 5 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Nsf_File : Gme_Info_ +{ + Nsf_Emu::header_t h; + + Nsf_File() { set_type( gme_nsf_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + blargg_err_t err = in.read( &h, Nsf_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + + if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) + set_warning( "Uses unsupported audio expansion hardware" ); + + set_track_count( h.track_count ); + return check_nsf_header( &h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_nsf_fields( h, out ); + return 0; + } +}; + +static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } +static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } + +gme_type_t_ const gme_nsf_type [1] = { "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }; + +// Setup + +void Nsf_Emu::set_tempo_( double t ) +{ + unsigned playback_rate = get_le16( header_.ntsc_speed ); + unsigned standard_rate = 0x411A; + clock_rate_ = 1789772.72727; + play_period = 262 * 341L * 4 - 2; // two fewer PPU clocks every four frames + + if ( pal_only ) + { + play_period = 33247 * clock_divisor; + clock_rate_ = 1662607.125; + standard_rate = 0x4E20; + playback_rate = get_le16( header_.pal_speed ); + } + + if ( !playback_rate ) + playback_rate = standard_rate; + + if ( playback_rate != standard_rate || t != 1.0 ) + play_period = long (playback_rate * clock_rate_ / (1000000.0 / clock_divisor * t)); + + apu.set_tempo( t ); +} + +blargg_err_t Nsf_Emu::init_sound() +{ + if ( header_.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) + set_warning( "Uses unsupported audio expansion hardware" ); + + { + #define APU_NAMES "Square 1", "Square 2", "Triangle", "Noise", "DMC" + + int const count = Nes_Apu::osc_count; + static const char* const apu_names [count] = { APU_NAMES }; + set_voice_count( count ); + set_voice_names( apu_names ); + + } + + static int const types [] = { + wave_type | 1, wave_type | 2, wave_type | 0, + noise_type | 0, mixed_type | 1, + wave_type | 3, wave_type | 4, wave_type | 5, + wave_type | 6, wave_type | 7, wave_type | 8, wave_type | 9, + wave_type |10, wave_type |11, wave_type |12, wave_type |13 + }; + set_voice_types( types ); // common to all sound chip configurations + + double adjusted_gain = gain(); + + #if NSF_EMU_APU_ONLY + { + if ( header_.chip_flags ) + set_warning( "Uses unsupported audio expansion hardware" ); + } + #else + { + if ( header_.chip_flags & (namco_flag | vrc6_flag | fme7_flag) ) + set_voice_count( Nes_Apu::osc_count + 3 ); + + if ( header_.chip_flags & namco_flag ) + { + namco = BLARGG_NEW Nes_Namco_Apu; + CHECK_ALLOC( namco ); + adjusted_gain *= 0.75; + + int const count = Nes_Apu::osc_count + Nes_Namco_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8" + }; + set_voice_count( count ); + set_voice_names( names ); + } + + if ( header_.chip_flags & vrc6_flag ) + { + vrc6 = BLARGG_NEW Nes_Vrc6_Apu; + CHECK_ALLOC( vrc6 ); + adjusted_gain *= 0.75; + + { + int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Saw Wave", "Square 3", "Square 4" + }; + set_voice_count( count ); + set_voice_names( names ); + } + + if ( header_.chip_flags & namco_flag ) + { + int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count + + Nes_Namco_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Saw Wave", "Square 3", "Square 4", + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8" + }; + set_voice_count( count ); + set_voice_names( names ); + } + } + + if ( header_.chip_flags & fme7_flag ) + { + fme7 = BLARGG_NEW Nes_Fme7_Apu; + CHECK_ALLOC( fme7 ); + adjusted_gain *= 0.75; + + int const count = Nes_Apu::osc_count + Nes_Fme7_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Square 3", "Square 4", "Square 5" + }; + set_voice_count( count ); + set_voice_names( names ); + } + + if ( namco ) namco->volume( adjusted_gain ); + if ( vrc6 ) vrc6 ->volume( adjusted_gain ); + if ( fme7 ) fme7 ->volume( adjusted_gain ); + } + #endif + + apu.volume( adjusted_gain ); + + return 0; +} + +blargg_err_t Nsf_Emu::load_( Data_Reader& in ) +{ + assert( offsetof (header_t,unused [4]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); + + set_track_count( header_.track_count ); + RETURN_ERR( check_nsf_header( &header_ ) ); + + if ( header_.vers != 1 ) + set_warning( "Unknown file version" ); + + // sound and memory + blargg_err_t err = init_sound(); + if ( err ) + return err; + + // set up data + nes_addr_t load_addr = get_le16( header_.load_addr ); + init_addr = get_le16( header_.init_addr ); + play_addr = get_le16( header_.play_addr ); + if ( !load_addr ) load_addr = rom_begin; + if ( !init_addr ) init_addr = rom_begin; + if ( !play_addr ) play_addr = rom_begin; + if ( load_addr < rom_begin || init_addr < rom_begin ) + { + const char* w = warning(); + if ( !w ) + w = "Corrupt file (invalid load/init/play address)"; + return w; + } + + rom.set_addr( load_addr % bank_size ); + int total_banks = rom.size() / bank_size; + + // bank switching + int first_bank = (load_addr - rom_begin) / bank_size; + for ( int i = 0; i < bank_count; i++ ) + { + unsigned bank = i - first_bank; + if ( bank >= (unsigned) total_banks ) + bank = 0; + initial_banks [i] = bank; + + if ( header_.banks [i] ) + { + // bank-switched + memcpy( initial_banks, header_.banks, sizeof initial_banks ); + break; + } + } + + pal_only = (header_.speed_flags & 3) == 1; + + #if !NSF_EMU_EXTRA_FLAGS + header_.speed_flags = 0; + #endif + + set_tempo( tempo() ); + + return setup_buffer( (long) (clock_rate_ + 0.5) ); +} + +void Nsf_Emu::update_eq( blip_eq_t const& eq ) +{ + apu.treble_eq( eq ); + + #if !NSF_EMU_APU_ONLY + { + if ( namco ) namco->treble_eq( eq ); + if ( vrc6 ) vrc6 ->treble_eq( eq ); + if ( fme7 ) fme7 ->treble_eq( eq ); + } + #endif +} + +void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) +{ + if ( i < Nes_Apu::osc_count ) + { + apu.osc_output( i, buf ); + return; + } + i -= Nes_Apu::osc_count; + + #if !NSF_EMU_APU_ONLY + { + if ( fme7 && i < Nes_Fme7_Apu::osc_count ) + { + fme7->osc_output( i, buf ); + return; + } + + if ( vrc6 ) + { + if ( i < Nes_Vrc6_Apu::osc_count ) + { + // put saw first + if ( --i < 0 ) + i = 2; + vrc6->osc_output( i, buf ); + return; + } + i -= Nes_Vrc6_Apu::osc_count; + } + + if ( namco && i < Nes_Namco_Apu::osc_count ) + { + namco->osc_output( i, buf ); + return; + } + } + #endif +} + +// Emulation + +// see nes_cpu_io.h for read/write functions + +void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) +{ + #if !NSF_EMU_APU_ONLY + { + if ( namco ) + { + switch ( addr ) + { + case Nes_Namco_Apu::data_reg_addr: + namco->write_data( time(), data ); + return; + + case Nes_Namco_Apu::addr_reg_addr: + namco->write_addr( data ); + return; + } + } + + if ( addr >= Nes_Fme7_Apu::latch_addr && fme7 ) + { + switch ( addr & Nes_Fme7_Apu::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 ( vrc6 ) + { + unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1); + unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step; + if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count ) + { + vrc6->write_osc( time(), osc, reg, data ); + return; + } + } + } + #endif + + // unmapped write + + #ifndef NDEBUG + { + // some games write to $8000 and $8001 repeatedly + if ( addr == 0x8000 || addr == 0x8001 ) return; + + // probably namco sound mistakenly turned on in mck + if ( addr == 0x4800 || addr == 0xF800 ) return; + + // memory mapper? + if ( addr == 0xFFF8 ) return; + + dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data ); + } + #endif +} + +blargg_err_t Nsf_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( low_mem, 0, sizeof low_mem ); + memset( sram, 0, sizeof sram ); + + cpu::reset( unmapped_code ); // also maps low_mem + cpu::map_code( sram_addr, sizeof sram, sram ); + for ( int i = 0; i < bank_count; ++i ) + cpu_write( bank_select_addr + i, initial_banks [i] ); + + apu.reset( pal_only, (header_.speed_flags & 0x20) ? 0x3F : 0 ); + apu.write_register( 0, 0x4015, 0x0F ); + apu.write_register( 0, 0x4017, (header_.speed_flags & 0x10) ? 0x80 : 0 ); + #if !NSF_EMU_APU_ONLY + { + if ( namco ) namco->reset(); + if ( vrc6 ) vrc6 ->reset(); + if ( fme7 ) fme7 ->reset(); + } + #endif + + play_ready = 4; + play_extra = 0; + next_play = play_period / clock_divisor; + + saved_state.pc = badop_addr; + low_mem [0x1FF] = (badop_addr - 1) >> 8; + low_mem [0x1FE] = (badop_addr - 1) & 0xFF; + r.sp = 0xFD; + r.pc = init_addr; + r.a = track; + r.x = pal_only; + + return 0; +} + +blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) +{ + set_time( 0 ); + while ( time() < duration ) + { + nes_time_t end = min( (blip_time_t) next_play, duration ); + end = min( end, time() + 32767 ); // allows CPU to use 16-bit time delta + if ( cpu::run( end ) ) + { + if ( r.pc != badop_addr ) + { + set_warning( "Emulation error (illegal instruction)" ); + r.pc++; + } + else + { + play_ready = 1; + if ( saved_state.pc != badop_addr ) + { + cpu::r = saved_state; + saved_state.pc = badop_addr; + } + else + { + set_time( end ); + } + } + } + + if ( time() >= next_play ) + { + nes_time_t period = (play_period + play_extra) / clock_divisor; + play_extra = play_period - period * clock_divisor; + next_play += period; + if ( play_ready && !--play_ready ) + { + check( saved_state.pc == badop_addr ); + if ( r.pc != badop_addr ) + saved_state = cpu::r; + + r.pc = play_addr; + low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8; + low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF; + GME_FRAME_HOOK( this ); + } + } + } + + if ( cpu::error_count() ) + { + cpu::clear_error_count(); + set_warning( "Emulation error (illegal instruction)" ); + } + + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + if ( next_play < 0 ) + next_play = 0; + + apu.end_frame( duration ); + + #if !NSF_EMU_APU_ONLY + { + if ( namco ) namco->end_frame( duration ); + if ( vrc6 ) vrc6 ->end_frame( duration ); + if ( fme7 ) fme7 ->end_frame( duration ); + } + #endif + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h new file mode 100644 index 00000000..e06b9172 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsf_Emu.h @@ -0,0 +1,106 @@ +// Nintendo NES/Famicom NSF music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef NSF_EMU_H +#define NSF_EMU_H + +#include "Classic_Emu.h" +#include "Nes_Apu.h" +#include "Nes_Cpu.h" + +class Nsf_Emu : private Nes_Cpu, public Classic_Emu { + typedef Nes_Cpu cpu; +public: + // Equalizer profiles for US NES and Japanese Famicom + static equalizer_t const nes_eq; + static equalizer_t const famicom_eq; + + // NSF file header + enum { header_size = 0x80 }; + struct header_t + { + 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]; + 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]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_nsf_type; } + +public: + // deprecated + Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + +public: + Nsf_Emu(); + ~Nsf_Emu(); + Nes_Apu* apu_() { return &apu; } +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_( Data_Reader& ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); + void unload(); +protected: + enum { bank_count = 8 }; + byte initial_banks [bank_count]; + nes_addr_t init_addr; + nes_addr_t play_addr; + double clock_rate_; + bool pal_only; + + // timing + Nes_Cpu::registers_t saved_state; + nes_time_t next_play; + nes_time_t play_period; + int play_extra; + int play_ready; + + enum { rom_begin = 0x8000 }; + enum { bank_select_addr = 0x5FF8 }; + enum { bank_size = 0x1000 }; + Rom_Data rom; + +public: private: friend class Nes_Cpu; + void cpu_jsr( nes_addr_t ); + int cpu_read( nes_addr_t ); + void cpu_write( nes_addr_t, int ); + void cpu_write_misc( nes_addr_t, int ); + enum { badop_addr = bank_select_addr }; + +private: + class Nes_Namco_Apu* namco; + class Nes_Vrc6_Apu* vrc6; + class Nes_Fme7_Apu* fme7; + Nes_Apu apu; + static int pcm_read( void*, nes_addr_t ); + blargg_err_t init_sound(); + + header_t header_; + + enum { sram_addr = 0x6000 }; + byte sram [0x2000]; + byte unmapped_code [Nes_Cpu::page_size + 8]; +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp new file mode 100644 index 00000000..0a785e60 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.cpp @@ -0,0 +1,330 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Nsfe_Emu.h" + +#include "blargg_endian.h" +#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" + +Nsfe_Info::Nsfe_Info() { playlist_disabled = false; } + +Nsfe_Info::~Nsfe_Info() { } + +inline void Nsfe_Info::unload() +{ + track_name_data.clear(); + track_names.clear(); + playlist.clear(); + track_times.clear(); +} + +// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ? +void Nsfe_Info::disable_playlist( bool b ) +{ + playlist_disabled = b; + info.track_count = playlist.size(); + if ( !info.track_count || playlist_disabled ) + info.track_count = actual_track_count_; +} + +int Nsfe_Info::remap_track( int track ) const +{ + if ( !playlist_disabled && (unsigned) track < playlist.size() ) + track = playlist [track]; + return track; +} + +// Read multiple strings and separate into individual strings +static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector& chars, + blargg_vector& strs ) +{ + RETURN_ERR( chars.resize( size + 1 ) ); + chars [size] = 0; // in case last string doesn't have terminator + RETURN_ERR( in.read( &chars [0], size ) ); + + RETURN_ERR( strs.resize( 128 ) ); + int count = 0; + for ( int i = 0; i < size; i++ ) + { + if ( (int) strs.size() <= count ) + RETURN_ERR( strs.resize( count * 2 ) ); + strs [count++] = &chars [i]; + while ( i < size && chars [i] ) + i++; + } + + return strs.resize( count ); +} + +// Copy in to out, where out has out_max characters allocated. Truncate to +// out_max - 1 characters. +static void copy_str( const char* in, char* out, int out_max ) +{ + out [out_max - 1] = 0; + strncpy( out, in, out_max - 1 ); +} + +struct nsfe_info_t +{ + byte load_addr [2]; + byte init_addr [2]; + byte play_addr [2]; + byte speed_flags; + byte chip_flags; + byte track_count; + byte first_track; + byte unused [6]; +}; + +blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) +{ + int const nsfe_info_size = 16; + assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); + + // check header + byte signature [4]; + blargg_err_t err = in.read( signature, sizeof signature ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); + if ( memcmp( signature, "NSFE", 4 ) ) + return gme_wrong_file_type; + + // free previous info + track_name_data.clear(); + track_names.clear(); + playlist.clear(); + track_times.clear(); + + // default nsf header + static const Nsf_Emu::header_t base_header = + { + {'N','E','S','M','\x1A'},// tag + 1, // version + 1, 1, // track count, first track + {0,0},{0,0},{0,0}, // addresses + "","","", // strings + {0x1A, 0x41}, // NTSC rate + {0,0,0,0,0,0,0,0}, // banks + {0x20, 0x4E}, // PAL rate + 0, 0, // flags + {0,0,0,0} // unused + }; + Nsf_Emu::header_t& header = info; + header = base_header; + + // parse tags + int phase = 0; + while ( phase != 3 ) + { + // read size and tag + byte block_header [2] [4]; + RETURN_ERR( in.read( block_header, sizeof block_header ) ); + blargg_long size = get_le32( block_header [0] ); + blargg_long tag = get_le32( block_header [1] ); + + //dprintf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); + + switch ( tag ) + { + case BLARGG_4CHAR('O','F','N','I'): { + check( phase == 0 ); + if ( size < 8 ) + return "Corrupt file"; + + nsfe_info_t finfo; + finfo.track_count = 1; + finfo.first_track = 0; + + RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) ); + if ( size > nsfe_info_size ) + RETURN_ERR( in.skip( size - nsfe_info_size ) ); + phase = 1; + info.speed_flags = finfo.speed_flags; + info.chip_flags = finfo.chip_flags; + info.track_count = finfo.track_count; + this->actual_track_count_ = finfo.track_count; + info.first_track = finfo.first_track; + memcpy( info.load_addr, finfo.load_addr, 2 * 3 ); + break; + } + + case BLARGG_4CHAR('K','N','A','B'): + if ( size > (int) sizeof info.banks ) + return "Corrupt file"; + RETURN_ERR( in.read( info.banks, size ) ); + break; + + case BLARGG_4CHAR('h','t','u','a'): { + blargg_vector chars; + blargg_vector strs; + RETURN_ERR( read_strs( in, size, chars, strs ) ); + int n = strs.size(); + + if ( n > 3 ) + copy_str( strs [3], info.dumper, sizeof info.dumper ); + + if ( n > 2 ) + copy_str( strs [2], info.copyright, sizeof info.copyright ); + + if ( n > 1 ) + copy_str( strs [1], info.author, sizeof info.author ); + + if ( n > 0 ) + copy_str( strs [0], info.game, sizeof info.game ); + + break; + } + + case BLARGG_4CHAR('e','m','i','t'): + RETURN_ERR( track_times.resize( size / 4 ) ); + RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); + break; + + case BLARGG_4CHAR('l','b','l','t'): + RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); + break; + + case BLARGG_4CHAR('t','s','l','p'): + RETURN_ERR( playlist.resize( size ) ); + RETURN_ERR( in.read( &playlist [0], size ) ); + break; + + case BLARGG_4CHAR('A','T','A','D'): { + check( phase == 1 ); + phase = 2; + if ( !nsf_emu ) + { + RETURN_ERR( in.skip( size ) ); + } + else + { + Subset_Reader sub( &in, size ); // limit emu to nsf data + Remaining_Reader rem( &header, Nsf_Emu::header_size, &sub ); + RETURN_ERR( nsf_emu->load( rem ) ); + check( rem.remain() == 0 ); + } + break; + } + + case BLARGG_4CHAR('D','N','E','N'): + check( phase == 2 ); + phase = 3; + break; + + default: + // tags that can be skipped start with a lowercase character + check( islower( (tag >> 24) & 0xFF ) ); + RETURN_ERR( in.skip( size ) ); + break; + } + } + + return 0; +} + +blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const +{ + int remapped = remap_track( track ); + if ( (unsigned) remapped < track_times.size() ) + { + long length = (BOOST::int32_t) get_le32( track_times [remapped] ); + if ( length > 0 ) + out->length = length; + } + if ( (unsigned) remapped < track_names.size() ) + Gme_File::copy_field_( out->song, track_names [remapped] ); + + GME_COPY_FIELD( info, out, game ); + GME_COPY_FIELD( info, out, author ); + GME_COPY_FIELD( info, out, copyright ); + GME_COPY_FIELD( info, out, dumper ); + return 0; +} + +Nsfe_Emu::Nsfe_Emu() +{ + loading = false; + set_type( gme_nsfe_type ); +} + +Nsfe_Emu::~Nsfe_Emu() { } + +void Nsfe_Emu::unload() +{ + if ( !loading ) + info.unload(); // TODO: extremely hacky! + Nsf_Emu::unload(); +} + +blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const +{ + return info.track_info_( out, track ); +} + +struct Nsfe_File : Gme_Info_ +{ + Nsfe_Info info; + + Nsfe_File() { set_type( gme_nsfe_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + RETURN_ERR( info.load( in, 0 ) ); + info.disable_playlist( false ); + set_track_count( info.info.track_count ); + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + return info.track_info_( out, track ); + } +}; + +static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } +static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } + +gme_type_t_ const gme_nsfe_type [1] = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }; + +blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) +{ + if ( loading ) + return Nsf_Emu::load_( in ); + + // TODO: this hacky recursion-avoidance could have subtle problems + loading = true; + blargg_err_t err = info.load( in, this ); + loading = false; + disable_playlist( false ); + return err; +} + +void Nsfe_Emu::disable_playlist( bool b ) +{ + info.disable_playlist( b ); + set_track_count( info.info.track_count ); +} + +void Nsfe_Emu::clear_playlist_() +{ + disable_playlist(); + Nsf_Emu::clear_playlist_(); +} + +blargg_err_t Nsfe_Emu::start_track_( int track ) +{ + return Nsf_Emu::start_track_( info.remap_track( track ) ); +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h new file mode 100644 index 00000000..561c3be0 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Nsfe_Emu.h @@ -0,0 +1,68 @@ +// Nintendo NES/Famicom NSFE music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef NSFE_EMU_H +#define NSFE_EMU_H + +#include "blargg_common.h" +#include "Nsf_Emu.h" + +// Allows reading info from NSFE file without creating emulator +class Nsfe_Info { +public: + blargg_err_t load( Data_Reader&, Nsf_Emu* ); + + struct info_t : Nsf_Emu::header_t + { + char game [256]; + char author [256]; + char copyright [256]; + char dumper [256]; + } info; + + 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(); + + Nsfe_Info(); + ~Nsfe_Info(); +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; } + +public: + // deprecated + struct header_t { char tag [4]; }; + Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + void disable_playlist( bool = true ); // use clear_playlist() + +public: + Nsfe_Emu(); + ~Nsfe_Emu(); +protected: + blargg_err_t load_( Data_Reader& ); + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t start_track_( int ); + void unload(); + void clear_playlist_(); +private: + Nsfe_Info info; + bool loading; +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp new file mode 100644 index 00000000..23fa9072 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.cpp @@ -0,0 +1,334 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Sap_Apu.h" + +#include + +/* 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 max_frequency = 12000; // pure waves above this frequency are silenced + +static void gen_poly( blargg_ulong mask, int count, byte* out ) +{ + blargg_ulong 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; +blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; +blargg_ulong const poly5 = 0x167C6EA1; + +inline blargg_ulong run_poly5( blargg_ulong in, int shift ) +{ + return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); +} + +#define POLY_MASK( width, tap1, tap2 ) \ + ((1UL << (width - 1 - tap1)) | (1UL << (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 ); + blargg_ulong n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L + + poly5 [1] * 0x100L + poly5 [0]; + blargg_ulong rev = n & 1; + for ( int i = 1; i < poly5_len; i++ ) + rev |= (n >> i & 1) << (poly5_len - i); + dprintf( "poly5: 0x%08lX\n", rev ); + } +} + +Sap_Apu::Sap_Apu() +{ + impl = 0; + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, 0 ); +} + +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 + blargg_long 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 * 0x100L + 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 ) + { + output->set_modified(); + + 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; + 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 + blargg_ulong 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; + } + + // 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 ) + { + blargg_long 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, unsigned addr, int data ) +{ + run_until( time ); + int i = (addr ^ 0xD200) >> 1; + if ( 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; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h new file mode 100644 index 00000000..c71ce31e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Apu.h @@ -0,0 +1,77 @@ +// Atari POKEY sound chip emulator + +// Game_Music_Emu 0.5.2 +#ifndef SAP_APU_H +#define SAP_APU_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +class Sap_Apu_Impl; + +class Sap_Apu { +public: + enum { osc_count = 4 }; + void osc_output( int index, Blip_Buffer* ); + + void reset( Sap_Apu_Impl* ); + + enum { start_addr = 0xD200 }; + enum { end_addr = 0xD209 }; + void write_data( blip_time_t, unsigned addr, int data ); + + void end_frame( blip_time_t ); + +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 = (1L << 4) - 1 }; + enum { poly9_len = (1L << 9) - 1 }; + enum { poly17_len = (1L << 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: + Blip_Synth synth; + + Sap_Apu_Impl(); + void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } + +private: + typedef unsigned char byte; + byte poly4 [Sap_Apu::poly4_len / 8 + 1]; + byte poly9 [Sap_Apu::poly9_len / 8 + 1]; + byte poly17 [Sap_Apu::poly17_len / 8 + 1]; + friend class Sap_Apu; +}; + +inline void Sap_Apu::osc_output( int i, Blip_Buffer* b ) +{ + assert( (unsigned) i < osc_count ); + oscs [i].output = b; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp new file mode 100644 index 00000000..10dc6061 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.cpp @@ -0,0 +1,1011 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Sap_Cpu.h" + +#include +#include "blargg_endian.h" + +//#include "nes_cpu_log.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 */ + +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) + +#include "sap_cpu_io.h" + +#ifndef CPU_DONE + #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } +#endif + +#include "blargg_source.h" + +int const st_n = 0x80; +int const st_v = 0x40; +int const st_r = 0x20; +int const st_b = 0x10; +int const st_d = 0x08; +int const st_i = 0x04; +int const st_z = 0x02; +int const st_c = 0x01; + +void Sap_Cpu::reset( void* new_mem ) +{ + check( state == &state_ ); + state = &state_; + mem = (uint8_t*) new_mem; + r.status = st_i; + r.sp = 0xFF; + r.pc = 0; + r.a = 0; + r.x = 0; + r.y = 0; + state_.time = 0; + state_.base = 0; + irq_time_ = future_sap_time; + end_time_ = future_sap_time; + + blargg_verify_byte_order(); +} + +#define TIME (s_time + s.base) +#define READ( addr ) CPU_READ( this, (addr), TIME ) +#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} +#define READ_LOW( addr ) (mem [int (addr)]) +#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) +#define READ_PROG( addr ) (READ_LOW( addr )) + +#define SET_SP( v ) (sp = ((v) + 1) | 0x100) +#define GET_SP() ((sp - 1) & 0xFF) +#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) + +// even on x86, using short and unsigned char was slower +typedef int fint16; +typedef unsigned fuint16; +typedef unsigned fuint8; +typedef blargg_long fint32; + +bool Sap_Cpu::run( sap_time_t end_time ) +{ + bool illegal_encountered = false; + set_end_time( end_time ); + state_t s = this->state_; + this->state = &s; + fint32 s_time = s.time; + uint8_t* const mem = this->mem; // cache + + // registers + fuint16 pc = r.pc; + fuint8 a = r.a; + fuint8 x = r.x; + fuint8 y = r.y; + fuint16 sp; + SET_SP( r.sp ); + + // status flags + #define IS_NEG (nz & 0x8080) + + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ + } while ( 0 ) + + fuint8 status; + fuint16 c; // carry set if (c & 0x100) != 0 + fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + { + fuint8 temp = r.status; + SET_STATUS( temp ); + } + + goto loop; +dec_clock_loop: + s_time--; +loop: + + #ifndef NDEBUG + { + sap_time_t correct = end_time_; + if ( !(status & st_i) && correct > irq_time_ ) + correct = irq_time_; + check( s.base == correct ); + } + #endif + + check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + fuint8 opcode = mem [pc]; + pc++; + uint8_t const* instr = mem + pc; + + static uint8_t const clock_table [256] = + {// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 + 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 + 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 + 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 + 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A + 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E + 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F + }; // 0x00 was 7 + + fuint16 data; + data = clock_table [opcode]; + if ( (s_time += data) >= 0 ) + goto possibly_out_of_time; +almost_out_of_time: + + data = *instr; + + #ifdef NES_CPU_LOG_H + nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] ); + #endif + + switch ( opcode ) + { +possibly_out_of_time: + if ( s_time < (int) data ) + goto almost_out_of_time; + s_time -= data; + goto out_of_time; + +// Macros + +#define GET_MSB() (instr [1]) +#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) +#define GET_ADDR() GET_LE16( instr ) + +#define NO_PAGE_CROSSING( lsb ) +#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; + +#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; + +#define IND_Y( cross, out ) {\ + fuint16 temp = READ_LOW( data ) + y;\ + out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + cross( temp );\ + } + +#define IND_X( out ) {\ + fuint16 temp = data + x;\ + out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ + } + +#define ARITH_ADDR_MODES( op )\ +case op - 0x04: /* (ind,x) */\ + IND_X( data )\ + goto ptr##op;\ +case op + 0x0C: /* (ind),y */\ + IND_Y( HANDLE_PAGE_CROSSING, data )\ + goto ptr##op;\ +case op + 0x10: /* zp,X */\ + data = uint8_t (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:\ + HANDLE_PAGE_CROSSING( data );\ +case op + 0x08: /* abs */\ + ADD_PAGE();\ +ptr##op:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ +case op + 0x04: /* imm */\ +imm##op: + +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + fint16 offset = (BOOST::int8_t) data;\ + fuint16 extra_clock = (++pc & 0xFF) + offset;\ + if ( !(cond) ) goto dec_clock_loop;\ + pc += offset;\ + s_time += extra_clock >> 8 & 1;\ + goto loop;\ +} + +// Often-Used + + case 0xB5: // LDA zp,x + a = nz = READ_LOW( uint8_t (data + x) ); + pc++; + goto loop; + + case 0xA5: // LDA zp + a = nz = READ_LOW( data ); + pc++; + goto loop; + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ); + + case 0x20: { // JSR + fuint16 temp = pc + 1; + pc = GET_ADDR(); + WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); + sp = (sp - 2) | 0x100; + WRITE_LOW( sp, temp ); + goto loop; + } + + case 0x4C: // JMP abs + pc = GET_ADDR(); + goto loop; + + case 0xE8: // INX + INC_DEC_XY( x, 1 ) + + case 0x10: // BPL + BRANCH( !IS_NEG ) + + ARITH_ADDR_MODES( 0xC5 ) // CMP + nz = a - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ); + + case 0x95: // STA zp,x + data = uint8_t (data + x); + case 0x85: // STA zp + pc++; + WRITE_LOW( data, a ); + goto loop; + + case 0xC8: // INY + INC_DEC_XY( y, 1 ) + + case 0xA8: // TAY + y = a; + nz = a; + goto loop; + + case 0x98: // TYA + a = y; + nz = y; + goto loop; + + case 0xAD:{// LDA abs + unsigned addr = GET_ADDR(); + pc += 2; + nz = READ( addr ); + a = nz; + goto loop; + } + + case 0x60: // RTS + pc = 1 + READ_LOW( sp ); + pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); + sp = (sp - 0xFE) | 0x100; + goto loop; + + { + fuint16 addr; + + case 0x99: // STA abs,Y + addr = y + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x8D: // STA abs + addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + goto sta_ptr; + + case 0x9D: // STA abs,X (slightly more common than STA abs) + addr = x + GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, a ); + goto loop; + } + sta_ptr: + FLUSH_TIME(); + WRITE( addr, a ); + CACHE_TIME(); + goto loop; + + case 0x91: // STA (ind),Y + IND_Y( NO_PAGE_CROSSING, addr ) + pc++; + goto sta_ptr; + + case 0x81: // STA (ind,X) + IND_X( addr ) + pc++; + goto sta_ptr; + + } + + case 0xA9: // LDA #imm + pc++; + a = data; + nz = data; + goto loop; + + // common read instructions + { + fuint16 addr; + + case 0xA1: // LDA (ind,X) + IND_X( addr ) + pc++; + goto a_nz_read_addr; + + case 0xB1:// LDA (ind),Y + addr = READ_LOW( data ) + y; + HANDLE_PAGE_CROSSING( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xB9: // LDA abs,Y + HANDLE_PAGE_CROSSING( data + y ); + addr = GET_ADDR() + y; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + goto a_nz_read_addr; + + case 0xBD: // LDA abs,X + HANDLE_PAGE_CROSSING( data + x ); + addr = GET_ADDR() + x; + pc += 2; + a = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + a_nz_read_addr: + FLUSH_TIME(); + a = nz = READ( addr ); + CACHE_TIME(); + goto loop; + + } + +// Branch + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + +// Load/store + + case 0x94: // STY zp,x + data = uint8_t (data + x); + case 0x84: // STY zp + pc++; + WRITE_LOW( data, y ); + goto loop; + + case 0x96: // STX zp,y + data = uint8_t (data + y); + case 0x86: // STX zp + pc++; + WRITE_LOW( data, x ); + goto loop; + + case 0xB6: // LDX zp,y + data = uint8_t (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 = uint8_t (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; + HANDLE_PAGE_CROSSING( data ); + case 0xAC:{// LDY abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + y = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0xBE: // LDX abs,y + data += y; + HANDLE_PAGE_CROSSING( data ); + case 0xAE:{// LDX abs + unsigned addr = data + 0x100 * GET_MSB(); + pc += 2; + FLUSH_TIME(); + x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + { + fuint8 temp; + case 0x8C: // STY abs + temp = y; + goto store_abs; + + case 0x8E: // STX abs + temp = x; + store_abs: + unsigned addr = GET_ADDR(); + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, temp ); + goto loop; + } + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + +// Compare + + case 0xEC:{// CPX abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( 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 &= 0xFF; + goto loop; + + case 0xCC:{// CPY abs + unsigned addr = GET_ADDR(); + pc++; + FLUSH_TIME(); + data = READ( 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 &= 0xFF; + goto loop; + +// Logical + + 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; + + case 0x2C:{// BIT abs + unsigned addr = GET_ADDR(); + pc += 2; + status &= ~st_v; + nz = READ( addr ); + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + } + + case 0x24: // BIT zp + nz = READ_LOW( data ); + pc++; + status &= ~st_v; + status |= nz & st_v; + if ( a & nz ) + goto loop; + nz <<= 8; // result must be zero, even if N bit is set + goto loop; + +// Add/subtract + + ARITH_ADDR_MODES( 0xE5 ) // SBC + case 0xEB: // unofficial equivalent + data ^= 0xFF; + goto adc_imm; + + ARITH_ADDR_MODES( 0x65 ) // ADC + adc_imm: { + check( !(status & st_d) ); + fint16 carry = c >> 8 & 1; + fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + status &= ~st_v; + status |= ov >> 2 & 0x40; + c = nz = a + data + carry; + pc++; + a = (uint8_t) 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 = (uint8_t) nz; + goto loop; + + case 0x2A: { // ROL A + nz = a << 1; + fint16 temp = c >> 8 & 1; + c = nz; + nz |= temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x5E: // LSR abs,X + data += x; + case 0x4E: // LSR abs + c = 0; + case 0x6E: // ROR abs + ror_abs: { + ADD_PAGE(); + FLUSH_TIME(); + int temp = READ( 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(); + nz = c >> 8 & 1; + FLUSH_TIME(); + nz |= (c = READ( data ) << 1); + rotate_common: + pc++; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + + case 0x7E: // ROR abs,X + data += x; + goto ror_abs; + + case 0x76: // ROR zp,x + data = uint8_t (data + x); + goto ror_zp; + + case 0x56: // LSR zp,x + data = uint8_t (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 = uint8_t (data + x); + goto rol_zp; + + case 0x16: // ASL zp,x + data = uint8_t (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 + + case 0xCA: // DEX + INC_DEC_XY( x, -1 ) + + case 0x88: // DEY + INC_DEC_XY( y, -1 ) + + case 0xF6: // INC zp,x + data = uint8_t (data + x); + case 0xE6: // INC zp + nz = 1; + goto add_nz_zp; + + case 0xD6: // DEC zp,x + data = uint8_t (data + x); + case 0xC6: // DEC zp + nz = (unsigned) -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 = (unsigned) -1; + inc_common: + FLUSH_TIME(); + nz += READ( data ); + pc += 2; + WRITE( data, (uint8_t) nz ); + CACHE_TIME(); + goto loop; + +// Transfer + + case 0xAA: // TAX + x = a; + nz = a; + goto loop; + + case 0x8A: // TXA + a = x; + 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; + +// Stack + + case 0x48: // PHA + PUSH( a ); // verified + goto loop; + + case 0x68: // PLA + a = nz = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + goto loop; + + case 0x40:{// RTI + fuint8 temp = READ_LOW( sp ); + pc = READ_LOW( 0x100 | (sp - 0xFF) ); + pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; + sp = (sp - 0xFD) | 0x100; + data = status; + SET_STATUS( temp ); + this->r.status = status; // update externally-visible I flag + if ( (data ^ status) & st_i ) + { + sap_time_t new_time = end_time_; + if ( !(status & st_i) && new_time > irq_time_ ) + new_time = irq_time_; + blargg_long delta = s.base - new_time; + s.base = new_time; + s_time += delta; + } + goto loop; + } + + case 0x28:{// PLP + fuint8 temp = READ_LOW( sp ); + sp = (sp - 0xFF) | 0x100; + fuint8 changed = status ^ temp; + SET_STATUS( temp ); + if ( !(changed & st_i) ) + goto loop; // I flag didn't change + if ( status & st_i ) + goto handle_sei; + goto handle_cli; + } + + case 0x08: { // PHP + fuint8 temp; + CALC_STATUS( temp ); + PUSH( temp | (st_b | st_r) ); + goto loop; + } + + case 0x6C:{// JMP (ind) + data = GET_ADDR(); + pc = READ_PROG( data ); + data = (data & 0xFF00) | ((data + 1) & 0xFF); + pc |= 0x100 * READ_PROG( data ); + goto loop; + } + + case 0x00: // BRK + goto handle_brk; + +// Flags + + case 0x38: // SEC + c = (unsigned) ~0; + goto loop; + + case 0x18: // CLC + c = 0; + goto loop; + + case 0xB8: // CLV + status &= ~st_v; + goto loop; + + case 0xD8: // CLD + status &= ~st_d; + goto loop; + + case 0xF8: // SED + status |= st_d; + goto loop; + + case 0x58: // CLI + if ( !(status & st_i) ) + goto loop; + status &= ~st_i; + handle_cli: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - irq_time_; + if ( delta <= 0 ) + { + if ( TIME < irq_time_ ) + goto loop; + goto delayed_cli; + } + s.base = 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; + irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop + goto loop; + } + delayed_cli: + dprintf( "Delayed CLI not emulated\n" ); + goto loop; + } + + case 0x78: // SEI + if ( status & st_i ) + goto loop; + status |= st_i; + handle_sei: { + this->r.status = status; // update externally-visible I flag + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + if ( s_time < 0 ) + goto loop; + dprintf( "Delayed SEI not emulated\n" ); + goto loop; + } + +// Unofficial + + // SKW - Skip word + case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: + HANDLE_PAGE_CROSSING( data + x ); + case 0x0C: + pc++; + // SKB - Skip byte + case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: + case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: + pc++; + goto loop; + + // NOP + case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: + goto loop; + +// Unimplemented + + // halt + //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: + //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: + + default: + assert( (unsigned) opcode <= 0xFF ); + illegal_encountered = true; + pc--; + goto stop; + } + assert( false ); + + int result_; +handle_brk: + if ( (pc - 1) >= idle_addr ) + goto idle_done; + pc++; + result_ = 4; + dprintf( "BRK executed\n" ); + +interrupt: + { + s_time += 7; + + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); + WRITE_LOW( 0x100 | (sp - 2), pc ); + pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); + + sp = (sp - 3) | 0x100; + fuint8 temp; + CALC_STATUS( temp ); + temp |= st_r; + if ( result_ ) + temp |= st_b; // TODO: incorrectly sets B flag for IRQ + WRITE_LOW( sp, temp ); + + status &= ~st_d; + status |= st_i; + this->r.status = status; // update externally-visible I flag + + blargg_long delta = s.base - end_time_; + s.base = end_time_; + s_time += delta; + goto loop; + } + +idle_done: + //s_time = 0; + pc--; + goto stop; +out_of_time: + pc--; + FLUSH_TIME(); + CPU_DONE( this, TIME, result_ ); + CACHE_TIME(); + if ( result_ >= 0 ) + goto interrupt; + if ( s_time < 0 ) + goto loop; + +stop: + + s.time = s_time; + + r.pc = pc; + r.sp = GET_SP(); + r.a = a; + r.x = x; + r.y = y; + + { + fuint8 temp; + CALC_STATUS( temp ); + r.status = temp; + } + + this->state_ = s; + this->state = &this->state_; + + return illegal_encountered; +} + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h new file mode 100644 index 00000000..712f63cd --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Cpu.h @@ -0,0 +1,83 @@ +// Atari 6502 CPU emulator + +// Game_Music_Emu 0.5.2 +#ifndef SAP_CPU_H +#define SAP_CPU_H + +#include "blargg_common.h" + +typedef blargg_long sap_time_t; // clock cycle count +typedef unsigned sap_addr_t; // 16-bit address +enum { future_sap_time = LONG_MAX / 2 + 1 }; + +class Sap_Cpu { +public: + typedef BOOST::uint8_t uint8_t; + + // Clear all registers and keep pointer to 64K memory passed in + void reset( void* mem_64k ); + + // Run until specified time is reached. Returns true if suspicious/unsupported + // instruction was encountered at any point during run. + bool run( sap_time_t end_time ); + + // Registers are not updated until run() returns (except I flag in status) + struct registers_t { + BOOST::uint16_t pc; + BOOST::uint8_t a; + BOOST::uint8_t x; + BOOST::uint8_t y; + BOOST::uint8_t status; + BOOST::uint8_t sp; + }; + registers_t r; + + enum { idle_addr = 0xFEFF }; + + // Time of beginning of next instruction to be executed + sap_time_t time() const { return state->time + state->base; } + void set_time( sap_time_t t ) { state->time = t - state->base; } + void adjust_time( int delta ) { state->time += delta; } + + sap_time_t irq_time() const { return irq_time_; } + void set_irq_time( sap_time_t ); + + sap_time_t end_time() const { return end_time_; } + void set_end_time( sap_time_t ); + +public: + Sap_Cpu() { state = &state_; } + enum { irq_inhibit = 0x04 }; +private: + struct state_t { + sap_time_t base; + sap_time_t time; + }; + state_t* state; // points to state_ or a local copy within run() + state_t state_; + sap_time_t irq_time_; + sap_time_t end_time_; + uint8_t* mem; + + inline sap_time_t update_end_time( sap_time_t end, sap_time_t irq ); +}; + +inline sap_time_t Sap_Cpu::update_end_time( sap_time_t t, sap_time_t irq ) +{ + if ( irq < t && !(r.status & irq_inhibit) ) t = irq; + sap_time_t delta = state->base - t; + state->base = t; + return delta; +} + +inline void Sap_Cpu::set_irq_time( sap_time_t t ) +{ + state->time += update_end_time( end_time_, (irq_time_ = t) ); +} + +inline void Sap_Cpu::set_end_time( sap_time_t t ) +{ + state->time += update_end_time( (end_time_ = t), irq_time_ ); +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp new file mode 100644 index 00000000..8314fd6e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.cpp @@ -0,0 +1,442 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Sap_Emu.h" + +#include "blargg_endian.h" +#include + +/* 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" + +long const base_scanline_period = 114; + +Sap_Emu::Sap_Emu() +{ + set_type( gme_sap_type ); + + static const char* const names [Sap_Apu::osc_count * 2] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8", + }; + set_voice_names( names ); + + static int const types [Sap_Apu::osc_count * 2] = { + wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, + wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4, + }; + set_voice_types( types ); + set_silence_lookahead( 6 ); +} + +Sap_Emu::~Sap_Emu() { } + +// Track info + +// Returns 16 or greater if not hex +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static long from_hex( byte const* in ) +{ + unsigned result = 0; + for ( int n = 4; n--; ) + { + int h = from_hex_char( *in++ ); + if ( h > 15 ) + return -1; + result = result * 0x10 + h; + } + return result; +} + +static int from_dec( byte const* in, byte const* end ) +{ + if ( in >= end ) + return -1; + + int n = 0; + while ( in < end ) + { + int dig = *in++ - '0'; + if ( (unsigned) dig > 9 ) + return -1; + n = n * 10 + dig; + } + return n; +} + +static void parse_string( byte const* in, byte const* end, int len, char* out ) +{ + byte const* start = in; + if ( *in++ == '\"' ) + { + start++; + while ( in < end && *in != '\"' ) + in++; + } + else + { + in = end; + } + len = min( len - 1, int (in - start) ); + out [len] = 0; + memcpy( out, start, len ); +} + +static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out ) +{ + out->track_count = 1; + out->author [0] = 0; + out->name [0] = 0; + out->copyright [0] = 0; + + if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) + return gme_wrong_file_type; + + byte const* file_end = in + size - 5; + in += 5; + while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) + { + byte const* line_end = in; + while ( line_end < file_end && *line_end != 0x0D ) + line_end++; + + char const* tag = (char const*) in; + while ( in < line_end && *in > ' ' ) + in++; + int tag_len = (char const*) in - tag; + + while ( in < line_end && *in <= ' ' ) in++; + + if ( tag_len <= 0 ) + { + // skip line + } + else if ( !strncmp( "INIT", tag, tag_len ) ) + { + out->init_addr = from_hex( in ); + if ( (unsigned long) out->init_addr > 0xFFFF ) + return "Invalid init address"; + } + else if ( !strncmp( "PLAYER", tag, tag_len ) ) + { + out->play_addr = from_hex( in ); + if ( (unsigned long) out->play_addr > 0xFFFF ) + return "Invalid play address"; + } + else if ( !strncmp( "MUSIC", tag, tag_len ) ) + { + out->music_addr = from_hex( in ); + if ( (unsigned long) out->music_addr > 0xFFFF ) + return "Invalid music address"; + } + else if ( !strncmp( "SONGS", tag, tag_len ) ) + { + out->track_count = from_dec( in, line_end ); + if ( out->track_count <= 0 ) + return "Invalid track count"; + } + else if ( !strncmp( "TYPE", tag, tag_len ) ) + { + switch ( out->type = *in ) + { + case 'C': + case 'B': + break; + + case 'D': + return "Digimusic not supported"; + + default: + return "Unsupported player type"; + } + } + else if ( !strncmp( "STEREO", tag, tag_len ) ) + { + out->stereo = true; + } + else if ( !strncmp( "FASTPLAY", tag, tag_len ) ) + { + out->fastplay = from_dec( in, line_end ); + if ( out->fastplay <= 0 ) + return "Invalid fastplay value"; + } + else if ( !strncmp( "AUTHOR", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->author, out->author ); + } + else if ( !strncmp( "NAME", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->name, out->name ); + } + else if ( !strncmp( "DATE", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->copyright, out->copyright ); + } + + in = line_end + 2; + } + + if ( in [0] != 0xFF || in [1] != 0xFF ) + return "ROM data missing"; + out->rom_data = in + 2; + + return 0; +} + +static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out ) +{ + Gme_File::copy_field_( out->game, in.name ); + Gme_File::copy_field_( out->author, in.author ); + Gme_File::copy_field_( out->copyright, in.copyright ); +} + +blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const +{ + copy_sap_fields( info, out ); + return 0; +} + +struct Sap_File : Gme_Info_ +{ + Sap_Emu::info_t info; + + Sap_File() { set_type( gme_sap_type ); } + + blargg_err_t load_mem_( byte const* begin, long size ) + { + RETURN_ERR( parse_info( begin, size, &info ) ); + set_track_count( info.track_count ); + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_sap_fields( info, out ); + return 0; + } +}; + +static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } +static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } + +gme_type_t_ const gme_sap_type [1] = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }; + +// Setup + +blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) +{ + file_end = in + size; + + info.warning = 0; + info.type = 'B'; + info.stereo = false; + info.init_addr = -1; + info.play_addr = -1; + info.music_addr = -1; + info.fastplay = 312; + RETURN_ERR( parse_info( in, size, &info ) ); + + set_warning( info.warning ); + set_track_count( info.track_count ); + set_voice_count( Sap_Apu::osc_count << info.stereo ); + apu_impl.volume( gain() ); + + return setup_buffer( 1773447 ); +} + +void Sap_Emu::update_eq( blip_eq_t const& eq ) +{ + apu_impl.synth.treble_eq( eq ); +} + +void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + int i2 = i - Sap_Apu::osc_count; + if ( i2 >= 0 ) + apu2.osc_output( i2, right ); + else + apu.osc_output( i, (info.stereo ? left : center) ); +} + +// Emulation + +void Sap_Emu::set_tempo_( double t ) +{ + scanline_period = sap_time_t (base_scanline_period / t); +} + +inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; } + +void Sap_Emu::cpu_jsr( sap_addr_t addr ) +{ + check( r.sp >= 0xFE ); // catch anything trying to leave data on stack + r.pc = addr; + int high_byte = (idle_addr - 1) >> 8; + if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte ) + r.sp = 0xFF; // pop extra byte off + mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return + mem.ram [0x100 + r.sp--] = high_byte; + mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF; +} + +void Sap_Emu::run_routine( sap_addr_t addr ) +{ + cpu_jsr( addr ); + cpu::run( 312 * base_scanline_period * 60 ); + check( r.pc == idle_addr ); +} + +inline void Sap_Emu::call_init( int track ) +{ + switch ( info.type ) + { + case 'B': + r.a = track; + run_routine( info.init_addr ); + break; + + case 'C': + r.a = 0x70; + r.x = info.music_addr&0xFF; + r.y = info.music_addr >> 8; + run_routine( info.play_addr + 3 ); + r.a = 0; + r.x = track; + run_routine( info.play_addr + 3 ); + break; + } +} + +blargg_err_t Sap_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + memset( &mem, 0, sizeof mem ); + + byte const* in = info.rom_data; + while ( file_end - in >= 5 ) + { + unsigned start = get_le16( in ); + unsigned end = get_le16( in + 2 ); + //dprintf( "Block $%04X-$%04X\n", start, end ); + in += 4; + if ( end < start ) + { + set_warning( "Invalid file data block" ); + break; + } + long len = end - start + 1; + if ( len > file_end - in ) + { + set_warning( "Invalid file data block" ); + break; + } + + memcpy( mem.ram + start, in, len ); + in += len; + if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) + in += 2; + } + + apu.reset( &apu_impl ); + apu2.reset( &apu_impl ); + cpu::reset( mem.ram ); + time_mask = 0; // disables sound during init + call_init( track ); + time_mask = -1; + + next_play = play_period(); + + return 0; +} + +// Emulation + +// see sap_cpu_io.h for read/write functions + +void Sap_Emu::cpu_write_( sap_addr_t addr, int data ) +{ + if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) ) + { + GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data ); + apu.write_data( time() & time_mask, addr, data ); + return; + } + + if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && + info.stereo ) + { + GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data ); + apu2.write_data( time() & time_mask, addr ^ 0x10, data ); + return; + } + + if ( (addr & ~0x0010) != 0xD20F || data != 0x03 ) + dprintf( "Unmapped write $%04X <- $%02X\n", addr, data ); +} + +inline void Sap_Emu::call_play() +{ + switch ( info.type ) + { + case 'B': + cpu_jsr( info.play_addr ); + break; + + case 'C': + cpu_jsr( info.play_addr + 6 ); + break; + } +} + +blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) +{ + set_time( 0 ); + while ( time() < duration ) + { + if ( cpu::run( duration ) || r.pc > idle_addr ) + return "Emulation error (illegal instruction)"; + + if ( r.pc == idle_addr ) + { + if ( next_play <= duration ) + { + set_time( next_play ); + next_play += play_period(); + call_play(); + GME_FRAME_HOOK( this ); + } + else + { + set_time( duration ); + } + } + } + + duration = time(); + next_play -= duration; + check( next_play >= 0 ); + if ( next_play < 0 ) + next_play = 0; + apu.end_frame( duration ); + if ( info.stereo ) + apu2.end_frame( duration ); + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h new file mode 100644 index 00000000..4878faa6 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sap_Emu.h @@ -0,0 +1,69 @@ +// Atari XL/XE SAP music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef SAP_EMU_H +#define SAP_EMU_H + +#include "Classic_Emu.h" +#include "Sap_Apu.h" +#include "Sap_Cpu.h" + +class Sap_Emu : private Sap_Cpu, public Classic_Emu { + typedef Sap_Cpu cpu; +public: + static gme_type_t static_type() { return gme_sap_type; } +public: + Sap_Emu(); + ~Sap_Emu(); + struct info_t { + byte const* rom_data; + const char* warning; + long init_addr; + long play_addr; + long music_addr; + int type; + int track_count; + int fastplay; + bool stereo; + char author [256]; + char name [256]; + char copyright [ 32]; + }; +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t start_track_( int ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); +public: private: friend class Sap_Cpu; + int cpu_read( sap_addr_t ); + void cpu_write( sap_addr_t, int ); + void cpu_write_( sap_addr_t, int ); +private: + info_t info; + + byte const* file_end; + sap_time_t scanline_period; + sap_time_t next_play; + sap_time_t time_mask; + Sap_Apu apu; + Sap_Apu apu2; + + // large items + struct { + byte padding1 [0x100]; + byte ram [0x10000]; + byte padding2 [0x100]; + } mem; + Sap_Apu_Impl apu_impl; + + sap_time_t play_period() const; + void call_play(); + void cpu_jsr( sap_addr_t ); + void call_init( int track ); + void run_routine( sap_addr_t ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp new file mode 100644 index 00000000..b41fdec4 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.cpp @@ -0,0 +1,330 @@ +// Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/ + +#include "Sms_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" + +// Sms_Osc + +Sms_Osc::Sms_Osc() +{ + output = 0; + outputs [0] = 0; // always stays NULL + outputs [1] = 0; + outputs [2] = 0; + outputs [3] = 0; +} + +void Sms_Osc::reset() +{ + delay = 0; + last_amp = 0; + volume = 0; + output_select = 3; + output = outputs [3]; +} + +// Sms_Square + +inline void Sms_Square::reset() +{ + period = 0; + phase = 0; + Sms_Osc::reset(); +} + +void Sms_Square::run( blip_time_t time, blip_time_t end_time ) +{ + if ( !volume || period <= 128 ) + { + // ignore 16kHz and higher + if ( last_amp ) + { + synth->offset( time, -last_amp, output ); + last_amp = 0; + } + time += delay; + if ( !period ) + { + time = end_time; + } + else if ( time < end_time ) + { + // keep calculating phase + int count = (end_time - time + period - 1) / period; + phase = (phase + count) & 1; + time += count * period; + } + } + else + { + int amp = phase ? volume : -volume; + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + int delta = amp * 2; + do + { + delta = -delta; + synth->offset_inline( time, delta, output ); + time += period; + phase ^= 1; + } + while ( time < end_time ); + this->last_amp = phase ? volume : -volume; + } + } + delay = time - end_time; +} + +// Sms_Noise + +static int const noise_periods [3] = { 0x100, 0x200, 0x400 }; + +inline void Sms_Noise::reset() +{ + period = &noise_periods [0]; + shifter = 0x8000; + feedback = 0x9000; + Sms_Osc::reset(); +} + +void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + int amp = volume; + if ( shifter & 1 ) + amp = -amp; + + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth.offset( time, delta, output ); + } + } + + time += delay; + if ( !volume ) + time = end_time; + + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + unsigned shifter = this->shifter; + int delta = amp * 2; + int period = *this->period * 2; + if ( !period ) + period = 16; + + do + { + int changed = shifter + 1; + shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1); + if ( changed & 2 ) // true if bits 0 and 1 differ + { + delta = -delta; + synth.offset_inline( time, delta, output ); + } + time += period; + } + while ( time < end_time ); + + this->shifter = shifter; + this->last_amp = delta >> 1; + } + delay = time - end_time; +} + +// Sms_Apu + +Sms_Apu::Sms_Apu() +{ + for ( int i = 0; i < 3; i++ ) + { + squares [i].synth = &square_synth; + oscs [i] = &squares [i]; + } + oscs [3] = &noise; + + volume( 1.0 ); + reset(); +} + +Sms_Apu::~Sms_Apu() +{ +} + +void Sms_Apu::volume( double vol ) +{ + vol *= 0.85 / (osc_count * 64 * 2); + square_synth.volume( vol ); + noise.synth.volume( vol ); +} + +void Sms_Apu::treble_eq( const blip_eq_t& eq ) +{ + square_synth.treble_eq( eq ); + noise.synth.treble_eq( eq ); +} + +void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + require( (unsigned) index < osc_count ); + require( (center && left && right) || (!center && !left && !right) ); + Sms_Osc& osc = *oscs [index]; + osc.outputs [1] = right; + osc.outputs [2] = left; + osc.outputs [3] = center; + osc.output = osc.outputs [osc.output_select]; +} + +void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + for ( int i = 0; i < osc_count; i++ ) + osc_output( i, center, left, right ); +} + +void Sms_Apu::reset( unsigned feedback, int noise_width ) +{ + last_time = 0; + latch = 0; + + if ( !feedback || !noise_width ) + { + feedback = 0x0009; + noise_width = 16; + } + // convert to "Galios configuration" + looped_feedback = 1 << (noise_width - 1); + noise_feedback = 0; + while ( noise_width-- ) + { + noise_feedback = (noise_feedback << 1) | (feedback & 1); + feedback >>= 1; + } + + squares [0].reset(); + squares [1].reset(); + squares [2].reset(); + noise.reset(); +} + +void Sms_Apu::run_until( blip_time_t end_time ) +{ + require( end_time >= last_time ); // end_time must not be before previous time + + if ( end_time > last_time ) + { + // run oscillators + for ( int i = 0; i < osc_count; ++i ) + { + Sms_Osc& osc = *oscs [i]; + if ( osc.output ) + { + osc.output->set_modified(); + if ( i < 3 ) + squares [i].run( last_time, end_time ); + else + noise.run( last_time, end_time ); + } + } + + last_time = end_time; + } +} + +void Sms_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + assert( last_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 ); + + for ( int i = 0; i < osc_count; i++ ) + { + Sms_Osc& osc = *oscs [i]; + int flags = data >> i; + Blip_Buffer* old_output = osc.output; + osc.output_select = (flags >> 3 & 2) | (flags & 1); + osc.output = osc.outputs [osc.output_select]; + if ( osc.output != old_output && osc.last_amp ) + { + if ( old_output ) + { + old_output->set_modified(); + square_synth.offset( time, -osc.last_amp, old_output ); + } + osc.last_amp = 0; + } + } +} + +// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) +static unsigned char const volumes [16] = { + 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0 +}; + +void Sms_Apu::write_data( blip_time_t time, int data ) +{ + require( (unsigned) data <= 0xFF ); + + run_until( time ); + + if ( data & 0x80 ) + latch = data; + + int index = (latch >> 5) & 3; + if ( latch & 0x10 ) + { + oscs [index]->volume = volumes [data & 15]; + } + else if ( index < 3 ) + { + Sms_Square& sq = squares [index]; + if ( data & 0x80 ) + sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF); + else + sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00); + } + else + { + int select = data & 3; + if ( select < 3 ) + noise.period = &noise_periods [select]; + else + noise.period = &squares [2].period; + + noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; + noise.shifter = 0x8000; + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h new file mode 100644 index 00000000..3c11a9c3 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Apu.h @@ -0,0 +1,75 @@ +// Sega Master System SN76489 PSG sound chip emulator + +// Sms_Snd_Emu 0.1.4 +#ifndef SMS_APU_H +#define SMS_APU_H + +#include "Sms_Oscs.h" + +class Sms_Apu { +public: + // Set overall volume of all oscillators, where 1.0 is full volume + void volume( double ); + + // Set treble equalization + void treble_eq( const blip_eq_t& ); + + // Outputs can be assigned to a single buffer for mono output, or to three + // buffers for stereo output (using Stereo_Buffer to do the mixing). + + // Assign all oscillator outputs to specified buffer(s). If buffer + // is NULL, silences all oscillators. + void output( Blip_Buffer* mono ); + void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, + // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL, + // silences oscillator. + enum { osc_count = 4 }; + void osc_output( int index, Blip_Buffer* mono ); + void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + + // Reset oscillators and internal state + void reset( unsigned noise_feedback = 0, int noise_width = 0 ); + + // Write GameGear left/right assignment byte + void write_ggstereo( blip_time_t, int ); + + // Write to data port + void write_data( blip_time_t, int ); + + // Run all oscillators up to specified time, end current frame, then + // start a new frame at time 0. + void end_frame( blip_time_t ); + +public: + Sms_Apu(); + ~Sms_Apu(); +private: + // noncopyable + Sms_Apu( const Sms_Apu& ); + Sms_Apu& operator = ( const Sms_Apu& ); + + Sms_Osc* oscs [osc_count]; + Sms_Square squares [3]; + Sms_Square::Synth square_synth; // used by squares + blip_time_t last_time; + int latch; + Sms_Noise noise; + unsigned noise_feedback; + unsigned looped_feedback; + + void run_until( blip_time_t ); +}; + +struct sms_apu_state_t +{ + unsigned char regs [8] [2]; + unsigned char latch; +}; + +inline void Sms_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } + +inline void Sms_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h new file mode 100644 index 00000000..2a896fef --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Sms_Oscs.h @@ -0,0 +1,49 @@ +// Private oscillators used by Sms_Apu + +// Sms_Snd_Emu 0.1.4 +#ifndef SMS_OSCS_H +#define SMS_OSCS_H + +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct Sms_Osc +{ + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int output_select; + + int delay; + int last_amp; + int volume; + + Sms_Osc(); + void reset(); +}; + +struct Sms_Square : Sms_Osc +{ + int period; + int phase; + + typedef Blip_Synth Synth; + const Synth* synth; + + void reset(); + void run( blip_time_t, blip_time_t ); +}; + +struct Sms_Noise : Sms_Osc +{ + const int* period; + unsigned shifter; + unsigned feedback; + + typedef Blip_Synth Synth; + Synth synth; + + void reset(); + void run( blip_time_t, blip_time_t ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp new file mode 100644 index 00000000..e909ea18 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.cpp @@ -0,0 +1,489 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Snes_Spc.h" + +#include + +/* Copyright (C) 2004-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" + +// always in the future (CPU time can go over 0, but not by this much) +int const timer_disabled_time = 127; + +Snes_Spc::Snes_Spc() : dsp( mem.ram ), cpu( this, mem.ram ) +{ + set_tempo( 1.0 ); + + // Put STOP instruction around memory to catch PC underflow/overflow. + memset( mem.padding1, 0xFF, sizeof mem.padding1 ); + memset( mem.padding2, 0xFF, sizeof mem.padding2 ); + + // A few tracks read from the last four bytes of IPL ROM + boot_rom [sizeof boot_rom - 2] = 0xC0; + boot_rom [sizeof boot_rom - 1] = 0xFF; + memset( boot_rom, 0, sizeof boot_rom - 2 ); +} + +void Snes_Spc::set_tempo( double t ) +{ + int unit = (int) (16.0 / t + 0.5); + + timer [0].divisor = unit * 8; // 8 kHz + timer [1].divisor = unit * 8; // 8 kHz + timer [2].divisor = unit; // 64 kHz +} + +// Load + +void Snes_Spc::set_ipl_rom( void const* in ) +{ + memcpy( boot_rom, in, sizeof boot_rom ); +} + +blargg_err_t Snes_Spc::load_spc( const void* data, long size ) +{ + struct spc_file_t { + char signature [27]; + char unused [10]; + uint8_t pc [2]; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + char unused2 [212]; + uint8_t ram [0x10000]; + uint8_t dsp [128]; + uint8_t ipl_rom [128]; + }; + assert( offsetof (spc_file_t,ipl_rom) == spc_file_size ); + + const spc_file_t* spc = (spc_file_t const*) data; + + if ( size < spc_file_size ) + return "Not an SPC file"; + + if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 ) + return "Not an SPC file"; + + registers_t regs; + regs.pc = spc->pc [1] * 0x100 + spc->pc [0]; + regs.a = spc->a; + regs.x = spc->x; + regs.y = spc->y; + regs.status = spc->status; + regs.sp = spc->sp; + + if ( (unsigned long) size >= sizeof *spc ) + set_ipl_rom( spc->ipl_rom ); + + const char* error = load_state( regs, spc->ram, spc->dsp ); + + echo_accessed = false; + + return error; +} + +void Snes_Spc::clear_echo() +{ + if ( !(dsp.read( 0x6C ) & 0x20) ) + { + unsigned addr = 0x100 * dsp.read( 0x6D ); + size_t size = 0x800 * dsp.read( 0x7D ); + memset( mem.ram + addr, 0xFF, min( size, sizeof mem.ram - addr ) ); + } +} + +// Handle other file formats (emulator save states) in user code, not here. + +blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram, + const void* dsp_state ) +{ + // cpu + cpu.r = cpu_state; + + // Allow DSP to generate one sample before code starts + // (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it + // clears KON 31 cycles from starting execution. It works on the SNES + // since the SPC player adds a few extra cycles delay after restoring + // KON from the DSP registers at the end of an SPC file). + extra_cycles = 32; + + // ram + memcpy( mem.ram, new_ram, sizeof mem.ram ); + memcpy( extra_ram, mem.ram + rom_addr, sizeof extra_ram ); + + // boot rom (have to force enable_rom() to update it) + rom_enabled = !(mem.ram [0xF1] & 0x80); + enable_rom( !rom_enabled ); + + // dsp + dsp.reset(); + int i; + for ( i = 0; i < Spc_Dsp::register_count; i++ ) + dsp.write( i, ((uint8_t const*) dsp_state) [i] ); + + // timers + for ( i = 0; i < timer_count; i++ ) + { + Timer& t = timer [i]; + + t.next_tick = 0; + t.enabled = (mem.ram [0xF1] >> i) & 1; + if ( !t.enabled ) + t.next_tick = timer_disabled_time; + t.count = 0; + t.counter = mem.ram [0xFD + i] & 15; + + int p = mem.ram [0xFA + i]; + t.period = p ? p : 0x100; + } + + // Handle registers which already give 0 when read by setting RAM and not changing it. + // Put STOP instruction in registers which can be read, to catch attempted CPU execution. + mem.ram [0xF0] = 0; + mem.ram [0xF1] = 0; + mem.ram [0xF3] = 0xFF; + mem.ram [0xFA] = 0; + mem.ram [0xFB] = 0; + mem.ram [0xFC] = 0; + mem.ram [0xFD] = 0xFF; + mem.ram [0xFE] = 0xFF; + mem.ram [0xFF] = 0xFF; + + return 0; // success +} + +// Hardware + +// Current time starts negative and ends at 0 +inline spc_time_t Snes_Spc::time() const +{ + return -cpu.remain(); +} + +// Keep track of next time to run and avoid a function call if it hasn't been reached. + +// Timers + +void Snes_Spc::Timer::run_until_( spc_time_t time ) +{ + if ( !enabled ) + dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time ); + assert( enabled ); // when disabled, next_tick should always be in the future + + int elapsed = ((time - next_tick) / divisor) + 1; + next_tick += elapsed * divisor; + + elapsed += count; + if ( elapsed >= period ) // avoid unnecessary division + { + int n = elapsed / period; + elapsed -= n * period; + counter = (counter + n) & 15; + } + count = elapsed; +} + +// DSP + +const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second + +void Snes_Spc::run_dsp_( spc_time_t time ) +{ + int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample + sample_t* buf = sample_buf; + if ( buf ) { + sample_buf = buf + count * 2; // stereo + assert( sample_buf <= buf_end ); + } + next_dsp += count * clocks_per_sample; + dsp.run( count, buf ); +} + +inline void Snes_Spc::run_dsp( spc_time_t time ) +{ + if ( time >= next_dsp ) + run_dsp_( time ); +} + +// Debug-only check for read/write within echo buffer, since this might result in +// inaccurate emulation due to the DSP not being caught up to the present. +inline void Snes_Spc::check_for_echo_access( spc_addr_t addr ) +{ + if ( !echo_accessed && !(dsp.read( 0x6C ) & 0x20) ) + { + // ** If echo accesses are found that require running the DSP, cache + // the start and end address on DSP writes to speed up checking. + + unsigned start = 0x100 * dsp.read( 0x6D ); + unsigned end = start + 0x800 * dsp.read( 0x7D ); + if ( start <= addr && addr < end ) { + echo_accessed = true; + dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr ); + } + } +} + +// Read + +int Snes_Spc::read( spc_addr_t addr ) +{ + int result = mem.ram [addr]; + + if ( (rom_addr <= addr && addr < 0xFFFC || addr >= 0xFFFE) && rom_enabled ) + dprintf( "Read from ROM: %04X -> %02X\n", addr, result ); + + if ( unsigned (addr - 0xF0) < 0x10 ) + { + assert( 0xF0 <= addr && addr <= 0xFF ); + + // counters + int i = addr - 0xFD; + if ( i >= 0 ) + { + Timer& t = timer [i]; + t.run_until( time() ); + int old = t.counter; + t.counter = 0; + return old; + } + + // dsp + if ( addr == 0xF3 ) + { + run_dsp( time() ); + if ( mem.ram [0xF2] >= Spc_Dsp::register_count ) + dprintf( "DSP read from $%02X\n", (int) mem.ram [0xF2] ); + return dsp.read( mem.ram [0xF2] & 0x7F ); + } + + if ( addr == 0xF0 || addr == 0xF1 || addr == 0xF8 || + addr == 0xF9 || addr == 0xFA ) + dprintf( "Read from register $%02X\n", (int) addr ); + + // Registers which always read as 0 are handled by setting mem.ram [reg] to 0 + // at startup then never changing that value. + + check(( check_for_echo_access( addr ), true )); + } + + return result; +} + + +// Write + +void Snes_Spc::enable_rom( bool enable ) +{ + if ( rom_enabled != enable ) + { + rom_enabled = enable; + memcpy( mem.ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); + // TODO: ROM can still get overwritten when DSP writes to echo buffer + } +} + +void Snes_Spc::write( spc_addr_t addr, int data ) +{ + // first page is very common + if ( addr < 0xF0 ) { + mem.ram [addr] = (uint8_t) data; + } + else switch ( addr ) + { + // RAM + default: + check(( check_for_echo_access( addr ), true )); + if ( addr < rom_addr ) { + mem.ram [addr] = (uint8_t) data; + } + else { + extra_ram [addr - rom_addr] = (uint8_t) data; + if ( !rom_enabled ) + mem.ram [addr] = (uint8_t) data; + } + break; + + // DSP + //case 0xF2: // mapped to RAM + case 0xF3: { + run_dsp( time() ); + int reg = mem.ram [0xF2]; + if ( next_dsp > 0 ) { + // skip mode + + // key press + if ( reg == 0x4C ) + keys_pressed |= data & ~dsp.read( 0x5C ); + + // key release + if ( reg == 0x5C ) { + keys_released |= data; + keys_pressed &= ~data; + } + } + if ( reg < Spc_Dsp::register_count ) { + dsp.write( reg, data ); + } + else { + dprintf( "DSP write to $%02X\n", (int) reg ); + } + break; + } + + case 0xF0: // Test register + dprintf( "Wrote $%02X to $F0\n", (int) data ); + break; + + // Config + case 0xF1: + { + // timers + for ( int i = 0; i < timer_count; i++ ) + { + Timer& t = timer [i]; + if ( !(data & (1 << i)) ) { + t.enabled = 0; + t.next_tick = timer_disabled_time; + } + else if ( !t.enabled ) { + // just enabled + t.enabled = 1; + t.counter = 0; + t.count = 0; + t.next_tick = time(); + } + } + + // port clears + if ( data & 0x10 ) { + mem.ram [0xF4] = 0; + mem.ram [0xF5] = 0; + } + if ( data & 0x20 ) { + mem.ram [0xF6] = 0; + mem.ram [0xF7] = 0; + } + + enable_rom( (data & 0x80) != 0 ); + + break; + } + + // Ports + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + // to do: handle output ports + break; + + //case 0xF8: // verified on SNES that these are read/write (RAM) + //case 0xF9: + + // Timers + case 0xFA: + case 0xFB: + case 0xFC: { + Timer& t = timer [addr - 0xFA]; + if ( (t.period & 0xFF) != data ) { + t.run_until( time() ); + t.period = data ? data : 0x100; + } + break; + } + + // Counters (cleared on write) + case 0xFD: + case 0xFE: + case 0xFF: + dprintf( "Wrote to counter $%02X\n", (int) addr ); + timer [addr - 0xFD].counter = 0; + break; + } +} + +// Play + +blargg_err_t Snes_Spc::skip( long count ) +{ + if ( count > 4 * 32000L ) + { + // don't run DSP for long durations (2-3 times faster) + + const long sync_count = 32000L * 2; + + // keep track of any keys pressed/released (and not subsequently released) + keys_pressed = 0; + keys_released = 0; + // sentinel tells play to ignore DSP + RETURN_ERR( play( count - sync_count, skip_sentinel ) ); + + // press/release keys now + dsp.write( 0x5C, keys_released & ~keys_pressed ); + dsp.write( 0x4C, keys_pressed ); + + clear_echo(); + + // play the last few seconds normally to help synchronize DSP + count = sync_count; + } + + return play( count ); +} + +blargg_err_t Snes_Spc::play( long count, sample_t* out ) +{ + require( count % 2 == 0 ); // output is always in pairs of samples + + // CPU time() runs from -duration to 0 + spc_time_t duration = (count / 2) * clocks_per_sample; + + // DSP output is made on-the-fly when the CPU reads/writes DSP registers + sample_buf = out; + buf_end = out + (out && out != skip_sentinel ? count : 0); + next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample; + + // Localize timer next_tick times and run them to the present to prevent a running + // but ignored timer's next_tick from getting too far behind and overflowing. + for ( int i = 0; i < timer_count; i++ ) + { + Timer& t = timer [i]; + if ( t.enabled ) + { + t.next_tick -= duration; + t.run_until( -duration ); + } + } + + // Run CPU for duration, reduced by any extra cycles from previous run + int elapsed = cpu.run( duration - extra_cycles ); + if ( elapsed > 0 ) + { + dprintf( "Unhandled instruction $%02X, pc = $%04X\n", + (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc ); + return "Emulation error (illegal/unsupported instruction)"; + } + extra_cycles = -elapsed; + + // Catch DSP up to present. + run_dsp( 0 ); + if ( out ) { + assert( next_dsp == clocks_per_sample ); + assert( out == skip_sentinel || sample_buf - out == count ); + } + buf_end = 0; + + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h new file mode 100644 index 00000000..b558fb71 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Snes_Spc.h @@ -0,0 +1,121 @@ +// Super Nintendo (SNES) SPC-700 APU Emulator + +// Game_Music_Emu 0.5.2 +#ifndef SNES_SPC_H +#define SNES_SPC_H + +#include "blargg_common.h" +#include "Spc_Cpu.h" +#include "Spc_Dsp.h" + +class Snes_Spc { +public: + + // Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true. + enum { spc_file_size = 0x10180 }; + blargg_err_t load_spc( const void* spc, long spc_size ); + + // Generate 'count' samples and optionally write to 'buf'. Count must be even. + // Sample output is 16-bit 32kHz, signed stereo pairs with the left channel first. + typedef short sample_t; + blargg_err_t play( long count, sample_t* buf = NULL ); + +// Optional functionality + + // Load copy of state into emulator. + typedef Spc_Cpu::registers_t registers_t; + blargg_err_t load_state( const registers_t& cpu_state, const void* ram_64k, + const void* dsp_regs_128 ); + + // Clear echo buffer, useful because many tracks have junk in the buffer. + void clear_echo(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { voice_count = Spc_Dsp::voice_count }; + void mute_voices( int mask ); + + // Skip forward by the specified number of samples (64000 samples = 1 second) + blargg_err_t skip( long count ); + + // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped the + // 16-bit sample range. + void set_gain( double ); + + // If true, prevent channels and global volumes from being phase-negated + void disable_surround( bool disable = true ); + + // Set 128 bytes to use for IPL boot ROM. Makes copy. Default is zero filled, + // to avoid including copyrighted code from the SPC-700. + void set_ipl_rom( const void* ); + + void set_tempo( double ); + +public: + Snes_Spc(); + typedef BOOST::uint8_t uint8_t; +private: + // timers + struct Timer + { + spc_time_t next_tick; + int period; + int count; + int divisor; + int enabled; + int counter; + + void run_until_( spc_time_t ); + void run_until( spc_time_t time ) + { + if ( time >= next_tick ) + run_until_( time ); + } + }; + enum { timer_count = 3 }; + Timer timer [timer_count]; + + // hardware + int extra_cycles; + spc_time_t time() const; + int read( spc_addr_t ); + void write( spc_addr_t, int ); + friend class Spc_Cpu; + + // dsp + sample_t* sample_buf; + sample_t* buf_end; // to do: remove this once possible bug resolved + spc_time_t next_dsp; + Spc_Dsp dsp; + int keys_pressed; + int keys_released; + sample_t skip_sentinel [1]; // special value for play() passed by skip() + void run_dsp( spc_time_t ); + void run_dsp_( spc_time_t ); + bool echo_accessed; + void check_for_echo_access( spc_addr_t ); + + // boot rom + enum { rom_size = 64 }; + enum { rom_addr = 0xFFC0 }; + bool rom_enabled; + void enable_rom( bool ); + + // CPU and RAM (at end because it's large) + Spc_Cpu cpu; + uint8_t extra_ram [rom_size]; + struct { + // padding to catch jumps before beginning or past end + uint8_t padding1 [0x100]; + uint8_t ram [0x10000]; + uint8_t padding2 [0x100]; + } mem; + uint8_t boot_rom [rom_size]; +}; + +inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } + +inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } + +inline void Snes_Spc::set_gain( double v ) { dsp.set_gain( v ); } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp new file mode 100644 index 00000000..fb9983b8 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.cpp @@ -0,0 +1,1062 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Spc_Cpu.h" + +#include "blargg_endian.h" +#include "Snes_Spc.h" + +/* Copyright (C) 2004-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" + +// Several instructions are commented out (or not even implemented). These aren't +// used by the SPC files tested. + +// Optimize performance for the most common instructions, and size for the rest: +// +// 15% 0xF0 BEQ rel +// 8% 0xE4 MOV A,dp +// 4% 0xF5 MOV A,abs+X +// 4% 0xD0 BNE rel +// 4% 0x6F RET +// 4% 0x3F CALL addr +// 4% 0xF4 MOV A,dp+X +// 3% 0xC4 MOV dp,A +// 2% 0xEB MOV Y,dp +// 2% 0x3D INC X +// 2% 0xF6 MOV A,abs+Y +// (1% and below not shown) + +Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) +{ + remain_ = 0; + assert( INT_MAX >= 0x7FFFFFFF ); // requires 32-bit int + blargg_verify_byte_order(); +} + +#define READ( addr ) (emu.read( addr )) +#define WRITE( addr, value ) (emu.write( addr, value )) + +#define READ_DP( addr ) READ( (addr) + dp ) +#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) + +#define READ_PROG( addr ) (ram [addr]) +#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) + +int Spc_Cpu::read( spc_addr_t addr ) +{ + return READ( addr ); +} + +void Spc_Cpu::write( spc_addr_t addr, int data ) +{ + WRITE( addr, data ); +} + +// Cycle table derived from text copy of SPC-700 manual (using regular expressions) +static unsigned char const cycle_table [0x100] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0 + 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1 + 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2 + 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3 + 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4 + 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5 + 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6 + 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7 + 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8 + 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9 + 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A + 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B + 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C + 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D + 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E + 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 // F +}; + +// The C,mem instructions are hardly used, so a non-inline function is used for +// the common access code. +unsigned Spc_Cpu::mem_bit( spc_addr_t pc ) +{ + unsigned addr = READ_PROG16( pc ); + unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); + return (t << 8) & 0x100; +} + +spc_time_t Spc_Cpu::run( spc_time_t cycle_count ) +{ + remain_ = cycle_count; + + uint8_t* const ram = this->ram; // cache + + // Stack pointer is kept one greater than usual SPC stack pointer to allow + // common pre-decrement and post-increment memory instructions that some + // processors have. Address wrap-around isn't supported. + #define PUSH( v ) (*--sp = uint8_t (v)) + #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) + #define POP() (*sp++) + #define SET_SP( v ) (sp = ram + 0x101 + (v)) + #define GET_SP() (sp - 0x101 - ram) + + uint8_t* sp; + SET_SP( r.sp ); + + // registers + unsigned pc = (unsigned) r.pc; + int a = r.a; + int x = r.x; + int y = r.y; + + // status flags + + const int st_n = 0x80; + const int st_v = 0x40; + const int st_p = 0x20; + const int st_b = 0x10; + const int st_h = 0x08; + const int st_i = 0x04; + const int st_z = 0x02; + const int st_c = 0x01; + + #define IS_NEG (nz & 0x880) + + #define CALC_STATUS( out ) do {\ + out = status & ~(st_n | st_z | st_c);\ + out |= (c >> 8) & st_c;\ + out |= (dp >> 3) & st_p;\ + if ( IS_NEG ) out |= st_n;\ + if ( !(nz & 0xFF) ) out |= st_z;\ + } while ( 0 ) + + #define SET_STATUS( in ) do {\ + status = in & ~(st_n | st_z | st_c | st_p);\ + c = in << 8;\ + nz = (in << 4) & 0x800;\ + nz |= ~in & st_z;\ + dp = (in << 3) & 0x100;\ + } while ( 0 ) + + int status; + int c; // store C as 'c' & 0x100. + int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0 + unsigned dp; // direct page base + { + int temp = r.status; + SET_STATUS( temp ); + } + + goto loop; + + unsigned data; // first operand of instruction and temporary across function calls + + // Common endings for instructions +cbranch_taken_loop: // compare and branch + pc += (BOOST::int8_t) READ_PROG( pc ); + remain_ -= 2; +inc_pc_loop: // end of instruction with an operand + pc++; +loop: + + check( (unsigned) pc < 0x10000 ); + check( (unsigned) GET_SP() < 0x100 ); + + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + unsigned opcode = READ_PROG( pc ); + pc++; + // to do: if pc is at end of memory, this will get wrong byte + data = READ_PROG( pc ); + + if ( remain_ <= 0 ) + goto stop; + + remain_ -= cycle_table [opcode]; + + // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise + // use a local temporary. + switch ( opcode ) + { + + #define BRANCH( cond ) {\ + pc++;\ + int offset = (BOOST::int8_t) data;\ + if ( cond ) {\ + pc += offset;\ + remain_ -= 2;\ + }\ + goto loop;\ + } + +// Most-Common + + case 0xF0: // BEQ (most common) + BRANCH( !(uint8_t) nz ) + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ) + + case 0x3F: // CALL + PUSH16( pc + 2 ); + pc = READ_PROG16( pc ); + goto loop; + + case 0x6F: // RET + pc = POP(); + pc += POP() * 0x100; + goto loop; + +#define CASE( n ) case n: + +// Define common address modes based on opcode for immediate mode. Execution +// ends with data set to the address of the operand. +#define ADDR_MODES( op )\ + CASE( op - 0x02 ) /* (X) */\ + data = x + dp;\ + pc--;\ + goto end_##op;\ + CASE( op + 0x0F ) /* (dp)+Y */\ + data = READ_PROG16( data + dp ) + y;\ + goto end_##op;\ + CASE( op - 0x01 ) /* (dp+X) */\ + data = READ_PROG16( uint8_t (data + x) + dp );\ + goto end_##op;\ + CASE( op + 0x0E ) /* abs+Y */\ + data += y;\ + goto abs_##op;\ + CASE( op + 0x0D ) /* abs+X */\ + data += x;\ + CASE( op - 0x03 ) /* abs */\ + abs_##op:\ + pc++;\ + data += 0x100 * READ_PROG( pc );\ + goto end_##op;\ + CASE( op + 0x0C ) /* dp+X */\ + data = uint8_t (data + x);\ + CASE( op - 0x04 ) /* dp */\ + data += dp;\ + end_##op: + +// 1. 8-bit Data Transmission Commands. Group I + + ADDR_MODES( 0xE8 ) // MOV A,addr + // case 0xE4: // MOV a,dp (most common) + mov_a_addr: + a = nz = READ( data ); + goto inc_pc_loop; + case 0xBF: // MOV A,(X)+ + data = x + dp; + x = uint8_t (x + 1); + pc--; + goto mov_a_addr; + + case 0xE8: // MOV A,imm + a = data; + nz = data; + goto inc_pc_loop; + + case 0xF9: // MOV X,dp+Y + data = uint8_t (data + y); + case 0xF8: // MOV X,dp + data += dp; + goto mov_x_addr; + case 0xE9: // MOV X,abs + data = READ_PROG16( pc ); + pc++; + mov_x_addr: + data = READ( data ); + case 0xCD: // MOV X,imm + x = data; + nz = data; + goto inc_pc_loop; + + case 0xFB: // MOV Y,dp+X + data = uint8_t (data + x); + case 0xEB: // MOV Y,dp + data += dp; + goto mov_y_addr; + case 0xEC: // MOV Y,abs + data = READ_PROG16( pc ); + pc++; + mov_y_addr: + data = READ( data ); + case 0x8D: // MOV Y,imm + y = data; + nz = data; + goto inc_pc_loop; + +// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 + + ADDR_MODES( 0xC8 ) // MOV addr,A + WRITE( data, a ); + goto inc_pc_loop; + + { + int temp; + case 0xCC: // MOV abs,Y + temp = y; + goto mov_abs_temp; + case 0xC9: // MOV abs,X + temp = x; + mov_abs_temp: + WRITE( READ_PROG16( pc ), temp ); + pc += 2; + goto loop; + } + + case 0xD9: // MOV dp+Y,X + data = uint8_t (data + y); + case 0xD8: // MOV dp,X + WRITE( data + dp, x ); + goto inc_pc_loop; + + case 0xDB: // MOV dp+X,Y + data = uint8_t (data + x); + case 0xCB: // MOV dp,Y + WRITE( data + dp, y ); + goto inc_pc_loop; + + case 0xFA: // MOV dp,dp + data = READ( data + dp ); + case 0x8F: // MOV dp,#imm + pc++; + WRITE_DP( READ_PROG( pc ), data ); + goto inc_pc_loop; + +// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. + + case 0x7D: // MOV A,X + a = x; + nz = x; + goto loop; + + case 0xDD: // MOV A,Y + a = y; + nz = y; + goto loop; + + case 0x5D: // MOV X,A + x = a; + nz = a; + goto loop; + + case 0xFD: // MOV Y,A + y = a; + nz = a; + goto loop; + + case 0x9D: // MOV X,SP + x = nz = GET_SP(); + goto loop; + + case 0xBD: // MOV SP,X + SET_SP( x ); + goto loop; + + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) + + case 0xAF: // MOV (X)+,A + WRITE_DP( x, a ); + x++; + goto loop; + +// 5. 8-BIT LOGIC OPERATION COMMANDS + +#define LOGICAL_OP( op, func )\ + ADDR_MODES( op ) /* addr */\ + data = READ( data );\ + case op: /* imm */\ + nz = a func##= data;\ + goto inc_pc_loop;\ + { unsigned addr;\ + case op + 0x11: /* X,Y */\ + data = READ_DP( y );\ + addr = x + dp;\ + pc--;\ + goto addr_##op;\ + case op + 0x01: /* dp,dp */\ + data = READ_DP( data );\ + case op + 0x10: /*dp,imm*/\ + pc++;\ + addr = READ_PROG( pc ) + dp;\ + addr_##op:\ + nz = data func READ( addr );\ + WRITE( addr, nz );\ + goto inc_pc_loop;\ + } + + LOGICAL_OP( 0x28, & ); // AND + + LOGICAL_OP( 0x08, | ); // OR + + LOGICAL_OP( 0x48, ^ ); // EOR + +// 4. 8-BIT ARITHMETIC OPERATION COMMANDS + + ADDR_MODES( 0x68 ) // CMP addr + data = READ( data ); + case 0x68: // CMP imm + nz = a - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x79: // CMP (X),(Y) + data = READ_DP( x ); + nz = data - READ_DP( y ); + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x69: // CMP (dp),(dp) + data = READ_DP( data ); + case 0x78: // CMP dp,imm + pc++; + nz = READ_DP( READ_PROG( pc ) ) - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x3E: // CMP X,dp + data += dp; + goto cmp_x_addr; + case 0x1E: // CMP X,abs + data = READ_PROG16( pc ); + pc++; + cmp_x_addr: + data = READ( data ); + case 0xC8: // CMP X,imm + nz = x - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x7E: // CMP Y,dp + data += dp; + goto cmp_y_addr; + case 0x5E: // CMP Y,abs + data = READ_PROG16( pc ); + pc++; + cmp_y_addr: + data = READ( data ); + case 0xAD: // CMP Y,imm + nz = y - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + { + int addr; + case 0xB9: // SBC (x),(y) + case 0x99: // ADC (x),(y) + pc--; // compensate for inc later + data = READ_DP( x ); + addr = y + dp; + goto adc_addr; + case 0xA9: // SBC dp,dp + case 0x89: // ADC dp,dp + data = READ_DP( data ); + case 0xB8: // SBC dp,imm + case 0x98: // ADC dp,imm + pc++; + addr = READ_PROG( pc ) + dp; + adc_addr: + nz = READ( addr ); + goto adc_data; + +// catch ADC and SBC together, then decode later based on operand +#undef CASE +#define CASE( n ) case n: case (n) + 0x20: + ADDR_MODES( 0x88 ) // ADC/SBC addr + data = READ( data ); + case 0xA8: // SBC imm + case 0x88: // ADC imm + addr = -1; // A + nz = a; + adc_data: { + if ( opcode & 0x20 ) + data ^= 0xFF; // SBC + int carry = (c >> 8) & 1; + int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + int hc = (nz & 15) + carry; + c = nz += data + carry; + hc = (nz & 15) - hc; + status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h); + if ( addr < 0 ) { + a = (uint8_t) nz; + goto inc_pc_loop; + } + WRITE( addr, (uint8_t) nz ); + goto inc_pc_loop; + } + + } + +// 6. ADDITION & SUBTRACTION COMMANDS + +#define INC_DEC_REG( reg, n )\ + nz = reg + n;\ + reg = (uint8_t) nz;\ + goto loop; + + case 0xBC: INC_DEC_REG( a, 1 ) // INC A + case 0x3D: INC_DEC_REG( x, 1 ) // INC X + case 0xFC: INC_DEC_REG( y, 1 ) // INC Y + + case 0x9C: INC_DEC_REG( a, -1 ) // DEC A + case 0x1D: INC_DEC_REG( x, -1 ) // DEC X + case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y + + case 0x9B: // DEC dp+X + case 0xBB: // INC dp+X + data = uint8_t (data + x); + case 0x8B: // DEC dp + case 0xAB: // INC dp + data += dp; + goto inc_abs; + case 0x8C: // DEC abs + case 0xAC: // INC abs + data = READ_PROG16( pc ); + pc++; + inc_abs: + nz = ((opcode >> 4) & 2) - 1; + nz += READ( data ); + WRITE( data, (uint8_t) nz ); + goto inc_pc_loop; + +// 7. SHIFT, ROTATION COMMANDS + + case 0x5C: // LSR A + c = 0; + case 0x7C:{// ROR A + nz = ((c >> 1) & 0x80) | (a >> 1); + c = a << 8; + a = nz; + goto loop; + } + + case 0x1C: // ASL A + c = 0; + case 0x3C:{// ROL A + int temp = (c >> 8) & 1; + c = a << 1; + nz = c | temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x0B: // ASL dp + c = 0; + data += dp; + goto rol_mem; + case 0x1B: // ASL dp+X + c = 0; + case 0x3B: // ROL dp+X + data = uint8_t (data + x); + case 0x2B: // ROL dp + data += dp; + goto rol_mem; + case 0x0C: // ASL abs + c = 0; + case 0x2C: // ROL abs + data = READ_PROG16( pc ); + pc++; + rol_mem: + nz = (c >> 8) & 1; + nz |= (c = READ( data ) << 1); + WRITE( data, (uint8_t) nz ); + goto inc_pc_loop; + + case 0x4B: // LSR dp + c = 0; + data += dp; + goto ror_mem; + case 0x5B: // LSR dp+X + c = 0; + case 0x7B: // ROR dp+X + data = uint8_t (data + x); + case 0x6B: // ROR dp + data += dp; + goto ror_mem; + case 0x4C: // LSR abs + c = 0; + case 0x6C: // ROR abs + data = READ_PROG16( pc ); + pc++; + ror_mem: { + int temp = READ( data ); + nz = ((c >> 1) & 0x80) | (temp >> 1); + c = temp << 8; + WRITE( data, nz ); + goto inc_pc_loop; + } + + case 0x9F: // XCN + nz = a = (a >> 4) | uint8_t (a << 4); + goto loop; + +// 8. 16-BIT TRANSMISION COMMANDS + + case 0xBA: // MOVW YA,dp + a = READ_DP( data ); + nz = (a & 0x7F) | (a >> 1); + y = READ_DP( uint8_t (data + 1) ); + nz |= y; + goto inc_pc_loop; + + case 0xDA: // MOVW dp,YA + WRITE_DP( data, a ); + WRITE_DP( uint8_t (data + 1), y ); + goto inc_pc_loop; + +// 9. 16-BIT OPERATION COMMANDS + + case 0x3A: // INCW dp + case 0x1A:{// DECW dp + data += dp; + + // low byte + int temp = READ( data ); + temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW + nz = ((temp >> 1) | temp) & 0x7F; + WRITE( data, (uint8_t) temp ); + + // high byte + data = uint8_t (data + 1) + dp; + temp >>= 8; + temp = uint8_t (temp + READ( data )); + nz |= temp; + WRITE( data, temp ); + + goto inc_pc_loop; + } + + case 0x9A: // SUBW YA,dp + case 0x7A: // ADDW YA,dp + { + // read 16-bit addend + int temp = READ_DP( data ); + int sign = READ_DP( uint8_t (data + 1) ); + temp += 0x100 * sign; + status &= ~(st_v | st_h); + + // to do: fix half-carry for SUBW (it's probably wrong) + + // for SUBW, negate and truncate to 16 bits + if ( opcode & 0x80 ) { + temp = (temp ^ 0xFFFF) + 1; + sign = temp >> 8; + } + + // add low byte (A) + temp += a; + a = (uint8_t) temp; + nz = (temp | (temp >> 1)) & 0x7F; + + // add high byte (Y) + temp >>= 8; + c = y + temp; + nz = (nz | c) & 0xFF; + + // half-carry (temporary avoids CodeWarrior optimizer bug) + unsigned hc = (c & 15) - (y & 15); + status |= (hc >> 4) & st_h; + + // overflow if sign of YA changed when previous sign and addend sign were same + status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; + + y = (uint8_t) c; + + goto inc_pc_loop; + } + + case 0x5A: { // CMPW YA,dp + int temp = a - READ_DP( data ); + nz = ((temp >> 1) | temp) & 0x7F; + temp = y + (temp >> 8); + temp -= READ_DP( uint8_t (data + 1) ); + nz |= temp; + c = ~temp; + nz &= 0xFF; + goto inc_pc_loop; + } + +// 10. MULTIPLICATION & DIVISON COMMANDS + + case 0xCF: { // MUL YA + unsigned temp = y * a; + a = (uint8_t) temp; + nz = ((temp >> 1) | temp) & 0x7F; + y = temp >> 8; + nz |= y; + goto loop; + } + + case 0x9E: // DIV YA,X + { + // behavior based on SPC CPU tests + + status &= ~(st_h | st_v); + + if ( (y & 15) >= (x & 15) ) + status |= st_h; + + if ( y >= x ) + status |= st_v; + + unsigned ya = y * 0x100 + a; + if ( y < x * 2 ) + { + a = ya / x; + y = ya - a * x; + } + else + { + a = 255 - (ya - x * 0x200) / (256 - x); + y = x + (ya - x * 0x200) % (256 - x); + } + + nz = (uint8_t) a; + a = (uint8_t) a; + + goto loop; + } + +// 11. DECIMAL COMPENSATION COMMANDS + + // seem unused + // case 0xDF: // DAA + // case 0xBE: // DAS + +// 12. BRANCHING COMMANDS + + case 0x2F: // BRA rel + pc += (BOOST::int8_t) data; + goto inc_pc_loop; + + case 0x30: // BMI + BRANCH( IS_NEG ) + + case 0x10: // BPL + BRANCH( !IS_NEG ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x70: // BVS + BRANCH( status & st_v ) + + case 0x50: // BVC + BRANCH( !(status & st_v) ) + + case 0x03: // BBS dp.bit,rel + case 0x23: + case 0x43: + case 0x63: + case 0x83: + case 0xA3: + case 0xC3: + case 0xE3: + pc++; + if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) + goto cbranch_taken_loop; + goto inc_pc_loop; + + case 0x13: // BBC dp.bit,rel + case 0x33: + case 0x53: + case 0x73: + case 0x93: + case 0xB3: + case 0xD3: + case 0xF3: + pc++; + if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) + goto cbranch_taken_loop; + goto inc_pc_loop; + + case 0xDE: // CBNE dp+X,rel + data = uint8_t (data + x); + // fall through + case 0x2E: // CBNE dp,rel + pc++; + if ( READ_DP( data ) != a ) + goto cbranch_taken_loop; + goto inc_pc_loop; + + case 0xFE: // DBNZ Y,rel + y = uint8_t (y - 1); + BRANCH( y ) + + case 0x6E: { // DBNZ dp,rel + pc++; + unsigned temp = READ_DP( data ) - 1; + WRITE_DP( (uint8_t) data, (uint8_t) temp ); + if ( temp ) + goto cbranch_taken_loop; + goto inc_pc_loop; + } + + case 0x1F: // JMP (abs+X) + pc = READ_PROG16( pc ) + x; + // fall through + case 0x5F: // JMP abs + pc = READ_PROG16( pc ); + goto loop; + +// 13. SUB-ROUTINE CALL RETURN COMMANDS + + case 0x0F:{// BRK + check( false ); // untested + PUSH16( pc + 1 ); + pc = READ_PROG16( 0xFFDE ); // vector address verified + int temp; + CALC_STATUS( temp ); + PUSH( temp ); + status = (status | st_b) & ~st_i; + goto loop; + } + + case 0x4F: // PCALL offset + pc++; + PUSH16( pc ); + pc = 0xFF00 + data; + goto loop; + + case 0x01: // TCALL n + case 0x11: + case 0x21: + case 0x31: + case 0x41: + case 0x51: + case 0x61: + case 0x71: + case 0x81: + case 0x91: + case 0xA1: + case 0xB1: + case 0xC1: + case 0xD1: + case 0xE1: + case 0xF1: + PUSH16( pc ); + pc = READ_PROG16( 0xFFDE - (opcode >> 3) ); + goto loop; + +// 14. STACK OPERATION COMMANDS + + { + int temp; + case 0x7F: // RET1 + temp = POP(); + pc = POP(); + pc |= POP() << 8; + goto set_status; + case 0x8E: // POP PSW + temp = POP(); + set_status: + SET_STATUS( temp ); + goto loop; + } + + case 0x0D: { // PUSH PSW + int temp; + CALC_STATUS( temp ); + PUSH( temp ); + goto loop; + } + + case 0x2D: // PUSH A + PUSH( a ); + goto loop; + + case 0x4D: // PUSH X + PUSH( x ); + goto loop; + + case 0x6D: // PUSH Y + PUSH( y ); + goto loop; + + case 0xAE: // POP A + a = POP(); + goto loop; + + case 0xCE: // POP X + x = POP(); + goto loop; + + case 0xEE: // POP Y + y = POP(); + goto loop; + +// 15. BIT OPERATION COMMANDS + + case 0x02: // SET1 + case 0x22: + case 0x42: + case 0x62: + case 0x82: + case 0xA2: + case 0xC2: + case 0xE2: + case 0x12: // CLR1 + case 0x32: + case 0x52: + case 0x72: + case 0x92: + case 0xB2: + case 0xD2: + case 0xF2: { + data += dp; + int bit = 1 << (opcode >> 5); + int mask = ~bit; + if ( opcode & 0x10 ) + bit = 0; + WRITE( data, (READ( data ) & mask) | bit ); + goto inc_pc_loop; + } + + case 0x0E: // TSET1 abs + case 0x4E:{// TCLR1 abs + data = READ_PROG16( pc ); + pc += 2; + unsigned temp = READ( data ); + nz = temp & a; + temp &= ~a; + if ( !(opcode & 0x40) ) + temp |= a; + WRITE( data, temp ); + goto loop; + } + + case 0x4A: // AND1 C,mem.bit + c &= mem_bit( pc ); + pc += 2; + goto loop; + + case 0x6A: // AND1 C,/mem.bit + check( false ); // untested + c &= ~mem_bit( pc ); + pc += 2; + goto loop; + + case 0x0A: // OR1 C,mem.bit + check( false ); // untested + c |= mem_bit( pc ); + pc += 2; + goto loop; + + case 0x2A: // OR1 C,/mem.bit + check( false ); // untested + c |= ~mem_bit( pc ); + pc += 2; + goto loop; + + case 0x8A: // EOR1 C,mem.bit + c ^= mem_bit( pc ); + pc += 2; + goto loop; + + case 0xEA: { // NOT1 mem.bit + data = READ_PROG16( pc ); + pc += 2; + unsigned temp = READ( data & 0x1FFF ); + temp ^= 1 << (data >> 13); + WRITE( data & 0x1FFF, temp ); + goto loop; + } + + case 0xCA: { // MOV1 mem.bit,C + data = READ_PROG16( pc ); + pc += 2; + unsigned temp = READ( data & 0x1FFF ); + unsigned bit = data >> 13; + temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); + WRITE( data & 0x1FFF, temp ); + goto loop; + } + + case 0xAA: // MOV1 C,mem.bit + c = mem_bit( pc ); + pc += 2; + goto loop; + +// 16. PROGRAM STATUS FLAG OPERATION COMMANDS + + case 0x60: // CLRC + c = 0; + goto loop; + + case 0x80: // SETC + c = ~0; + goto loop; + + case 0xED: // NOTC + c ^= 0x100; + goto loop; + + case 0xE0: // CLRV + status &= ~(st_v | st_h); + goto loop; + + case 0x20: // CLRP + dp = 0; + goto loop; + + case 0x40: // SETP + dp = 0x100; + goto loop; + + case 0xA0: // EI + check( false ); // untested + status |= st_i; + goto loop; + + case 0xC0: // DI + check( false ); // untested + status &= ~st_i; + goto loop; + +// 17. OTHER COMMANDS + + case 0x00: // NOP + goto loop; + + //case 0xEF: // SLEEP + //case 0xFF: // STOP + + } // switch + + // unhandled instructions fall out of switch so emulator can catch them + +stop: + pc--; + + { + int temp; + CALC_STATUS( temp ); + r.status = (uint8_t) temp; + } + + r.pc = pc; + r.sp = (uint8_t) GET_SP(); + r.a = (uint8_t) a; + r.x = (uint8_t) x; + r.y = (uint8_t) y; + + return remain_; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h new file mode 100644 index 00000000..2252663b --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Cpu.h @@ -0,0 +1,57 @@ +// Super Nintendo (SNES) SPC-700 CPU emulator + +// Game_Music_Emu 0.5.2 +#ifndef SPC_CPU_H +#define SPC_CPU_H + +#include "blargg_common.h" + +typedef unsigned spc_addr_t; +typedef blargg_long spc_time_t; + +class Snes_Spc; + +class Spc_Cpu { + typedef BOOST::uint8_t uint8_t; + uint8_t* const ram; +public: + // Keeps pointer to 64K RAM + Spc_Cpu( Snes_Spc* spc, uint8_t* ram ); + + // SPC-700 registers. *Not* kept updated during a call to run(). + struct registers_t { + long pc; // more than 16 bits to allow overflow detection + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; + } r; + + // Run CPU for at least 'count' cycles. Return the number of cycles remaining + // when emulation stopped (negative if extra cycles were emulated). Emulation + // stops when there are no more remaining cycles or an unhandled instruction + // is encountered (STOP, SLEEP, and any others not yet implemented). In the + // latter case, the return value is greater than zero. + spc_time_t run( spc_time_t count ); + + // Number of clock cycles remaining for current run() call + spc_time_t remain() const; + + // Access memory as the emulated CPU does + int read ( spc_addr_t ); + void write( spc_addr_t, int ); + +private: + // noncopyable + Spc_Cpu( const Spc_Cpu& ); + Spc_Cpu& operator = ( const Spc_Cpu& ); + unsigned mem_bit( spc_addr_t ); + + spc_time_t remain_; + Snes_Spc& emu; +}; + +inline spc_time_t Spc_Cpu::remain() const { return remain_; } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp new file mode 100644 index 00000000..3d934f63 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.cpp @@ -0,0 +1,666 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +// Based on Brad Martin's OpenSPC DSP emulator + +#include "Spc_Dsp.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2002 Brad Martin */ +/* Copyright (C) 2004-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" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ ) +{ + set_gain( 1.0 ); + mute_voices( 0 ); + disable_surround( false ); + + assert( offsetof (globals_t,unused9 [2]) == register_count ); + assert( sizeof (voice) == register_count ); + blargg_verify_byte_order(); +} + +void Spc_Dsp::mute_voices( int mask ) +{ + for ( int i = 0; i < voice_count; i++ ) + voice_state [i].enabled = (mask >> i & 1) ? 31 : 7; +} + +void Spc_Dsp::reset() +{ + keys = 0; + echo_ptr = 0; + noise_count = 0; + noise = 1; + fir_offset = 0; + + g.flags = 0xE0; // reset, mute, echo off + g.key_ons = 0; + + for ( int i = 0; i < voice_count; i++ ) + { + voice_t& v = voice_state [i]; + v.on_cnt = 0; + v.volume [0] = 0; + v.volume [1] = 0; + v.envstate = state_release; + } + + memset( fir_buf, 0, sizeof fir_buf ); +} + +void Spc_Dsp::write( int i, int data ) +{ + require( (unsigned) i < register_count ); + + reg [i] = data; + int high = i >> 4; + switch ( i & 0x0F ) + { + // voice volume + case 0: + case 1: { + short* volume = voice_state [high].volume; + int left = (int8_t) reg [i & ~1]; + int right = (int8_t) reg [i | 1]; + volume [0] = left; + volume [1] = right; + // kill surround only if enabled and signs of volumes differ + if ( left * right < surround_threshold ) + { + if ( left < 0 ) + volume [0] = -left; + else + volume [1] = -right; + } + break; + } + + // fir coefficients + case 0x0F: + fir_coeff [high] = (int8_t) data; // sign-extend + break; + } +} + +// This table is for envelope timing. It represents the number of counts +// that should be subtracted from the counter each sample period (32kHz). +// The counter starts at 30720 (0x7800). Each count divides exactly into +// 0x7800 without remainder. +const int env_rate_init = 0x7800; +static short const env_rates [0x20] = +{ + 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, + 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, + 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, + 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 +}; + +const int env_range = 0x800; + +inline int Spc_Dsp::clock_envelope( int v ) +{ /* Return value is current + * ENVX */ + raw_voice_t& raw_voice = this->voice [v]; + voice_t& voice = voice_state [v]; + + int envx = voice.envx; + if ( voice.envstate == state_release ) + { + /* + * Docs: "When in the state of "key off". the "click" sound is + * prevented by the addition of the fixed value 1/256" WTF??? + * Alright, I'm going to choose to interpret that this way: + * When a note is keyed off, start the RELEASE state, which + * subtracts 1/256th each sample period (32kHz). Note there's + * no need for a count because it always happens every update. + */ + envx -= env_range / 256; + if ( envx <= 0 ) + { + envx = 0; + keys &= ~(1 << v); + return -1; + } + voice.envx = envx; + raw_voice.envx = envx >> 8; + return envx; + } + + int cnt = voice.envcnt; + int adsr1 = raw_voice.adsr [0]; + if ( adsr1 & 0x80 ) + { + switch ( voice.envstate ) + { + case state_attack: { + // increase envelope by 1/64 each step + int t = adsr1 & 15; + if ( t == 15 ) + { + envx += env_range / 2; + } + else + { + cnt -= env_rates [t * 2 + 1]; + if ( cnt > 0 ) + break; + envx += env_range / 64; + cnt = env_rate_init; + } + if ( envx >= env_range ) + { + envx = env_range - 1; + voice.envstate = state_decay; + } + voice.envx = envx; + break; + } + + case state_decay: { + // Docs: "DR... [is multiplied] by the fixed value + // 1-1/256." Well, at least that makes some sense. + // Multiplying ENVX by 255/256 every time DECAY is + // updated. + cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10]; + if ( cnt <= 0 ) + { + cnt = env_rate_init; + envx -= ((envx - 1) >> 8) + 1; + voice.envx = envx; + } + int sustain_level = raw_voice.adsr [1] >> 5; + + if ( envx <= (sustain_level + 1) * 0x100 ) + voice.envstate = state_sustain; + break; + } + + case state_sustain: + // Docs: "SR [is multiplied] by the fixed value 1-1/256." + // Multiplying ENVX by 255/256 every time SUSTAIN is + // updated. + cnt -= env_rates [raw_voice.adsr [1] & 0x1F]; + if ( cnt <= 0 ) + { + cnt = env_rate_init; + envx -= ((envx - 1) >> 8) + 1; + voice.envx = envx; + } + break; + + case state_release: + // handled above + break; + } + } + else + { /* GAIN mode is set */ + /* + * Note: if the game switches between ADSR and GAIN modes + * partway through, should the count be reset, or should it + * continue from where it was? Does the DSP actually watch for + * that bit to change, or does it just go along with whatever + * it sees when it performs the update? I'm going to assume + * the latter and not update the count, unless I see a game + * that obviously wants the other behavior. The effect would + * be pretty subtle, in any case. + */ + int t = raw_voice.gain; + if (t < 0x80) + { + envx = voice.envx = t << 4; + } + else switch (t >> 5) + { + case 4: /* Docs: "Decrease (linear): Subtraction + * of the fixed value 1/64." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + envx -= env_range / 64; + if ( envx < 0 ) + { + envx = 0; + if ( voice.envstate == state_attack ) + voice.envstate = state_decay; + } + voice.envx = envx; + break; + case 5: /* Docs: "Drecrease (exponential): + * Multiplication by the fixed value + * 1-1/256." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + envx -= ((envx - 1) >> 8) + 1; + if ( envx < 0 ) + { + envx = 0; + if ( voice.envstate == state_attack ) + voice.envstate = state_decay; + } + voice.envx = envx; + break; + case 6: /* Docs: "Increase (linear): Addition of + * the fixed value 1/64." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + envx += env_range / 64; + if ( envx >= env_range ) + envx = env_range - 1; + voice.envx = envx; + break; + case 7: /* Docs: "Increase (bent line): Addition + * of the constant 1/64 up to .75 of the + * constaint 1/256 from .75 to 1." */ + cnt -= env_rates [t & 0x1F]; + if (cnt > 0) + break; + cnt = env_rate_init; + if ( envx < env_range * 3 / 4 ) + envx += env_range / 64; + else + envx += env_range / 256; + if ( envx >= env_range ) + envx = env_range - 1; + voice.envx = envx; + break; + } + } + voice.envcnt = cnt; + raw_voice.envx = envx >> 4; + return envx; +} + +// Clamp n into range -32768 <= n <= 32767 +inline int clamp_16( int n ) +{ + if ( (BOOST::int16_t) n != n ) + n = BOOST::int16_t (0x7FFF - (n >> 31)); + return n; +} + +void Spc_Dsp::run( long count, short* out_buf ) +{ + // to do: make clock_envelope() inline so that this becomes a leaf function? + + // Should we just fill the buffer with silence? Flags won't be cleared + // during this run so it seems it should keep resetting every sample. + if ( g.flags & 0x80 ) + reset(); + + struct src_dir { + char start [2]; + char loop [2]; + }; + + const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100]; + + int left_volume = g.left_volume; + int right_volume = g.right_volume; + if ( left_volume * right_volume < surround_threshold ) + right_volume = -right_volume; // kill global surround + left_volume *= emu_gain; + right_volume *= emu_gain; + + while ( --count >= 0 ) + { + // Here we check for keys on/off. Docs say that successive writes + // to KON/KOF must be separated by at least 2 Ts periods or risk + // being neglected. Therefore DSP only looks at these during an + // update, and not at the time of the write. Only need to do this + // once however, since the regs haven't changed over the whole + // period we need to catch up with. + + g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX. + + if ( g.noise_enables ) + { + noise_count -= env_rates [g.flags & 0x1F]; + if ( noise_count <= 0 ) + { + noise_count = env_rate_init; + + noise_amp = BOOST::int16_t (noise * 2); + + // TODO: switch to Galios style + int feedback = (noise << 13) ^ (noise << 14); + noise = (feedback & 0x4000) | (noise >> 1); + } + } + + // What is the expected behavior when pitch modulation is enabled on + // voice 0? Jurassic Park 2 does this. Assume 0 for now. + blargg_long prev_outx = 0; + + int echol = 0; + int echor = 0; + int left = 0; + int right = 0; + for ( int vidx = 0; vidx < voice_count; vidx++ ) + { + const int vbit = 1 << vidx; + raw_voice_t& raw_voice = voice [vidx]; + voice_t& voice = voice_state [vidx]; + + if ( voice.on_cnt && !--voice.on_cnt ) + { + // key on + keys |= vbit; + voice.addr = GET_LE16( sd [raw_voice.waveform].start ); + voice.block_remain = 1; + voice.envx = 0; + voice.block_header = 0; + voice.fraction = 0x3FFF; // decode three samples immediately + voice.interp0 = 0; // BRR decoder filter uses previous two samples + voice.interp1 = 0; + + // NOTE: Real SNES does *not* appear to initialize the + // envelope counter to anything in particular. The first + // cycle always seems to come at a random time sooner than + // expected; as yet, I have been unable to find any + // pattern. I doubt it will matter though, so we'll go + // ahead and do the full time for now. + voice.envcnt = env_rate_init; + voice.envstate = state_attack; + } + + if ( g.key_ons & vbit & ~g.key_offs ) + { + // voice doesn't come on if key off is set + g.key_ons &= ~vbit; + voice.on_cnt = 8; + } + + if ( keys & g.key_offs & vbit ) + { + // key off + voice.envstate = state_release; + voice.on_cnt = 0; + } + + int envx; + if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) + { + raw_voice.envx = 0; + raw_voice.outx = 0; + prev_outx = 0; + continue; + } + + // Decode samples when fraction >= 1.0 (0x1000) + for ( int n = voice.fraction >> 12; --n >= 0; ) + { + if ( !--voice.block_remain ) + { + if ( voice.block_header & 1 ) + { + g.wave_ended |= vbit; + + if ( voice.block_header & 2 ) + { + // verified (played endless looping sample and ENDX was set) + voice.addr = GET_LE16( sd [raw_voice.waveform].loop ); + } + else + { + // first block was end block; don't play anything (verified) + goto sample_ended; // to do: find alternative to goto + } + } + + voice.block_header = ram [voice.addr++]; + voice.block_remain = 16; // nybbles + } + + // if next block has end flag set, *this* block ends *early* (verified) + if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 && + (voice.block_header & 3) != 3 ) + { + sample_ended: + g.wave_ended |= vbit; + keys &= ~vbit; + raw_voice.envx = 0; + voice.envx = 0; + // add silence samples to interpolation buffer + do + { + voice.interp3 = voice.interp2; + voice.interp2 = voice.interp1; + voice.interp1 = voice.interp0; + voice.interp0 = 0; + } + while ( --n >= 0 ); + break; + } + + int delta = ram [voice.addr]; + if ( voice.block_remain & 1 ) + { + delta <<= 4; // use lower nybble + voice.addr++; + } + + // Use sign-extended upper nybble + delta = int8_t (delta) >> 4; + + // For invalid ranges (D,E,F): if the nybble is negative, + // the result is F000. If positive, 0000. Nothing else + // like previous range, etc seems to have any effect. If + // range is valid, do the shift normally. Note these are + // both shifted right once to do the filters properly, but + // the output will be shifted back again at the end. + int shift = voice.block_header >> 4; + delta = (delta << shift) >> 1; + if ( shift > 0x0C ) + delta = (delta >> 14) & ~0x7FF; + + // One, two and three point IIR filters + int smp1 = voice.interp0; + int smp2 = voice.interp1; + if ( voice.block_header & 8 ) + { + delta += smp1; + delta -= smp2 >> 1; + if ( !(voice.block_header & 4) ) + { + delta += (-smp1 - (smp1 >> 1)) >> 5; + delta += smp2 >> 5; + } + else + { + delta += (-smp1 * 13) >> 7; + delta += (smp2 + (smp2 >> 1)) >> 4; + } + } + else if ( voice.block_header & 4 ) + { + delta += smp1 >> 1; + delta += (-smp1) >> 5; + } + + voice.interp3 = voice.interp2; + voice.interp2 = smp2; + voice.interp1 = smp1; + voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend + } + + // rate (with possible modulation) + int rate = GET_LE16( raw_voice.rate ) & 0x3FFF; + if ( g.pitch_mods & vbit ) + rate = (rate * (prev_outx + 32768)) >> 15; + + // Gaussian interpolation using most recent 4 samples + int index = voice.fraction >> 2 & 0x3FC; + voice.fraction = (voice.fraction & 0x0FFF) + rate; + const BOOST::int16_t* table = (BOOST::int16_t const*) ((char const*) gauss + index); + const BOOST::int16_t* table2 = (BOOST::int16_t const*) ((char const*) gauss + (255*4 - index)); + int s = ((table [0] * voice.interp3) >> 12) + + ((table [1] * voice.interp2) >> 12) + + ((table2 [1] * voice.interp1) >> 12); + s = (BOOST::int16_t) (s * 2); + s += (table2 [0] * voice.interp0) >> 11 & ~1; + int output = clamp_16( s ); + if ( g.noise_enables & vbit ) + output = noise_amp; + + // scale output and set outx values + output = (output * envx) >> 11 & ~1; + + // output and apply muting (by setting voice.enabled to 31) + // if voice is externally disabled (not a SNES feature) + int l = (voice.volume [0] * output) >> voice.enabled; + int r = (voice.volume [1] * output) >> voice.enabled; + prev_outx = output; + raw_voice.outx = int8_t (output >> 8); + if ( g.echo_ons & vbit ) + { + echol += l; + echor += r; + } + left += l; + right += r; + } + // end of channel loop + + // main volume control + left = (left * left_volume ) >> (7 + emu_gain_bits); + right = (right * right_volume) >> (7 + emu_gain_bits); + + // Echo FIR filter + + // read feedback from echo buffer + int echo_ptr = this->echo_ptr; + uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF]; + echo_ptr += 4; + if ( echo_ptr >= (g.echo_delay & 15) * 0x800 ) + echo_ptr = 0; + int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend + int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend + this->echo_ptr = echo_ptr; + + // put samples in history ring buffer + const int fir_offset = this->fir_offset; + short (*fir_pos) [2] = &fir_buf [fir_offset]; + this->fir_offset = (fir_offset + 7) & 7; // move backwards one step + fir_pos [0] [0] = (short) fb_left; + fir_pos [0] [1] = (short) fb_right; + fir_pos [8] [0] = (short) fb_left; // duplicate at +8 eliminates wrap checking below + fir_pos [8] [1] = (short) fb_right; + + // FIR + fb_left = fb_left * fir_coeff [7] + + fir_pos [1] [0] * fir_coeff [6] + + fir_pos [2] [0] * fir_coeff [5] + + fir_pos [3] [0] * fir_coeff [4] + + fir_pos [4] [0] * fir_coeff [3] + + fir_pos [5] [0] * fir_coeff [2] + + fir_pos [6] [0] * fir_coeff [1] + + fir_pos [7] [0] * fir_coeff [0]; + + fb_right = fb_right * fir_coeff [7] + + fir_pos [1] [1] * fir_coeff [6] + + fir_pos [2] [1] * fir_coeff [5] + + fir_pos [3] [1] * fir_coeff [4] + + fir_pos [4] [1] * fir_coeff [3] + + fir_pos [5] [1] * fir_coeff [2] + + fir_pos [6] [1] * fir_coeff [1] + + fir_pos [7] [1] * fir_coeff [0]; + + left += (fb_left * g.left_echo_volume ) >> 14; + right += (fb_right * g.right_echo_volume) >> 14; + + // echo buffer feedback + if ( !(g.flags & 0x20) ) + { + echol += (fb_left * g.echo_feedback) >> 14; + echor += (fb_right * g.echo_feedback) >> 14; + SET_LE16( echo_buf , clamp_16( echol ) ); + SET_LE16( echo_buf + 2, clamp_16( echor ) ); + } + + if ( out_buf ) + { + // write final samples + + left = clamp_16( left ); + right = clamp_16( right ); + + int mute = g.flags & 0x40; + + out_buf [0] = (short) left; + out_buf [1] = (short) right; + out_buf += 2; + + // muting + if ( mute ) + { + out_buf [-2] = 0; + out_buf [-1] = 0; + } + } + } +} + +// Base normal_gauss table is almost exactly (with an error of 0 or -1 for each entry): +// int normal_gauss [512]; +// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45 + +// Interleved gauss table (to improve cache coherency). +// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] +const BOOST::int16_t Spc_Dsp::gauss [512] = +{ + 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, + 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, + 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, + 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, + 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, + 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, + 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, + 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, + 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, + 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, + 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, + 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, + 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, + 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, + 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, + 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, + 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, + 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, + 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, + 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, + 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, + 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, + 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, + 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, + 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, + 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, + 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, + 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, + 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, + 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, + 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, + 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, +}; diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h new file mode 100644 index 00000000..36492275 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Dsp.h @@ -0,0 +1,152 @@ +// Super Nintendo (SNES) SPC DSP emulator + +// Game_Music_Emu 0.5.2 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +class Spc_Dsp { + typedef BOOST::int8_t int8_t; + typedef BOOST::uint8_t uint8_t; +public: + + // Keeps pointer to 64K ram + Spc_Dsp( uint8_t* ram ); + + // Mute voice n if bit n (1 << n) of mask is clear. + enum { voice_count = 8 }; + void mute_voices( int mask ); + + // Clear state and silence everything. + void reset(); + + // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to + // the 16-bit sample range. + void set_gain( double ); + + // If true, prevent channels and global volumes from being phase-negated + void disable_surround( bool disable ); + + // Read/write register 'n', where n ranges from 0 to register_count - 1. + enum { register_count = 128 }; + int read ( int n ); + void write( int n, int ); + + // Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL. + void run( long count, short* buf = NULL ); + + +// End of public interface +private: + + struct raw_voice_t { + int8_t left_vol; + int8_t right_vol; + uint8_t rate [2]; + uint8_t waveform; + uint8_t adsr [2]; // envelope rates for attack, decay, and sustain + uint8_t gain; // envelope gain (if not using ADSR) + int8_t envx; // current envelope level + int8_t outx; // current sample + int8_t unused [6]; + }; + + struct globals_t { + int8_t unused1 [12]; + int8_t left_volume; // 0C Main Volume Left (-.7) + int8_t echo_feedback; // 0D Echo Feedback (-.7) + int8_t unused2 [14]; + int8_t right_volume; // 1C Main Volume Right (-.7) + int8_t unused3 [15]; + int8_t left_echo_volume; // 2C Echo Volume Left (-.7) + uint8_t pitch_mods; // 2D Pitch Modulation on/off for each voice + int8_t unused4 [14]; + int8_t right_echo_volume; // 3C Echo Volume Right (-.7) + uint8_t noise_enables; // 3D Noise output on/off for each voice + int8_t unused5 [14]; + uint8_t key_ons; // 4C Key On for each voice + uint8_t echo_ons; // 4D Echo on/off for each voice + int8_t unused6 [14]; + uint8_t key_offs; // 5C key off for each voice (instantiates release mode) + uint8_t wave_page; // 5D source directory (wave table offsets) + int8_t unused7 [14]; + uint8_t flags; // 6C flags and noise freq + uint8_t echo_page; // 6D + int8_t unused8 [14]; + uint8_t wave_ended; // 7C + uint8_t echo_delay; // 7D ms >> 4 + char unused9 [2]; + }; + + union { + raw_voice_t voice [voice_count]; + uint8_t reg [register_count]; + globals_t g; + }; + + uint8_t* const ram; + + // Cache of echo FIR values for faster access + short fir_coeff [voice_count]; + + // fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code + short fir_buf [16] [2]; + int fir_offset; // (0 to 7) + + enum { emu_gain_bits = 8 }; + int emu_gain; + + int keyed_on; // 8-bits for 8 voices + int keys; + + int echo_ptr; + int noise_amp; + int noise; + int noise_count; + + int surround_threshold; + + static BOOST::int16_t const gauss []; + + enum state_t { + state_attack, + state_decay, + state_sustain, + state_release + }; + + struct voice_t { + short volume [2]; + short fraction;// 12-bit fractional position + short interp3; // most recent four decoded samples + short interp2; + short interp1; + short interp0; + short block_remain; // number of nybbles remaining in current block + unsigned short addr; + short block_header; // header byte from current block + short envcnt; + short envx; + short on_cnt; + short enabled; // 7 if enabled, 31 if disabled + short envstate; + short unused; // pad to power of 2 + }; + + voice_t voice_state [voice_count]; + + int clock_envelope( int ); +}; + +inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; } + +inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); } + +inline int Spc_Dsp::read( int i ) +{ + assert( (unsigned) i < register_count ); + return reg [i]; +} + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp new file mode 100644 index 00000000..22be9e2a --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.cpp @@ -0,0 +1,326 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Spc_Emu.h" + +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2004-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" + +Spc_Emu::Spc_Emu() +{ + set_type( gme_spc_type ); + + static const char* const names [Snes_Spc::voice_count] = { + "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" + }; + set_voice_names( names ); + + set_gain( 1.4 ); +} + +Spc_Emu::~Spc_Emu() { } + +// Track info + +long const trailer_offset = 0x10200; + +byte const* Spc_Emu::trailer() const { return &file_data [min( file_size, trailer_offset )]; } + +long Spc_Emu::trailer_size() const { return max( 0L, file_size - trailer_offset ); } + +static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) +{ + // header + byte const* end = begin + size; + if ( size < 8 || memcmp( begin, "xid6", 4 ) ) + { + check( false ); + return; + } + long info_size = get_le32( begin + 4 ); + byte const* in = begin + 8; + if ( end - in > info_size ) + { + dprintf( "Extra data after SPC xid6 info\n" ); + end = in + info_size; + } + + int year = 0; + char copyright [256 + 5]; + int copyright_len = 0; + int const year_len = 5; + + while ( end - in >= 4 ) + { + // header + int id = in [0]; + int data = in [3] * 0x100 + in [2]; + int type = in [1]; + int len = type ? data : 0; + in += 4; + if ( len > end - in ) + { + check( false ); + break; // block goes past end of data + } + + // handle specific block types + char* field = 0; + switch ( id ) + { + case 0x01: field = out->song; break; + case 0x02: field = out->game; break; + case 0x03: field = out->author; break; + case 0x04: field = out->dumper; break; + case 0x07: field = out->comment; break; + case 0x14: year = data; break; + + //case 0x30: // intro length + // Many SPCs have intro length set wrong for looped tracks, making it useless + /* + case 0x30: + check( len == 4 ); + if ( len >= 4 ) + { + out->intro_length = get_le32( in ) / 64; + if ( out->length > 0 ) + { + long loop = out->length - out->intro_length; + if ( loop >= 2000 ) + out->loop_length = loop; + } + } + break; + */ + + case 0x13: + copyright_len = min( len, (int) sizeof copyright - year_len ); + memcpy( ©right [year_len], in, copyright_len ); + break; + + default: + if ( id < 0x01 || (id > 0x07 && id < 0x10) || + (id > 0x14 && id < 0x30) || id > 0x36 ) + dprintf( "Unknown SPC xid6 block: %X\n", (int) id ); + break; + } + if ( field ) + { + check( type == 1 ); + Gme_File::copy_field_( field, (char const*) in, len ); + } + + // skip to next block + in += len; + + // blocks are supposed to be 4-byte aligned with zero-padding... + byte const* unaligned = in; + while ( (in - begin) & 3 && in < end ) + { + if ( *in++ != 0 ) + { + // ...but some files have no padding + in = unaligned; + dprintf( "SPC info tag wasn't properly padded to align\n" ); + break; + } + } + } + + char* p = ©right [year_len]; + if ( year ) + { + *--p = ' '; + for ( int n = 4; n--; ) + { + *--p = char (year % 10 + '0'); + year /= 10; + } + copyright_len += year_len; + } + if ( copyright_len ) + Gme_File::copy_field_( out->copyright, p, copyright_len ); + + check( in == end ); +} + +static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size, + track_info_t* out ) +{ + // decode length (can be in text or binary format, sometimes ambiguous ugh) + long len_secs = 0; + for ( int i = 0; i < 3; i++ ) + { + unsigned n = h.len_secs [i] - '0'; + if ( n > 9 ) + { + // ignore single-digit text lengths + // (except if author field is present and begins at offset 1, ugh) + if ( i == 1 && (h.author [0] || !h.author [1]) ) + len_secs = 0; + break; + } + len_secs *= 10; + len_secs += n; + } + if ( !len_secs || len_secs > 0x1FFF ) + len_secs = get_le16( h.len_secs ); + if ( len_secs < 0x1FFF ) + out->length = len_secs * 1000; + + int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); + Gme_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset ); + + GME_COPY_FIELD( h, out, song ); + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, dumper ); + GME_COPY_FIELD( h, out, comment ); + + if ( xid6_size ) + get_spc_xid6( xid6, xid6_size, out ); +} + +blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const +{ + get_spc_info( header(), trailer(), trailer_size(), out ); + return 0; +} + +static blargg_err_t check_spc_header( void const* header ) +{ + if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Spc_File : Gme_Info_ +{ + Spc_Emu::header_t header; + blargg_vector xid6; + + Spc_File() { set_type( gme_spc_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + long file_size = in.remain(); + if ( file_size < Snes_Spc::spc_file_size ) + return gme_wrong_file_type; + RETURN_ERR( in.read( &header, Spc_Emu::header_size ) ); + RETURN_ERR( check_spc_header( header.tag ) ); + long const xid6_offset = 0x10200; + long xid6_size = file_size - xid6_offset; + if ( xid6_size > 0 ) + { + RETURN_ERR( xid6.resize( xid6_size ) ); + RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_size ) ); + RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); + } + return 0; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_spc_info( header, xid6.begin(), xid6.size(), out ); + return 0; + } +}; + +static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } +static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } + +gme_type_t_ const gme_spc_type [1] = { "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }; + +// Setup + +blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) +{ + apu.set_gain( gain() ); + if ( sample_rate != native_sample_rate ) + { + RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) ); + resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 ); + } + return 0; +} + +void Spc_Emu::mute_voices_( int m ) +{ + Music_Emu::mute_voices_( m ); + apu.mute_voices( m ); +} + +blargg_err_t Spc_Emu::load_mem_( byte const* in, long size ) +{ + assert( offsetof (header_t,unused2 [46]) == header_size ); + file_data = in; + file_size = size; + set_voice_count( Snes_Spc::voice_count ); + if ( size < Snes_Spc::spc_file_size ) + return gme_wrong_file_type; + return check_spc_header( in ); +} + +// Emulation + +void Spc_Emu::set_tempo_( double t ) { apu.set_tempo( t ); } + +blargg_err_t Spc_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + resampler.clear(); + RETURN_ERR( apu.load_spc( file_data, file_size ) ); + apu.clear_echo(); + return 0; +} + +blargg_err_t Spc_Emu::skip_( long count ) +{ + if ( sample_rate() != native_sample_rate ) + { + count = long (count * resampler.ratio()) & ~1; + count -= resampler.skip_input( count ); + } + + // TODO: shouldn't skip be adjusted for the 64 samples read afterwards? + + if ( count > 0 ) + RETURN_ERR( apu.skip( count ) ); + + // eliminate pop due to resampler + const int resampler_latency = 64; + sample_t buf [resampler_latency]; + return play_( resampler_latency, buf ); +} + +blargg_err_t Spc_Emu::play_( long count, sample_t* out ) +{ + if ( sample_rate() == native_sample_rate ) + return apu.play( count, out ); + + long remain = count; + while ( remain > 0 ) + { + remain -= resampler.read( &out [count - remain], remain ); + if ( remain > 0 ) + { + long n = resampler.max_write(); + RETURN_ERR( apu.play( n, resampler.buffer() ) ); + resampler.write( n ); + } + } + check( remain == 0 ); + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h new file mode 100644 index 00000000..44b54c30 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Spc_Emu.h @@ -0,0 +1,77 @@ +// Super Nintendo SPC music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef SPC_EMU_H +#define SPC_EMU_H + +#include "Fir_Resampler.h" +#include "Music_Emu.h" +#include "Snes_Spc.h" + +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 }; + + // SPC file header + enum { header_size = 0x100 }; + struct header_t + { + 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_data; } + + // Prevents channels and global volumes from being phase-negated + void disable_surround( bool disable = true ); + + static gme_type_t static_type() { return gme_spc_type; } + +public: + // deprecated + Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + byte const* trailer() const; // use track_info() + long trailer_size() const; + +public: + Spc_Emu(); + ~Spc_Emu(); +protected: + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t set_sample_rate_( long ); + blargg_err_t start_track_( int ); + blargg_err_t play_( long, sample_t* ); + blargg_err_t skip_( long ); + void mute_voices_( int ); + void set_tempo_( double ); +private: + byte const* file_data; + long file_size; + Fir_Resampler<24> resampler; + Snes_Spc apu; +}; + +inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp new file mode 100644 index 00000000..0fef6bd9 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.cpp @@ -0,0 +1,412 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Vgm_Emu.h" + +#include "blargg_endian.h" +#include +#include + +/* 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" + +double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow +double const rolloff = 0.990; +double const oversample_factor = 1.5; + +Vgm_Emu::Vgm_Emu() +{ + disable_oversampling_ = false; + psg_rate = 0; + set_type( gme_vgm_type ); + + static int const types [8] = { + wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0 + }; + set_voice_types( types ); + + set_silence_lookahead( 1 ); // tracks should already be trimmed + + static equalizer_t const eq = { -14.0, 80 }; + set_equalizer( eq ); +} + +Vgm_Emu::~Vgm_Emu() { } + +// 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 long check_gd3_header( byte const* h, long remain ) +{ + if ( remain < gd3_header_size ) return 0; + if ( memcmp( h, "Gd3 ", 4 ) ) return 0; + if ( get_le32( h + 4 ) >= 0x200 ) return 0; + + long gd3_size = get_le32( h + 8 ); + if ( gd3_size > remain - gd3_header_size ) return 0; + + return gd3_size; +} + +byte const* Vgm_Emu::gd3_data( int* size ) const +{ + if ( size ) + *size = 0; + + long gd3_offset = get_le32( header().gd3_offset ) - 0x2C; + if ( gd3_offset < 0 ) + return 0; + + byte const* gd3 = data + header_size + gd3_offset; + long gd3_size = check_gd3_header( gd3, data_end - gd3 ); + if ( !gd3_size ) + return 0; + + if ( size ) + *size = gd3_size + gd3_header_size; + + return gd3; +} + +static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) +{ + long length = get_le32( h.track_duration ) * 10 / 441; + if ( length > 0 ) + { + long 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; + } + else + { + out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase) + out->intro_length = length; // make it clear that track is no longer than length + out->loop_length = 0; + } + } +} + +blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const +{ + get_vgm_length( header(), out ); + + int size; + byte const* gd3 = gd3_data( &size ); + if ( gd3 ) + parse_gd3( gd3 + gd3_header_size, gd3 + size, out ); + + return 0; +} + +static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h ) +{ + if ( memcmp( h.tag, "Vgm ", 4 ) ) + return gme_wrong_file_type; + return 0; +} + +struct Vgm_File : Gme_Info_ +{ + Vgm_Emu::header_t h; + blargg_vector gd3; + + Vgm_File() { set_type( gme_vgm_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + long file_size = in.remain(); + if ( file_size <= Vgm_Emu::header_size ) + return gme_wrong_file_type; + + RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); + RETURN_ERR( check_vgm_header( h ) ); + + long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; + long remain = file_size - Vgm_Emu::header_size - gd3_offset; + byte gd3_h [gd3_header_size]; + if ( gd3_offset > 0 && remain >= gd3_header_size ) + { + RETURN_ERR( in.skip( gd3_offset ) ); + RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); + long 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() ) ); + } + } + return 0; + } + + 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 0; + } +}; + +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 ) +{ + if ( psg_rate ) + { + vgm_rate = (long) (44100 * t + 0.5); + blip_time_factor = (long) floor( double (1L << blip_time_bits) / vgm_rate * psg_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 = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 ); + //vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 ); + + fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); + } +} + +blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate ) +{ + RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) ); + return Classic_Emu::set_sample_rate_( sample_rate ); +} + +void Vgm_Emu::update_eq( blip_eq_t const& eq ) +{ + psg.treble_eq( eq ); + dac_synth.treble_eq( eq ); +} + +void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < psg.osc_count ) + psg.osc_output( i, c, l, r ); +} + +void Vgm_Emu::mute_voices_( int mask ) +{ + Classic_Emu::mute_voices_( mask ); + dac_synth.output( &blip_buf ); + if ( uses_fm ) + { + psg.output( (mask & 0x80) ? 0 : &blip_buf ); + if ( ym2612.enabled() ) + { + dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); + ym2612.mute_voices( mask ); + } + + if ( ym2413.enabled() ) + { + int m = mask & 0x3F; + if ( mask & 0x20 ) + m |= 0x01E0; // channels 5-8 + if ( mask & 0x40 ) + m |= 0x3E00; + ym2413.mute_voices( m ); + } + } +} + +blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) +{ + assert( offsetof (header_t,unused2 [8]) == header_size ); + + if ( new_size <= header_size ) + return gme_wrong_file_type; + + header_t const& h = *(header_t const*) new_data; + + RETURN_ERR( check_vgm_header( h ) ); + + check( get_le32( h.version ) <= 0x150 ); + + // psg rate + psg_rate = get_le32( h.psg_rate ); + if ( !psg_rate ) + psg_rate = 3579545; + blip_buf.clock_rate( psg_rate ); + + data = new_data; + data_end = new_data + new_size; + + // get loop + loop_begin = data_end; + if ( get_le32( h.loop_offset ) ) + loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; + + set_voice_count( psg.osc_count ); + + RETURN_ERR( setup_fm() ); + + 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( uses_fm ? fm_names : psg_names ); + + // do after FM in case output buffer is changed + return Classic_Emu::setup_buffer( psg_rate ); +} + +blargg_err_t Vgm_Emu::setup_fm() +{ + long ym2612_rate = get_le32( header().ym2612_rate ); + long ym2413_rate = get_le32( header().ym2413_rate ); + if ( ym2413_rate && get_le32( header().version ) < 0x110 ) + update_fm_rates( &ym2413_rate, &ym2612_rate ); + + uses_fm = false; + + fm_rate = blip_buf.sample_rate() * oversample_factor; + + if ( ym2612_rate ) + { + uses_fm = true; + if ( disable_oversampling_ ) + fm_rate = ym2612_rate / 144.0; + Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); + RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); + ym2612.enable( true ); + set_voice_count( 8 ); + } + + if ( !uses_fm && ym2413_rate ) + { + uses_fm = true; + if ( disable_oversampling_ ) + fm_rate = ym2413_rate / 72.0; + Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); + int result = ym2413.set_rate( fm_rate, ym2413_rate ); + if ( result == 2 ) + return "YM2413 FM sound isn't supported"; + CHECK_ALLOC( !result ); + ym2413.enable( true ); + set_voice_count( 8 ); + } + + if ( uses_fm ) + { + RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); + psg.volume( 0.135 * fm_gain * gain() ); + } + else + { + ym2612.enable( false ); + ym2413.enable( false ); + psg.volume( gain() ); + } + + return 0; +} + +// Emulation + +blargg_err_t Vgm_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + psg.reset( get_le16( header().noise_feedback ), header().noise_width ); + + dac_disabled = -1; + pos = data + header_size; + pcm_data = pos; + pcm_pos = pos; + dac_amp = -1; + vgm_time = 0; + if ( get_le32( header().version ) >= 0x150 ) + { + long data_offset = get_le32( header().data_offset ); + check( data_offset ); + if ( data_offset ) + pos += data_offset + offsetof (header_t,data_offset) - 0x40; + } + + if ( uses_fm ) + { + if ( ym2413.enabled() ) + ym2413.reset(); + + if ( ym2612.enabled() ) + ym2612.reset(); + + fm_time_offset = 0; + blip_buf.clear(); + Dual_Resampler::clear(); + } + return 0; +} + +blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) +{ + time_io = run_commands( msec * vgm_rate / 1000 ); + psg.end_frame( time_io ); + return 0; +} + +blargg_err_t Vgm_Emu::play_( long count, sample_t* out ) +{ + if ( !uses_fm ) + return Classic_Emu::play_( count, out ); + + Dual_Resampler::dual_play( count, out, blip_buf ); + return 0; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h new file mode 100644 index 00000000..bcb784d5 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu.h @@ -0,0 +1,84 @@ +// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator + +// Game_Music_Emu 0.5.2 +#ifndef VGM_EMU_H +#define VGM_EMU_H + +#include "Vgm_Emu_Impl.h" + +// Emulates VGM music using SN76489/SN76496 PSG, 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. Currently YM2413 support requires that you supply a +// YM2413 sound chip emulator. I can provide one I've modified to work with the library. +class Vgm_Emu : public Vgm_Emu_Impl { +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 !uses_fm; } + + // Disable 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 header format + enum { header_size = 0x40 }; + struct header_t + { + char tag [4]; + byte data_size [4]; + byte version [4]; + byte psg_rate [4]; + byte ym2413_rate [4]; + byte gd3_offset [4]; + byte track_duration [4]; + byte loop_offset [4]; + byte loop_duration [4]; + byte frame_rate [4]; + byte noise_feedback [2]; + byte noise_width; + byte unused1; + byte ym2612_rate [4]; + byte ym2151_rate [4]; + byte data_offset [4]; + byte unused2 [8]; + }; + + // Header for currently loaded file + header_t const& header() const { return *(header_t const*) data; } + + static gme_type_t static_type() { return gme_vgm_type; } + +public: + // deprecated + Music_Emu::load; + blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader + { return load_remaining_( &h, sizeof h, in ); } + byte const* gd3_data( int* size_out = 0 ) const; // use track_info() + +public: + Vgm_Emu(); + ~Vgm_Emu(); +protected: + blargg_err_t track_info_( track_info_t*, int track ) const; + blargg_err_t load_mem_( byte const*, long ); + blargg_err_t set_sample_rate_( long sample_rate ); + blargg_err_t start_track_( int ); + blargg_err_t play_( long count, sample_t* ); + blargg_err_t run_clocks( blip_time_t&, int ); + void set_tempo_( double ); + void mute_voices_( int mask ); + void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + void update_eq( blip_eq_t const& ); +private: + // removed; use disable_oversampling() and set_tempo() instead + Vgm_Emu( bool oversample, double tempo = 1.0 ); + double fm_rate; + long psg_rate; + long vgm_rate; + bool disable_oversampling_; + bool uses_fm; + blargg_err_t setup_fm(); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp new file mode 100644 index 00000000..a2d7c93e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.cpp @@ -0,0 +1,314 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Vgm_Emu.h" + +#include +#include +#include "blargg_endian.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" + +enum { + cmd_gg_stereo = 0x4F, + cmd_psg = 0x50, + cmd_ym2413 = 0x51, + cmd_ym2612_port0 = 0x52, + cmd_ym2612_port1 = 0x53, + cmd_ym2151 = 0x54, + cmd_delay = 0x61, + cmd_delay_735 = 0x62, + cmd_delay_882 = 0x63, + cmd_byte_delay = 0x64, + cmd_end = 0x66, + cmd_data_block = 0x67, + cmd_short_delay = 0x70, + cmd_pcm_delay = 0x80, + cmd_pcm_seek = 0xE0, + + pcm_block_type = 0x00, + ym2612_dac_port = 0x2A +}; + +inline int command_len( int command ) +{ + switch ( command >> 4 ) + { + case 0x03: + case 0x04: + return 2; + + case 0x05: + case 0x0A: + case 0x0B: + return 3; + + case 0x0C: + case 0x0D: + return 4; + + case 0x0E: + case 0x0F: + return 5; + } + + check( false ); + return 1; +} + +template +inline void Ym_Emu::begin_frame( short* p ) +{ + require( enabled() ); + out = p; + last_time = 0; +} + +template +inline int Ym_Emu::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; +} + +inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const +{ + return (t * fm_time_factor + fm_time_offset) >> fm_time_bits; +} + +inline blip_time_t Vgm_Emu_Impl::to_blip_time( vgm_time_t t ) const +{ + return (t * blip_time_factor) >> blip_time_bits; +} + +void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp ) +{ + blip_time_t blip_time = to_blip_time( vgm_time ); + int old = dac_amp; + int delta = amp - old; + dac_amp = amp; + if ( old >= 0 ) + dac_synth.offset_inline( blip_time, delta, &blip_buf ); + else + dac_amp |= dac_disabled; +} + +blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) +{ + vgm_time_t vgm_time = this->vgm_time; + byte const* pos = this->pos; + if ( pos >= data_end ) + { + set_track_ended(); + if ( pos > data_end ) + set_warning( "Stream lacked end event" ); + } + + while ( vgm_time < end_time && pos < data_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: + pos = loop_begin; // if not looped, loop_begin == data_end + break; + + case cmd_delay_735: + vgm_time += 735; + break; + + case cmd_delay_882: + vgm_time += 882; + break; + + case cmd_gg_stereo: + psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ ); + break; + + case cmd_psg: + psg.write_data( to_blip_time( vgm_time ), *pos++ ); + break; + + case cmd_delay: + vgm_time += pos [1] * 0x100L + pos [0]; + pos += 2; + break; + + case cmd_byte_delay: + vgm_time += *pos++; + break; + + case cmd_ym2413: + if ( ym2413.run_until( to_fm_time( vgm_time ) ) ) + ym2413.write( pos [0], pos [1] ); + pos += 2; + break; + + case cmd_ym2612_port0: + if ( pos [0] == ym2612_dac_port ) + { + write_pcm( vgm_time, pos [1] ); + } + else if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) + { + if ( pos [0] == 0x2B ) + { + dac_disabled = (pos [1] >> 7 & 1) - 1; + dac_amp |= dac_disabled; + } + ym2612.write0( pos [0], pos [1] ); + } + pos += 2; + break; + + case cmd_ym2612_port1: + if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) + ym2612.write1( pos [0], pos [1] ); + pos += 2; + break; + + case cmd_data_block: { + check( *pos == cmd_end ); + int type = pos [1]; + long size = get_le32( pos + 2 ); + pos += 6; + if ( type == pcm_block_type ) + pcm_data = pos; + pos += size; + break; + } + + case cmd_pcm_seek: + pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L + + pos [1] * 0x100L + pos [0]; + pos += 4; + break; + + default: + int cmd = pos [-1]; + switch ( cmd & 0xF0 ) + { + case cmd_pcm_delay: + write_pcm( vgm_time, *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_blip_time( end_time ); +} + +int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) +{ + // to do: timing is working mostly by luck + + int min_pairs = sample_count >> 1; + int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1; + assert( to_fm_time( vgm_time ) <= min_pairs ); + int pairs = min_pairs; + while ( (pairs = to_fm_time( vgm_time )) < min_pairs ) + vgm_time++; + //dprintf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs ); + + if ( ym2612.enabled() ) + { + ym2612.begin_frame( buf ); + memset( buf, 0, pairs * stereo * sizeof *buf ); + } + else if ( ym2413.enabled() ) + { + ym2413.begin_frame( buf ); + } + + run_commands( vgm_time ); + ym2612.run_until( pairs ); + ym2413.run_until( pairs ); + + fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - + ((long) pairs << fm_time_bits); + + psg.end_frame( blip_time ); + + return pairs * stereo; +} + +// Update pre-1.10 header FM rates by scanning commands +void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const +{ + byte const* p = data + 0x40; + while ( p < data_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_ym2413: + *ym2612_rate = 0; + return; + + case cmd_ym2612_port0: + case cmd_ym2612_port1: + *ym2612_rate = *ym2413_rate; + *ym2413_rate = 0; + return; + + case cmd_ym2151: + *ym2413_rate = 0; + *ym2612_rate = 0; + return; + + default: + p += command_len( *p ); + } + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h new file mode 100644 index 00000000..4d387d09 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Vgm_Emu_Impl.h @@ -0,0 +1,71 @@ +// Low-level parts of Vgm_Emu + +// Game_Music_Emu 0.5.2 +#ifndef VGM_EMU_IMPL_H +#define VGM_EMU_IMPL_H + +#include "Dual_Resampler.h" +#include "Classic_Emu.h" +#include "Ym2413_Emu.h" +#include "Ym2612_Emu.h" +#include "Sms_Apu.h" + +template +class Ym_Emu : public Emu { +protected: + int last_time; + short* out; + enum { disabled_time = -1 }; +public: + Ym_Emu() : last_time( disabled_time ), out( NULL ) { } + void enable( bool b ) { last_time = b ? 0 : disabled_time; } + bool enabled() const { return last_time != disabled_time; } + void begin_frame( short* p ); + int run_until( int time ); +}; + +class Vgm_Emu_Impl : public Classic_Emu, private Dual_Resampler { +public: + typedef Classic_Emu::sample_t sample_t; +protected: + enum { stereo = 2 }; + + typedef int vgm_time_t; + + enum { fm_time_bits = 12 }; + typedef int fm_time_t; + long fm_time_offset; + int fm_time_factor; + fm_time_t to_fm_time( vgm_time_t ) const; + + enum { blip_time_bits = 12 }; + int blip_time_factor; + blip_time_t to_blip_time( vgm_time_t ) const; + + byte const* data; + byte const* loop_begin; + byte const* data_end; + void update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const; + + vgm_time_t vgm_time; + byte const* pos; + blip_time_t run_commands( vgm_time_t ); + int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); + + byte const* pcm_data; + byte const* pcm_pos; + int dac_amp; + int dac_disabled; // -1 if disabled + void write_pcm( vgm_time_t, int amp ); + + Ym_Emu ym2612; + Ym_Emu ym2413; + + Blip_Buffer blip_buf; + Sms_Apu psg; + Blip_Synth dac_synth; + + friend class Vgm_Emu; +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp new file mode 100644 index 00000000..ede67304 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.cpp @@ -0,0 +1,21 @@ + +// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip + +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Ym2413_Emu.h" + +Ym2413_Emu::Ym2413_Emu() { } + +Ym2413_Emu::~Ym2413_Emu() { } + +int Ym2413_Emu::set_rate( double, double ) { return 2; } + +void Ym2413_Emu::reset() { } + +void Ym2413_Emu::write( int, int ) { } + +void Ym2413_Emu::mute_voices( int ) { } + +void Ym2413_Emu::run( int, sample_t* ) { } + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h new file mode 100644 index 00000000..98a2a48e --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2413_Emu.h @@ -0,0 +1,33 @@ +// YM2413 FM sound chip emulator interface + +// Game_Music_Emu 0.5.2 +#ifndef YM2413_EMU_H +#define YM2413_EMU_H + +class Ym2413_Emu { + struct OPLL* opll; +public: + Ym2413_Emu(); + ~Ym2413_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + int set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 14 }; + void mute_voices( int mask ); + + // Write 'data' to 'addr' + void write( int addr, int data ); + + // Run and write pair_count 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/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp new file mode 100644 index 00000000..41ebb093 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.cpp @@ -0,0 +1,1319 @@ +// Game_Music_Emu 0.5.2. 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-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 */ + +// 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 only 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 + int env_xor; + int env_max; + + 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 +}; + +#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) + +#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 + +inline void set_seg( slot_t& s, int seg ) +{ + s.env_xor = 0; + s.env_max = INT_MAX; + s.SEG = seg; + if ( seg & 4 ) + { + s.env_xor = ENV_MASK; + s.env_max = ENV_MASK; + } +} + +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 */ + + set_seg( sl, (data & 8) ? (data & 0x0F) : 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 > sample_rate ); + + int i; + + // 144 = 12 * (prescale * 2) = 12 * 6 * 2 + // prescale set to 6 by default + + double Frequence = clock_rate / sample_rate / 144.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 + { + double x = MAX_OUT; // Max output + x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage + + g.TL_TAB [i] = (int) x; + 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++) + { + double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus + x = 20 * log10(1 / x); // 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 = 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 + + g.LFO_ENV_TAB [i] = (int) x; + + x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x *= (double) ((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(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); + x *= ENV_LENGHT; + + g.ENV_TAB [i] = (int) x; + + // Decay curve (just linear) + x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); + x *= ENV_LENGHT; + + g.ENV_TAB [ENV_LENGHT + i] = (int) x; + } + 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; // 3 and not 6 (Mickey Mania first music for test) + x /= ENV_STEP; + + 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 + + for(i = 0; i < 2048; i++) + { + double x = (double) (i) * 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 + + g.FINC_TAB [i] = (unsigned int) x; + } + + // 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 = 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 g.ENV_TAB + + 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 + + for(i = 0; i < 4; i++) + { + for (int j = 0; j < 32; j++) + { +#if ((SIN_LBITS + SIN_HBITS - 21) < 0) + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); +#else + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); +#endif + + g.DT_TAB [i + 0] [j] = (int) y; + g.DT_TAB [i + 4] [j] = (int) -y; + } + } + + // Tableau LFO + g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (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 + { + int release = sl->SEG & 1; + + if ( !release ) + { + // re KEY ON + + // sl->Fcnt = 0; + // sl->ChgEnM = ~0; + + sl->Ecnt = 0; + sl->Einc = sl->EincA; + sl->Ecmp = ENV_DECAY; + sl->Ecurp = ATTACK; + } + + set_seg( *sl, (sl->SEG << 1) & 4 ); + + if ( !release ) + 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] + ch.SLOT [S##x].TLL; \ + int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \ + ((temp##x - ch.SLOT [S##x].env_max) >> 31); + + 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 >>= 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)) + (1L << (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; + + long 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 ); } diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h new file mode 100644 index 00000000..383ac72d --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/Ym2612_Emu.h @@ -0,0 +1,38 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu 0.5.2 +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +struct Ym2612_Impl; + +class Ym2612_Emu { + Ym2612_Impl* impl; +public: + Ym2612_Emu() { impl = 0; } + ~Ym2612_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count 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/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h new file mode 100644 index 00000000..9ab0bd7d --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_common.h @@ -0,0 +1,179 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +#ifndef STATIC_CAST + #define STATIC_CAST(T,expr) ((T) (expr)) +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + void* p = realloc( begin_, n * sizeof (T) ); + if ( !p && n ) + return "Out of memory"; + begin_ = (T*) p; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + #if __cplusplus < 199711 + #define BLARGG_THROWS( spec ) + #else + #define BLARGG_THROWS( spec ) throw spec + #endif + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough +#include + +#if 0 +#if INT_MAX >= 0x7FFFFFFF + typedef int blargg_long; +#else + typedef long blargg_long; +#endif +#endif +typedef int64_t blargg_long; + +#if UINT_MAX >= 0xFFFFFFFF + typedef unsigned blargg_ulong; +#else + typedef unsigned long blargg_ulong; +#endif + +// BOOST::int8_t etc. + +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST + +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +#endif +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h new file mode 100644 index 00000000..9e9c751d --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_config.h @@ -0,0 +1,30 @@ +// Library configuration. Modify this file as necessary. + +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to use zlib for transparent decompression of gzipped files +//#define HAVE_ZLIB_H + +// Uncomment to support only the listed game music types. See gme_type_list.cpp +// for a list of all types. +//#define GME_TYPE_LIST gme_nsf_type, gme_gbs_type + +// Uncomment to enable platform-specific optimizations +//#define BLARGG_NONPORTABLE 1 + +// Uncomment to use faster, lower quality sound synthesis +//#define BLIP_BUFFER_FAST 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h new file mode 100644 index 00000000..67165565 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_endian.h @@ -0,0 +1,158 @@ +// CPU Byte Order Utilities + +// Game_Music_Emu 0.5.2 +#ifndef BLARGG_ENDIAN +#define BLARGG_ENDIAN + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__mips__) || defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#else + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) { + return ((unsigned char const*) p) [1] * 0x100u + + ((unsigned char const*) p) [0]; +} +inline unsigned get_be16( void const* p ) { + return ((unsigned char const*) p) [0] * 0x100u + + ((unsigned char const*) p) [1]; +} +inline blargg_ulong get_le32( void const* p ) { + return ((unsigned char const*) p) [3] * 0x01000000u + + ((unsigned char const*) p) [2] * 0x00010000u + + ((unsigned char const*) p) [1] * 0x00000100u + + ((unsigned char const*) p) [0]; +} +inline blargg_ulong get_be32( void const* p ) { + return ((unsigned char const*) p) [0] * 0x01000000u + + ((unsigned char const*) p) [1] * 0x00010000u + + ((unsigned char const*) p) [2] * 0x00000100u + + ((unsigned char const*) p) [3]; +} +inline void set_le16( void* p, unsigned n ) { + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} +inline void set_be16( void* p, unsigned n ) { + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} +inline void set_le32( void* p, blargg_ulong n ) { + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} +inline void set_be32( void* p, blargg_ulong n ) { + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [3] = (unsigned char) n; +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #endif + + #if BLARGG_CPU_POWERPC && defined (__MWERKS__) + // PowerPC has special byte-reversed instructions + // to do: assumes that PowerPC is running in big-endian mode + // to do: implement for other compilers which don't support these macros + #define GET_LE16( addr ) (__lhbrx( (addr), 0 )) + #define GET_LE32( addr ) (__lwbrx( (addr), 0 )) + #define SET_LE16( addr, data ) (__sthbrx( (data), (addr), 0 )) + #define SET_LE32( addr, data ) (__stwbrx( (data), (addr), 0 )) + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } +inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } +inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h new file mode 100644 index 00000000..945bf349 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/blargg_source.h @@ -0,0 +1,78 @@ +// Included at the beginning of library source files, after all other #include lines +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +// If debugging is enabled, abort program if expr is false. Meant for checking +// internal state and consistency. A failed assertion indicates a bug in the module. +// void assert( bool expr ); +#include + +// If debugging is enabled and expr is false, abort program. Meant for checking +// caller-supplied parameters and operations that are outside the control of the +// module. A failed requirement indicates a bug outside the module. +// void require( bool expr ); +#undef require +#define require( expr ) assert( expr ) + +// Like printf() except output goes to debug log file. Might be defined to do +// nothing (not even evaluate its arguments). +// void dprintf( const char* format, ... ); +inline void blargg_dprintf_( const char*, ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +// If enabled, evaluate expr and if false, make debug log entry with source file +// and line. Meant for finding situations that should be examined further, but that +// don't indicate a problem. In all cases, execution continues normally. +#undef check +#define check( expr ) ((void) 0) + +// If expr yields error string, return it from current function, otherwise continue. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + blargg_err_t blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +// If ptr is 0, return out of memory error string. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) + +// Avoid any macros which evaluate their arguments multiple times +#undef min +#undef max + +// using const references generates crappy code, and I am currenly only using these +// for built-in types, so they take arguments by value + +template +inline T min( T x, T y ) +{ + if ( x < y ) + return x; + return y; +} + +template +inline T max( T x, T y ) +{ + if ( x < y ) + return y; + return x; +} + +// TODO: good idea? bad idea? +#undef byte +#define byte byte_ +typedef unsigned char byte; + +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h new file mode 100644 index 00000000..ada99ead --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/gb_cpu_io.h @@ -0,0 +1,72 @@ + +#include "Gbs_Emu.h" + +#include "blargg_source.h" + +int Gbs_Emu::cpu_read( gb_addr_t addr ) +{ + int result = *cpu::get_code( addr ); + if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) + result = apu.read_register( clock(), addr ); +#ifndef NDEBUG + else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) + dprintf( "Read from unmapped memory $%.4x\n", (unsigned) addr ); + else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 ) + dprintf( "Unhandled I/O read 0x%4X\n", (unsigned) addr ); +#endif + return result; +} + +void Gbs_Emu::cpu_write( gb_addr_t addr, int data ) +{ + unsigned offset = addr - ram_addr; + if ( offset <= 0xFFFF - ram_addr ) + { + ram [offset] = data; + if ( (addr ^ 0xE000) <= 0x1F80 - 1 ) + { + if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) + { + GME_APU_HOOK( this, addr - Gb_Apu::start_addr, data ); + apu.write_register( clock(), addr, data ); + } + else if ( (addr ^ 0xFF06) < 2 ) + update_timer(); + else if ( addr == joypad_addr ) + ram [offset] = 0; // keep joypad return value 0 + else + ram [offset] = 0xFF; + + //if ( addr == 0xFFFF ) + // dprintf( "Wrote interrupt mask\n" ); + } + } + else if ( (addr ^ 0x2000) <= 0x2000 - 1 ) + { + set_bank( data ); + } +#ifndef NDEBUG + else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 ) + { + dprintf( "Wrote to unmapped memory $%.4x\n", (unsigned) addr ); + } +#endif +} + +#define CPU_READ_FAST( cpu, addr, time, out ) \ + CPU_READ_FAST_( STATIC_CAST(Gbs_Emu*,cpu), addr, time, out ) + +#define CPU_READ_FAST_( emu, addr, time, out ) \ +{\ + out = READ_PROG( addr );\ + if ( unsigned (addr - Gb_Apu::start_addr) <= Gb_Apu::register_count )\ + out = emu->apu.read_register( emu->cpu_time - time * clocks_per_instr, addr );\ + else\ + check( out == emu->cpu_read( addr ) );\ +} + +#define CPU_READ( cpu, addr, time ) \ + STATIC_CAST(Gbs_Emu*,cpu)->cpu_read( addr ) + +#define CPU_WRITE( cpu, addr, data, time ) \ + STATIC_CAST(Gbs_Emu*,cpu)->cpu_write( addr, data ) diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/gme.cpp b/plugins/gme/Game_Music_Emu-0.5.2/gme/gme.cpp new file mode 100644 index 00000000..d6cebfa8 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/gme.cpp @@ -0,0 +1,256 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Music_Emu.h" + +#if !GME_DISABLE_STEREO_DEPTH +#include "Effects_Buffer.h" +#endif +#include "blargg_endian.h" +#include +#include + +/* 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" + +#ifndef GME_TYPE_LIST + +// Default list of all supported game music types (copy this to blargg_config.h +// if you want to modify it) +#define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type + +#endif + +static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; + +gme_type_t const* gme_type_list() +{ + return gme_type_list_; +} + +const char* gme_identify_header( void const* header ) +{ + switch ( get_be32( header ) ) + { + case BLARGG_4CHAR('Z','X','A','Y'): return "AY"; + case BLARGG_4CHAR('G','B','S',0x01): return "GBS"; + case BLARGG_4CHAR('G','Y','M','X'): return "GYM"; + case BLARGG_4CHAR('H','E','S','M'): return "HES"; + case BLARGG_4CHAR('K','S','C','C'): + case BLARGG_4CHAR('K','S','S','X'): return "KSS"; + case BLARGG_4CHAR('N','E','S','M'): return "NSF"; + case BLARGG_4CHAR('N','S','F','E'): return "NSFE"; + case BLARGG_4CHAR('S','A','P',0x0D): return "SAP"; + case BLARGG_4CHAR('S','N','E','S'): return "SPC"; + case BLARGG_4CHAR('V','g','m',' '): return "VGM"; + } + return ""; +} + +static void to_uppercase( const char* in, int len, char* out ) +{ + for ( int i = 0; i < len; i++ ) + { + if ( !(out [i] = toupper( in [i] )) ) + return; + } + *out = 0; // extension too long +} + +gme_type_t gme_identify_extension( const char* extension_ ) +{ + char const* end = strrchr( extension_, '.' ); + if ( end ) + extension_ = end + 1; + + char extension [6]; + to_uppercase( extension_, sizeof extension, extension ); + + for ( gme_type_t const* types = gme_type_list_; *types; types++ ) + if ( !strcmp( extension, (*types)->extension_ ) ) + return *types; + return 0; +} + +gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) +{ + *type_out = gme_identify_extension( path ); + // TODO: don't examine header if file has extension? + if ( !*type_out ) + { + char header [4]; + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + RETURN_ERR( in.read( header, sizeof header ) ); + *type_out = gme_identify_extension( gme_identify_header( header ) ); + } + return 0; +} + +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ) +{ + require( (data || !size) && out ); + *out = 0; + + gme_type_t file_type = 0; + if ( size >= 4 ) + file_type = gme_identify_extension( gme_identify_header( data ) ); + if ( !file_type ) + return gme_wrong_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + gme_err_t err = gme_load_data( emu, data, size ); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ) +{ + require( path && out ); + *out = 0; + + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + + char header [4]; + int header_size = 0; + + gme_type_t file_type = gme_identify_extension( path ); + if ( !file_type ) + { + header_size = sizeof header; + RETURN_ERR( in.read( header, sizeof header ) ); + file_type = gme_identify_extension( gme_identify_header( header ) ); + } + if ( !file_type ) + return gme_wrong_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + // optimization: avoids seeking/re-reading header + Remaining_Reader rem( header, header_size, &in ); + gme_err_t err = emu->load( rem ); + in.close(); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +Music_Emu* gme_new_emu( gme_type_t type, long rate ) +{ + if ( type ) + { + if ( rate == gme_info_only ) + return type->new_info(); + + Music_Emu* me = type->new_emu(); + if ( me ) + { + #if !GME_DISABLE_STEREO_DEPTH + if ( type->flags_ & 1 ) + { + me->effects_buffer = BLARGG_NEW Effects_Buffer; + if ( me->effects_buffer ) + me->set_buffer( me->effects_buffer ); + } + + if ( !(type->flags_ & 1) || me->effects_buffer ) + #endif + { + if ( !me->set_sample_rate( rate ) ) + { + check( me->type() == type ); + return me; + } + } + delete me; + } + } + return 0; +} + +gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } + +gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) +{ + Mem_File_Reader in( data, size ); + return me->load( in ); +} + +gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) +{ + Callback_Reader in( func, size, data ); + return me->load( in ); +} + +void gme_delete( Music_Emu* me ) { delete me; } + +gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } + +const char* gme_warning( Music_Emu* me ) { return me->warning(); } + +int gme_track_count( Music_Emu const* me ) { return me->track_count(); } + +const char* gme_track_info( Music_Emu const* me, track_info_t* out, int track ) +{ + return me->track_info( out, track ); +} + +void gme_set_stereo_depth( Music_Emu* me, double depth ) +{ +#if !GME_DISABLE_STEREO_DEPTH + if ( me->effects_buffer ) + STATIC_CAST(Effects_Buffer*,me->effects_buffer)->set_depth( depth ); +#endif +} + +void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } +void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } +void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } + +gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } +gme_err_t gme_play ( Music_Emu* me, long n, short* p ) { return me->play( n, p ); } +void gme_set_fade ( Music_Emu* me, long start_msec ) { me->set_fade( start_msec ); } +int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } +long gme_tell ( Music_Emu const* me ) { return me->tell(); } +gme_err_t gme_seek ( Music_Emu* me, long msec ) { return me->seek( msec ); } +int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } +void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } +void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } +void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } +void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } +void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) { me->set_equalizer( *eq ); } +gme_equalizer_t gme_equalizer( Music_Emu const* me ) { return me->equalizer(); } +const char** gme_voice_names ( Music_Emu const* me ) { return me->voice_names(); } diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/gme.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/gme.h new file mode 100644 index 00000000..469c901c --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/gme.h @@ -0,0 +1,222 @@ +/* Game music emulator library C interface (also usable from C++) */ + +/* Game_Music_Emu 0.5.2 */ +#ifndef GME_H +#define GME_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Error string returned by library functions, or NULL if no error (success) */ +typedef const char* gme_err_t; + +/* First parameter of most gme_ functions is a pointer to the Music_Emu */ +typedef struct Music_Emu Music_Emu; + + +/******** Basic operations ********/ + +/* Create emulator and load game music file/data into it. Sets *out to new emulator. */ +gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ); + +/* Number of tracks available */ +int gme_track_count( Music_Emu const* ); + +/* Start a track, where 0 is the first track */ +gme_err_t gme_start_track( Music_Emu*, int index ); + +/* Generate 'count' 16-bit signed samples info 'out'. Output is in stereo. */ +gme_err_t gme_play( Music_Emu*, long count, short* out ); + +/* Finish using emulator and free memory */ +void gme_delete( Music_Emu* ); + + +/******** Track position/length ********/ + +/* Set time to start fading track out. Once fade ends track_ended() returns true. +Fade time can be changed while track is playing. */ +void gme_set_fade( Music_Emu*, long start_msec ); + +/* True if a track has reached its end */ +int gme_track_ended( Music_Emu const* ); + +/* Number of milliseconds (1000 = one second) played since beginning of track */ +long gme_tell( Music_Emu const* ); + +/* Seek to new time in track. Seeking backwards or far forward can take a while. */ +gme_err_t gme_seek( Music_Emu*, long msec ); + + +/******** Informational ********/ + +/* If you only need track information from a music file, pass gme_info_only for +sample_rate to open/load. */ +enum { gme_info_only = -1 }; + +/* Most recent warning string, or NULL if none. Clears current warning after returning. +Warning is also cleared when loading a file and starting a track. */ +const char* gme_warning( Music_Emu* ); + +/* Load m3u playlist file (must be done after loading music) */ +gme_err_t gme_load_m3u( Music_Emu*, const char* path ); + +/* Clear any loaded m3u playlist and any internal playlist that the music format +supports (NSFE for example). */ +void gme_clear_playlist( Music_Emu* ); + +/* Get information for a particular track (length, name, author, etc.) */ +typedef struct track_info_t track_info_t; +gme_err_t gme_track_info( Music_Emu const*, track_info_t* out, int track ); + +struct track_info_t +{ + long track_count; + + /* times in milliseconds; -1 if unknown */ + long length; + long intro_length; + long loop_length; + + /* empty string if not available */ + char system [256]; + char game [256]; + char song [256]; + char author [256]; + char copyright [256]; + char comment [256]; + char dumper [256]; +}; +enum { gme_max_field = 255 }; + + +/******** Advanced playback ********/ + +/* Adjust stereo echo depth, where 0.0 = off and 1.0 = maximum. Has no effect for +GYM, SPC, and Sega Genesis VGM music */ +void gme_set_stereo_depth( Music_Emu*, double depth ); + +/* Disable automatic end-of-track detection and skipping of silence at beginning +if ignore is true */ +void gme_ignore_silence( Music_Emu*, int ignore ); + +/* Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. +Track length as returned by track_info() assumes a tempo of 1.0. */ +void gme_set_tempo( Music_Emu*, double tempo ); + +/* Number of voices used by currently loaded file */ +int gme_voice_count( Music_Emu const* ); + +/* Names of voices */ +const char** gme_voice_names( Music_Emu const* ); + +/* Mute/unmute voice i, where voice 0 is first voice */ +void gme_mute_voice( Music_Emu*, int index, int mute ); + +/* Set muting state of all voices at once using a bit mask, where -1 mutes all +voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ +void gme_mute_voices( Music_Emu*, int muting_mask ); + +/* Frequency equalizer parameters (see gme.txt) */ +typedef struct gme_equalizer_t +{ + double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ + long bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ +} gme_equalizer_t; + +/* Get current frequency equalizater parameters */ +gme_equalizer_t gme_equalizer( Music_Emu const* ); + +/* Change frequency equalizer parameters */ +void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); + + + +/******** Game music types ********/ + +/* gme_type_t is a pointer to this structure. For example, gme_nsf_type->system is +"Nintendo NES" and gme_nsf_type->new_emu() is equilvant to new Nsf_Emu (in C++). */ +typedef struct gme_type_t_ const* gme_type_t; +struct gme_type_t_ +{ + const char* system; /* name of system this music file type is generally for */ + int track_count; /* non-zero for formats with a fixed number of tracks */ + Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ + Music_Emu* (*new_info)(); /* Create new info reader for this type */ + + /* internal */ + const char* extension_; + int flags_; +}; + +/* Emulator type constants for each supported file type */ +extern struct gme_type_t_ const gme_ay_type [], gme_gbs_type [], gme_gym_type [], + gme_hes_type [], gme_kss_type [], gme_nsf_type [], gme_nsfe_type [], + gme_sap_type [], gme_spc_type [], gme_vgm_type [], gme_vgz_type []; + +/* Type of this emulator */ +gme_type_t gme_type( Music_Emu const* ); + +/* Pointer to array of all music types, with NULL entry at end. Allows a player linked +to this library to support new music types without having to be updated. */ +gme_type_t const* gme_type_list(); + + +/******** Advanced file loading ********/ + +/* Error returned if file type is not supported */ +extern const char gme_wrong_file_type []; + +/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ); + +/* Determine likely game music type based on first four bytes of file. Returns +string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if +file header is not recognized. */ +const char* gme_identify_header( void const* header ); + +/* Get corresponding music type for file path or extension passed in. */ +gme_type_t gme_identify_extension( const char* path_or_extension ); + +/* Determine file type based on file's extension or header (if extension isn't recognized). +Sets *type_out to type, or 0 if unrecognized or error. */ +gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ); + +/* Create new emulator and set sample rate. Returns NULL if out of memory. If you only need +track information, pass gme_info_only for sample_rate. */ +Music_Emu* gme_new_emu( gme_type_t, long sample_rate ); + +/* Load music file into emulator */ +gme_err_t gme_load_file( Music_Emu*, const char* path ); + +/* Load music file from memory into emulator. Makes a copy of data passed. */ +gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); + +/* Load music file using custom data reader function that will be called to +read file data. Most emulators load the entire file in one read call. */ +typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, long count ); +gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); + +/* Load m3u playlist file from memory (must be done after loading music) */ +gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); + + +/******** User data ********/ + +/* Set/get pointer to data you want to associate with this emulator. +You can use this for whatever you want. */ +void gme_set_user_data( Music_Emu*, void* new_user_data ); +void* gme_user_data( Music_Emu const* ); + +/* Register cleanup function to be called when deleting emulator, or NULL to +clear it. Passes user_data to cleanup function. */ +typedef void (*gme_user_cleanup_t)( void* user_data ); +void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h new file mode 100644 index 00000000..b3d71dad --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/hes_cpu_io.h @@ -0,0 +1,101 @@ + +#include "Hes_Emu.h" + +#include "blargg_source.h" + +int Hes_Emu::cpu_read( hes_addr_t addr ) +{ + check( addr <= 0xFFFF ); + int result = *cpu::get_code( addr ); + if ( mmr [addr >> page_shift] == 0xFF ) + result = cpu_read_( addr ); + return result; +} + +void Hes_Emu::cpu_write( hes_addr_t addr, int data ) +{ + check( addr <= 0xFFFF ); + byte* out = write_pages [addr >> page_shift]; + addr &= page_size - 1; + if ( out ) + out [addr] = data; + else if ( mmr [addr >> page_shift] == 0xFF ) + cpu_write_( addr, data ); +} + +inline byte const* Hes_Emu::cpu_set_mmr( int page, int bank ) +{ + write_pages [page] = 0; + if ( bank < 0x80 ) + return rom.at_addr( bank * (blargg_long) page_size ); + + byte* data = 0; + switch ( bank ) + { + case 0xF8: + data = cpu::ram; + break; + + case 0xF9: + case 0xFA: + case 0xFB: + data = &sgx [(bank - 0xF9) * page_size]; + break; + + default: + if ( bank != 0xFF ) + dprintf( "Unmapped bank $%02X\n", bank ); + return rom.unmapped(); + } + + write_pages [page] = data; + return data; +} + +#define CPU_READ_FAST( cpu, addr, time, out ) \ + CPU_READ_FAST_( STATIC_CAST(Hes_Emu*,cpu), addr, time, out ) + +#define CPU_READ_FAST_( cpu, addr, time, out ) \ +{\ + out = READ_PROG( addr );\ + if ( mmr [addr >> page_shift] == 0xFF )\ + {\ + FLUSH_TIME();\ + out = cpu->cpu_read_( addr );\ + CACHE_TIME();\ + }\ +} + +#define CPU_WRITE_FAST( cpu, addr, data, time ) \ + CPU_WRITE_FAST_( STATIC_CAST(Hes_Emu*,cpu), addr, data, time ) + +#define CPU_WRITE_FAST_( cpu, addr, data, time ) \ +{\ + byte* out = cpu->write_pages [addr >> page_shift];\ + addr &= page_size - 1;\ + if ( out )\ + {\ + out [addr] = data;\ + }\ + else if ( mmr [addr >> page_shift] == 0xFF )\ + {\ + FLUSH_TIME();\ + cpu->cpu_write_( addr, data );\ + CACHE_TIME();\ + }\ +} + +#define CPU_READ( cpu, addr, time ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_read( addr ) + +#define CPU_WRITE( cpu, addr, data, time ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_write( addr, data ) + +#define CPU_WRITE_VDP( cpu, addr, data, time ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_write_vdp( addr, data ) + +#define CPU_SET_MMR( cpu, page, bank ) \ + STATIC_CAST(Hes_Emu*,cpu)->cpu_set_mmr( page, bank ) + +#define CPU_DONE( cpu, time, result_out ) \ + result_out = STATIC_CAST(Hes_Emu*,cpu)->cpu_done() diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h new file mode 100644 index 00000000..4bae3793 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/nes_cpu_io.h @@ -0,0 +1,83 @@ + +#include "Nsf_Emu.h" + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" +#endif + +#include "blargg_source.h" + +int Nsf_Emu::cpu_read( nes_addr_t addr ) +{ + int result; + + result = cpu::low_mem [addr & 0x7FF]; + if ( !(addr & 0xE000) ) + goto exit; + + result = *cpu::get_code( addr ); + if ( addr > 0x7FFF ) + goto exit; + + result = sram [addr & (sizeof sram - 1)]; + if ( addr > 0x5FFF ) + goto exit; + + if ( addr == Nes_Apu::status_addr ) + return apu.read_status( cpu::time() ); + + #if !NSF_EMU_APU_ONLY + if ( addr == Nes_Namco_Apu::data_reg_addr && namco ) + return namco->read_data(); + #endif + + result = addr >> 8; // simulate open bus + + if ( addr != 0x2002 ) + dprintf( "Read unmapped $%.4X\n", (unsigned) addr ); + +exit: + return result; +} + +void Nsf_Emu::cpu_write( nes_addr_t addr, int data ) +{ + { + nes_addr_t offset = addr ^ sram_addr; + if ( offset < sizeof sram ) + { + sram [offset] = data; + return; + } + } + { + int temp = addr & 0x7FF; + if ( !(addr & 0xE000) ) + { + cpu::low_mem [temp] = data; + return; + } + } + + if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr ) + { + GME_APU_HOOK( this, addr - Nes_Apu::start_addr, data ); + apu.write_register( cpu::time(), addr, data ); + return; + } + + unsigned bank = addr - bank_select_addr; + if ( bank < bank_count ) + { + blargg_long offset = rom.mask_addr( data * (blargg_long) bank_size ); + if ( offset >= rom.size() ) + set_warning( "Invalid bank" ); + cpu::map_code( (bank + 8) * bank_size, bank_size, rom.at_addr( offset ) ); + return; + } + + cpu_write_misc( addr, data ); +} + +#define CPU_READ( cpu, addr, time ) STATIC_CAST(Nsf_Emu&,*cpu).cpu_read( addr ) +#define CPU_WRITE( cpu, addr, data, time ) STATIC_CAST(Nsf_Emu&,*cpu).cpu_write( addr, data ) diff --git a/plugins/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h b/plugins/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h new file mode 100644 index 00000000..8c2f6dd0 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/gme/sap_cpu_io.h @@ -0,0 +1,26 @@ + +#include "Sap_Emu.h" + +#include "blargg_source.h" + +#define CPU_WRITE( cpu, addr, data, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_write( addr, data ) + +void Sap_Emu::cpu_write( sap_addr_t addr, int data ) +{ + mem.ram [addr] = data; + if ( (addr >> 8) == 0xD2 ) + cpu_write_( addr, data ); +} + +#ifdef NDEBUG + #define CPU_READ( cpu, addr, time ) READ_LOW( addr ) +#else + #define CPU_READ( cpu, addr, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_read( addr ) + + int Sap_Emu::cpu_read( sap_addr_t addr ) + { + if ( (addr & 0xF900) == 0xD000 ) + dprintf( "Unmapped read $%04X\n", addr ); + return mem.ram [addr]; + } +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/license.txt b/plugins/gme/Game_Music_Emu-0.5.2/license.txt new file mode 100644 index 00000000..5faba9d4 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/license.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp b/plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp new file mode 100644 index 00000000..74cb2c32 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.cpp @@ -0,0 +1,198 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Audio_Scope.h" + +#include +#include + +/* Copyright (C) 2005-2006 by Shay Green. Permission is hereby granted, free of +charge, to any person obtaining a copy of this software module and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. THE +SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +int const step_bits = 8; +int const step_unit = 1 << step_bits; +int const erase_color = 1; +int const draw_color = 2; + +Audio_Scope::Audio_Scope() +{ + surface = 0; + buf = 0; +} + +Audio_Scope::~Audio_Scope() +{ + free( buf ); + + if ( surface ) + SDL_FreeSurface( surface ); +} + +const char* Audio_Scope::init( int width, int height ) +{ + assert( height <= 256 ); + assert( !buf ); // can only call init() once + + buf = (byte*) calloc( width * sizeof *buf, 1 ); + if ( !buf ) + return "Out of memory"; + + low_y = 0; + high_y = height; + buf_size = width; + + for ( sample_shift = 6; sample_shift < 14; ) + if ( ((0x7FFFL * 2) >> sample_shift++) < height ) + break; + + v_offset = height / 2 - (0x10000 >> sample_shift); + + screen = SDL_SetVideoMode( width, height, 0, 0 ); + if ( !screen ) + return "Couldn't set video mode"; + + surface = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0 ); + if ( !screen ) + return "Couldn't create surface"; + + static SDL_Color palette [2] = { {0, 0, 0}, {0, 255, 0} }; + SDL_SetColors( surface, palette, 1, 2 ); + + return 0; // success +} + +const char* Audio_Scope::draw( const short* in, long count, double step ) +{ + int low = low_y; + int high = high_y; + + if ( count >= buf_size ) + { + count = buf_size; + low_y = 0x7FFF; + high_y = 0; + } + + if ( SDL_LockSurface( surface ) < 0 ) + return "Couldn't lock surface"; + render( in, count, (long) (step * step_unit) ); + SDL_UnlockSurface( surface ); + + if ( low > low_y ) + low = low_y; + + if ( high < high_y ) + high = high_y; + + SDL_Rect r; + r.x = 0; + r.w = buf_size; + r.y = low + v_offset; + r.h = high - low + 1; + + if ( SDL_BlitSurface( surface, &r, screen, &r ) < 0 ) + return "Blit to screen failed"; + + if ( SDL_Flip( screen ) < 0 ) + return "Couldn't flip screen"; + + return 0; // success +} + +void Audio_Scope::render( short const* in, long count, long step ) +{ + byte* old_pos = buf; + long surface_pitch = surface->pitch; + byte* out = (byte*) surface->pixels + v_offset * surface_pitch; + int old_erase = *old_pos; + int old_draw = 0; + long in_pos = 0; + + int low_y = this->low_y; + int high_y = this->high_y; + int half_step = (step + step_unit / 2) >> (step_bits + 1); + + while ( count-- ) + { + // Line drawing/erasing starts at previous sample and ends one short of + // current sample, except when previous and current are the same. + + // Extra read on the last iteration of line loops will always be at the + // height of the next sample, and thus within the gworld bounds. + + // Erase old line + { + int delta = *old_pos - old_erase; + int offset = old_erase * surface_pitch; + old_erase += delta; + + int next_line = surface_pitch; + if ( delta < 0 ) + { + delta = -delta; + next_line = -surface_pitch; + } + + do + { + out [offset] = erase_color; + offset += next_line; + } + while ( delta-- > 1 ); + } + + // Draw new line and put in old_buf + { + + int in_whole = in_pos >> step_bits; + int sample = (0x7FFF * 2 - in [in_whole] - in [in_whole + half_step]) >> sample_shift; + if ( !in_pos ) + old_draw = sample; + in_pos += step; + + int delta = sample - old_draw; + int offset = old_draw * surface_pitch; + old_draw += delta; + + int next_line = surface_pitch; + if ( delta < 0 ) + { + delta = -delta; + next_line = -surface_pitch; + } + + *old_pos++ = sample; + + // min/max updating can be interleved anywhere + + if ( low_y > sample ) + low_y = sample; + + do + { + out [offset] = draw_color; + offset += next_line; + } + while ( delta-- > 1 ); + + if ( high_y < sample ) + high_y = sample; + } + + out++; + } + + this->low_y = low_y; + this->high_y = high_y; +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h b/plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h new file mode 100644 index 00000000..75334676 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/player/Audio_Scope.h @@ -0,0 +1,36 @@ +// Simple audio waveform scope in a window, using SDL multimedia library + +#ifndef AUDIO_SCOPE_H +#define AUDIO_SCOPE_H + +#include "SDL.h" + +class Audio_Scope { +public: + typedef const char* error_t; + + // Initialize scope window of specified size. Height must be 256 or less. + error_t init( int width, int height ); + + // Draw at most 'count' samples from 'in', skipping 'step' samples after + // each sample drawn. Step can be less than 1.0. + error_t draw( const short* in, long count, double step = 1.0 ); + + Audio_Scope(); + ~Audio_Scope(); + +private: + typedef unsigned char byte; + SDL_Surface* screen; + SDL_Surface* surface; + byte* buf; + int buf_size; + int sample_shift; + int low_y; + int high_y; + int v_offset; + + void render( short const* in, long count, long step ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp b/plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp new file mode 100644 index 00000000..a39819f1 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.cpp @@ -0,0 +1,231 @@ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ + +#include "Music_Player.h" + +#include "gme/Music_Emu.h" + +#include +#include + +/* Copyright (C) 2005-2006 by Shay Green. Permission is hereby granted, free of +charge, to any person obtaining a copy of this software module and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. THE +SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "blargg_source.h" + +// Number of audio buffers per second. Adjust if you encounter audio skipping. +const int fill_rate = 45; + +// Simple sound driver using SDL +typedef void (*sound_callback_t)( void* data, short* out, int count ); +static const char* sound_init( long sample_rate, int buf_size, sound_callback_t, void* data ); +static void sound_start(); +static void sound_stop(); +static void sound_cleanup(); + +Music_Player::Music_Player() +{ + emu_ = 0; + scope_buf = 0; + paused = false; +} + +blargg_err_t Music_Player::init( long rate ) +{ + sample_rate = rate; + + int min_size = sample_rate * 2 / fill_rate; + int buf_size = 512; + while ( buf_size < min_size ) + buf_size *= 2; + + return sound_init( sample_rate, buf_size, fill_buffer, this ); +} + +void Music_Player::stop() +{ + sound_stop(); + delete emu_; + emu_ = 0; +} + +Music_Player::~Music_Player() +{ + stop(); + sound_cleanup(); +} + +blargg_err_t Music_Player::load_file( const char* path ) +{ + stop(); + + RETURN_ERR( gme_open_file( path, &emu_, sample_rate ) ); + + char m3u_path [256 + 5]; + strncpy( m3u_path, path, 256 ); + m3u_path [256] = 0; + char* p = strrchr( m3u_path, '.' ); + if ( !p ) + p = m3u_path + strlen( m3u_path ); + strcpy( p, ".m3u" ); + if ( emu_->load_m3u( m3u_path ) ) { } // ignore error + + return 0; +} + +int Music_Player::track_count() const +{ + return emu_ ? emu_->track_count() : false; +} + +blargg_err_t Music_Player::start_track( int track ) +{ + if ( emu_ ) + { + // Sound must not be running when operating on emulator + sound_stop(); + RETURN_ERR( emu_->start_track( track ) ); + + // Calculate track length + if ( !emu_->track_info( &track_info_ ) ) + { + if ( track_info_.length <= 0 ) + track_info_.length = track_info_.intro_length + + track_info_.loop_length * 2; + } + if ( track_info_.length <= 0 ) + track_info_.length = (long) (2.5 * 60 * 1000); + emu_->set_fade( track_info_.length ); + + paused = false; + sound_start(); + } + return 0; +} + +void Music_Player::pause( int b ) +{ + paused = b; + if ( b ) + sound_stop(); + else + sound_start(); +} + +void Music_Player::suspend() +{ + if ( !paused ) + sound_stop(); +} + +void Music_Player::resume() +{ + if ( !paused ) + sound_start(); +} + +bool Music_Player::track_ended() const +{ + return emu_ ? emu_->track_ended() : false; +} + +void Music_Player::set_stereo_depth( double tempo ) +{ + suspend(); + gme_set_stereo_depth( emu_, tempo ); + resume(); +} + +void Music_Player::set_tempo( double tempo ) +{ + suspend(); + emu_->set_tempo( tempo ); + resume(); +} + +void Music_Player::mute_voices( int mask ) +{ + suspend(); + emu_->mute_voices( mask ); + emu_->ignore_silence( mask != 0 ); + resume(); +} + +void Music_Player::fill_buffer( void* data, sample_t* out, int count ) +{ + Music_Player* self = (Music_Player*) data; + if ( self->emu_ ) + { + if ( self->emu_->play( count, out ) ) { } // ignore error + + if ( self->scope_buf ) + memcpy( self->scope_buf, out, self->scope_buf_size * sizeof *self->scope_buf ); + } +} + +// Sound output driver using SDL + +#include "SDL.h" + +static sound_callback_t sound_callback; +static void* sound_callback_data; + +static void sdl_callback( void* data, Uint8* out, int count ) +{ + if ( sound_callback ) + sound_callback( sound_callback_data, (short*) out, count / 2 ); +} + +static const char* sound_init( long sample_rate, int buf_size, + sound_callback_t cb, void* data ) +{ + sound_callback = cb; + sound_callback_data = data; + + static SDL_AudioSpec as; // making static clears all fields to 0 + as.freq = sample_rate; + as.format = AUDIO_S16SYS; + as.channels = 2; + as.callback = sdl_callback; + as.samples = buf_size; + if ( SDL_OpenAudio( &as, 0 ) < 0 ) + { + const char* err = SDL_GetError(); + if ( !err ) + err = "Couldn't open SDL audio"; + return err; + } + + return 0; +} + +static void sound_start() +{ + SDL_PauseAudio( false ); +} + +static void sound_stop() +{ + SDL_PauseAudio( true ); + + // be sure audio thread is not active + SDL_LockAudio(); + SDL_UnlockAudio(); +} + +static void sound_cleanup() +{ + sound_stop(); + SDL_CloseAudio(); +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.h b/plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.h new file mode 100644 index 00000000..7a573c45 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/player/Music_Player.h @@ -0,0 +1,69 @@ +// Simple game music file player + +// Game_Music_Emu 0.5.2 +#ifndef MUSIC_PLAYER_H +#define MUSIC_PLAYER_H + +#include "gme/Music_Emu.h" + +class Music_Player { +public: + // Initialize player and set sample rate + blargg_err_t init( long sample_rate = 44100 ); + + // Load game music file. NULL on success, otherwise error string. + blargg_err_t load_file( const char* path ); + + // (Re)start playing track. Tracks are numbered from 0 to track_count() - 1. + blargg_err_t start_track( int track ); + + // Stop playing current file + void stop(); + +// Optional functions + + // Number of tracks in current file, or 0 if no file loaded. + int track_count() const; + + // Info for current track + track_info_t const& track_info() const { return track_info_; } + + // Pause/resume playing current track. + void pause( int ); + + // True if track ended + bool track_ended() const; + + // Pointer to emulator + Music_Emu* emu() const { return emu_; } + + // Set stereo depth, where 0.0 = none and 1.0 = maximum + void set_stereo_depth( double ); + + // Set tempo, where 0.5 = half speed, 1.0 = normal, 2.0 = double speed + void set_tempo( double ); + + // Set voice muting bitmask + void mute_voices( int ); + + // Set buffer to copy samples from each buffer into, or NULL to disable + typedef short sample_t; + void set_scope_buffer( sample_t* buf, int size ) { scope_buf = buf; scope_buf_size = size; } + +public: + Music_Player(); + ~Music_Player(); +private: + Music_Emu* emu_; + sample_t* scope_buf; + long sample_rate; + int scope_buf_size; + bool paused; + track_info_t track_info_; + + void suspend(); + void resume(); + static void fill_buffer( void*, sample_t*, int ); +}; + +#endif diff --git a/plugins/gme/Game_Music_Emu-0.5.2/player/player.cpp b/plugins/gme/Game_Music_Emu-0.5.2/player/player.cpp new file mode 100644 index 00000000..a0685997 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/player/player.cpp @@ -0,0 +1,213 @@ +/* How to play game music files with Music_Player (requires SDL library) + +Run program with path to a game music file. + +Left/Right Change track +Space Pause/unpause +E Normal/slight stereo echo/more stereo echo +-/= Adjust tempo +1-9 Toggle channel on/off +0 Reset tempo and turn channels back on */ + +int const scope_width = 512; + +#include "Music_Player.h" +#include "Audio_Scope.h" + +#include +#include +#include +#include "SDL.h" + +void handle_error( const char* ); + +static bool paused; +static Audio_Scope* scope; +static Music_Player* player; +static short scope_buf [scope_width * 2]; + +static void init() +{ + // Start SDL + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) + exit( EXIT_FAILURE ); + atexit( SDL_Quit ); + SDL_EnableKeyRepeat( 500, 80 ); + + // Init scope + scope = new Audio_Scope; + if ( !scope ) + handle_error( "Out of memory" ); + if ( scope->init( scope_width, 256 ) ) + handle_error( "Couldn't initialize scope" ); + memset( scope_buf, 0, sizeof scope_buf ); + + // Create player + player = new Music_Player; + if ( !player ) + handle_error( "Out of memory" ); + handle_error( player->init() ); + player->set_scope_buffer( scope_buf, scope_width * 2 ); +} + +static void start_track( int track, const char* path ) +{ + paused = false; + handle_error( player->start_track( track - 1 ) ); + + // update window title with track info + + long seconds = player->track_info().length / 1000; + const char* game = player->track_info().game; + if ( !*game ) + { + // extract filename + game = strrchr( path, '\\' ); // DOS + if ( !game ) + game = strrchr( path, '/' ); // UNIX + if ( !game ) + game = path; + else + game++; // skip path separator + } + + char title [512]; + sprintf( title, "%s: %d/%d %s (%ld:%02ld)", + game, track, player->track_count(), player->track_info().song, + seconds / 60, seconds % 60 ); + SDL_WM_SetCaption( title, title ); +} + +int main( int argc, char** argv ) +{ + init(); + + // Load file + const char* path = (argc > 1 ? argv [argc - 1] : "test.nsf"); + handle_error( player->load_file( path ) ); + start_track( 1, path ); + + // Main loop + int track = 1; + double tempo = 1.0; + bool running = true; + double stereo_depth = 0.0; + int muting_mask = 0; + while ( running ) + { + SDL_Delay( 1000 / 100 ); + + // Update scope + scope->draw( scope_buf, scope_width, 2 ); + + // Automatically go to next track when current one ends + if ( player->track_ended() ) + { + if ( track < player->track_count() ) + start_track( ++track, path ); + else + player->pause( paused = true ); + } + + // Handle keyboard input + SDL_Event e; + while ( SDL_PollEvent( &e ) ) + { + switch ( e.type ) + { + case SDL_QUIT: + running = false; + break; + + case SDL_KEYDOWN: + int key = e.key.keysym.sym; + switch ( key ) + { + case SDLK_q: + case SDLK_ESCAPE: // quit + running = false; + break; + + case SDLK_LEFT: // prev track + if ( !paused && !--track ) + track = 1; + start_track( track, path ); + break; + + case SDLK_RIGHT: // next track + if ( track < player->track_count() ) + start_track( ++track, path ); + break; + + case SDLK_MINUS: // reduce tempo + tempo -= 0.1; + if ( tempo < 0.1 ) + tempo = 0.1; + player->set_tempo( tempo ); + break; + + case SDLK_EQUALS: // increase tempo + tempo += 0.1; + if ( tempo > 2.0 ) + tempo = 2.0; + player->set_tempo( tempo ); + break; + + case SDLK_SPACE: // toggle pause + paused = !paused; + player->pause( paused ); + break; + + case SDLK_e: // toggle echo + stereo_depth += 0.2; + if ( stereo_depth > 0.5 ) + stereo_depth = 0; + player->set_stereo_depth( stereo_depth ); + break; + + case SDLK_0: // reset tempo and muting + tempo = 1.0; + muting_mask = 0; + player->set_tempo( tempo ); + player->mute_voices( muting_mask ); + break; + + default: + if ( SDLK_1 <= key && key <= SDLK_9 ) // toggle muting + { + muting_mask ^= 1 << (key - SDLK_1); + player->mute_voices( muting_mask ); + } + } + } + } + } + + // Cleanup + delete player; + delete scope; + + return 0; +} + +void handle_error( const char* error ) +{ + if ( error ) + { + // put error in window title + char str [256]; + sprintf( str, "Error: %s", error ); + fprintf( stderr, str ); + SDL_WM_SetCaption( str, str ); + + // wait for keyboard or mouse activity + SDL_Event e; + do + { + while ( !SDL_PollEvent( &e ) ) { } + } + while ( e.type != SDL_QUIT && e.type != SDL_KEYDOWN && e.type != SDL_MOUSEBUTTONDOWN ); + + exit( EXIT_FAILURE ); + } +} diff --git a/plugins/gme/Game_Music_Emu-0.5.2/readme.txt b/plugins/gme/Game_Music_Emu-0.5.2/readme.txt new file mode 100644 index 00000000..e3470bfa --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/readme.txt @@ -0,0 +1,205 @@ +Game_Music_Emu 0.5.2: Game Music Emulators +------------------------------------------ +Game_Music_Emu is a collection of video game music file emulators that +support the following formats and systems: + +AY ZX Spectrum/Amstrad CPC +GBS Nintendo Game Boy +GYM Sega Genesis/Mega Drive +HES NEC TurboGrafx-16/PC Engine +KSS MSX Home Computer/other Z80 systems (doesn't support FM sound) +NSF/NSFE Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound) +SAP Atari systems using POKEY sound chip +SPC Super Nintendo/Super Famicom +VGM/VGZ Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro + +Features: +* Can be used in C and C++ code +* High emphasis has been placed on making the library very easy to use +* One set of common functions work with all emulators the same way +* Several code examples, including music player using SDL +* Portable code for use on any system with modern or older C++ compilers +* Adjustable output sample rate using quality band-limited resampling +* Uniform access to text information fields and track timing information +* End-of-track fading and automatic look ahead silence detection +* Treble/bass and stereo echo for AY/GBS/HES/KSS/NSF/NSFE/SAP/VGM +* Tempo can be adjusted and individual voices can be muted while playing +* Can read music data from file, memory, or custom reader function/class +* Can access track information without having to load into full emulator +* M3U track listing support for multi-track formats +* Modular design allows elimination of unneeded emulators/features + +This library has been used in game music players for Windows, Linux on +several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable, +GP2X, and Nintendo DS. + +Author : Shay Green +Website: http://www.slack.net/~ant/ +Forum : http://groups.google.com/group/blargg-sound-libs +License: GNU Lesser General Public License (LGPL) + + +Getting Started +--------------- +Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and +all source files in gme/. Be sure "test.nsf" is in the same directory. +Running the program should generate the recording "out.wav". + +Read gme.txt for more information. Post to the discussion forum for +assistance. + + +Files +----- +gme.txt General notes about the library +changes.txt Changes made since previous releases +design.txt Library design notes +license.txt GNU Lesser General Public License + +test.nsf Test file for NSF emulator +test.m3u Test m3u playlist for features.c demo + +demo/ + basics.c Records NSF file to wave sound file + cpp_basics.cpp C++ version of basics.c + features.c Demonstrates many additional features + Wave_Writer.h WAVE sound file writer used for demo output + Wave_Writer.cpp + +player/ Player using the SDL multimedia library + player.cpp Simple music player with waveform display + Music_Player.cpp Stand alone player for background music + Music_Player.h + Audio_Scope.cpp Audio waveform scope + Audio_Scope.h + +gme/ + blargg_config.h Library configuration (modify this file as needed) + + gme.h C interface (also usable in C++, and simpler too) + gme.cpp + + Gme_File.h File loading and track information + Music_Emu.h Track playback and adjustments + Data_Reader.h Custom data readers + + Effects_Buffer.h Sound buffer with stereo echo and panning + Effects_Buffer.cpp + + M3u_Playlist.h M3U playlist support + M3u_Playlist.cpp + + Ay_Emu.h ZX Spectrum AY emulator + Ay_Emu.cpp + Ay_Apu.cpp + Ay_Apu.h + Ay_Cpu.cpp + Ay_Cpu.h + + Gbs_Emu.h Nintendo Game Boy GBS emulator + Gbs_Emu.cpp + Gb_Apu.cpp + Gb_Apu.h + Gb_Cpu.cpp + Gb_Cpu.h + gb_cpu_io.h + Gb_Oscs.cpp + Gb_Oscs.h + + Hes_Emu.h TurboGrafx-16/PC Engine HES emulator + Hes_Apu.cpp + Hes_Apu.h + Hes_Cpu.cpp + Hes_Cpu.h + hes_cpu_io.h + Hes_Emu.cpp + + Kss_Emu.h MSX Home Computer/other Z80 systems KSS emulator + Kss_Emu.cpp + Kss_Cpu.cpp + Kss_Cpu.h + Kss_Scc_Apu.cpp + Kss_Scc_Apu.h + Ay_Apu.h + Ay_Apu.cpp + Sms_Apu.h + Sms_Apu.cpp + Sms_Oscs.h + + Nsf_Emu.h Nintendo NES NSF/NSFE emulator + Nsf_Emu.cpp + Nes_Apu.cpp + Nes_Apu.h + Nes_Cpu.cpp + Nes_Cpu.h + nes_cpu_io.h + Nes_Oscs.cpp + Nes_Oscs.h + Nes_Fme7_Apu.cpp + Nes_Fme7_Apu.h + Nes_Namco_Apu.cpp + Nes_Namco_Apu.h + Nes_Vrc6_Apu.cpp + Nes_Vrc6_Apu.h + Nsfe_Emu.h NSFE support + Nsfe_Emu.cpp + + Spc_Emu.h Super Nintendo SPC emulator + Spc_Emu.cpp + Snes_Spc.cpp + Snes_Spc.h + Spc_Cpu.cpp + Spc_Cpu.h + Spc_Dsp.cpp + Spc_Dsp.h + Fir_Resampler.cpp + Fir_Resampler.h + + Sap_Emu.h Atari SAP emulator + Sap_Emu.cpp + Sap_Apu.cpp + Sap_Apu.h + Sap_Cpu.cpp + Sap_Cpu.h + sap_cpu_io.h + + Vgm_Emu.h Sega VGM emulator + Vgm_Emu_Impl.cpp + Vgm_Emu_Impl.h + Vgm_Emu.cpp + Ym2413_Emu.cpp + Ym2413_Emu.h + Gym_Emu.h Sega Genesis GYM emulator + Gym_Emu.cpp + Sms_Apu.cpp Common Sega emulator files + Sms_Apu.h + Sms_Oscs.h + Ym2612_Emu.cpp + Ym2612_Emu.h + Dual_Resampler.cpp + Dual_Resampler.h + Fir_Resampler.cpp + Fir_Resampler.h + + blargg_common.h Common files needed by all emulators + blargg_endian.h + blargg_source.h + Blip_Buffer.cpp + Blip_Buffer.h + Gme_File.cpp + Music_Emu.cpp + Classic_Emu.h + Classic_Emu.cpp + Multi_Buffer.h + Multi_Buffer.cpp + Data_Reader.cpp + + +Legal +----- +Game_Music_Emu library copyright (C) 2003-2006 Shay Green. +SNES SPC DSP emulator based on OpenSPC, copyright (C) 2002 Brad Martin. +Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville. + +-- +Shay Green diff --git a/plugins/gme/Game_Music_Emu-0.5.2/test.m3u b/plugins/gme/Game_Music_Emu-0.5.2/test.m3u new file mode 100644 index 00000000..fd46bfe1 --- /dev/null +++ b/plugins/gme/Game_Music_Emu-0.5.2/test.m3u @@ -0,0 +1,2 @@ +# filename,track number,track name,track time +test.nsf,$00,BGM C,1:16 diff --git a/plugins/gme/Game_Music_Emu-0.5.2/test.nsf b/plugins/gme/Game_Music_Emu-0.5.2/test.nsf new file mode 100644 index 00000000..da5fcedd Binary files /dev/null and b/plugins/gme/Game_Music_Emu-0.5.2/test.nsf differ diff --git a/plugins/gme/Makefile.am b/plugins/gme/Makefile.am new file mode 100644 index 00000000..90c9209e --- /dev/null +++ b/plugins/gme/Makefile.am @@ -0,0 +1,12 @@ +gmepath=@top_srcdir@/plugins/gme/Game_Music_Emu-0.5.2 +SUBDIRS = Game_Music_Emu-0.5.2 Game_Music_Emu-0.5.2/gme + +gmedir = $(libdir)/$(PACKAGE) + +pkglib_LTLIBRARIES = gme.la + +gme_la_SOURCES = cgme.c +gme_la_LDFLAGS = -module + +gme_la_LIBADD = $(LDADD) $(gmepath)/gme/libgme.a +AM_CFLAGS = $(CFLAGS) -I$(gmepath) -std=c99 -fPIC diff --git a/plugins/gme/cgme.c b/plugins/gme/cgme.c new file mode 100644 index 00000000..2bed740c --- /dev/null +++ b/plugins/gme/cgme.c @@ -0,0 +1,248 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko + + 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, see . +*/ +#include +#include +#include +#include "gme/gme.h" +#include "deadbeef.h" + +static DB_decoder_t plugin; +static DB_functions_t *deadbeef; + +typedef struct { + DB_fileinfo_t info; + Music_Emu *emu; + int reallength; + uint32_t cgme_voicemask; + float duration; // of current song +} gme_info_t; + +static DB_fileinfo_t * +cgme_init (DB_playItem_t *it) { + DB_fileinfo_t *_info = malloc (sizeof (gme_info_t)); + gme_info_t *info = (gme_info_t*)_info; + memset (_info, 0, sizeof (gme_info_t)); + int samplerate = deadbeef->conf_get_int ("synth.samplerate", 48000); + if (gme_open_file (it->fname, &info->emu, samplerate)) { + plugin.free (_info); + return NULL; + } + gme_mute_voices (info->emu, info->cgme_voicemask); + gme_start_track (info->emu, it->tracknum); + + track_info_t inf; + + gme_track_info (info->emu, &inf, it->tracknum); + + _info->plugin = &plugin; + _info->bps = 16; + _info->channels = 2; + _info->samplerate = samplerate; + info->duration = deadbeef->pl_get_item_duration (it); + info->reallength = inf.length; + _info->readpos = 0; + return _info; +} + +static void +cgme_free (DB_fileinfo_t *_info) { + gme_info_t *info = (gme_info_t*)_info; + if (info->emu) { + gme_delete (info->emu); + } + free (info); +} + +static int +cgme_read (DB_fileinfo_t *_info, char *bytes, int size) { + gme_info_t *info = (gme_info_t*)_info; + float t = (size/4) / (float)_info->samplerate; + if (_info->readpos + t >= info->duration) { + t = info->duration - _info->readpos; + if (t <= 0) { + return 0; + } + // DON'T ajust size, buffer must always be po2 + //size = t * (float)info->samplerate * 4; + } + if (gme_play (info->emu, size/2, (short*)bytes)) { + return 0; + } + _info->readpos += t; + if (info->reallength == -1) { + if (gme_track_ended (info->emu)) { + return 0; + } + } + return size; +} + +static int +cgme_seek (DB_fileinfo_t *_info, float time) { + gme_info_t *info = (gme_info_t*)_info; + if (gme_seek (info->emu, (long)(time * 1000))) { + return -1; + } + _info->readpos = time; + return 0; +} + +static DB_playItem_t * +cgme_insert (DB_playItem_t *after, const char *fname) { + Music_Emu *emu; + if (!gme_open_file (fname, &emu, gme_info_only)) { + int cnt = gme_track_count (emu); + for (int i = 0; i < cnt; i++) { + track_info_t inf; + const char *ret = gme_track_info (emu, &inf, i); + if (!ret) { + DB_playItem_t *it = deadbeef->pl_item_alloc (); + it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); + it->fname = strdup (fname); + char str[1024]; + if (inf.song[0]) { + snprintf (str, 1024, "%d %s - %s", i, inf.game, inf.song); + } + else { + snprintf (str, 1024, "%d %s - ?", i, inf.game); + } + it->tracknum = i; + + // add metadata + deadbeef->pl_add_meta (it, "system", inf.system); + deadbeef->pl_add_meta (it, "album", inf.game); + int tl = sizeof (inf.song); + int n; + for (n = 0; i < tl && inf.song[n] && inf.song[n] == ' '; n++); + if (n == tl || !inf.song[n]) { + deadbeef->pl_add_meta (it, "title", NULL); + } + else { + deadbeef->pl_add_meta (it, "title", inf.song); + } + deadbeef->pl_add_meta (it, "artist", inf.author); + deadbeef->pl_add_meta (it, "copyright", inf.copyright); + deadbeef->pl_add_meta (it, "comment", inf.comment); + deadbeef->pl_add_meta (it, "dumper", inf.dumper); + char trk[10]; + snprintf (trk, 10, "%d", i+1); + deadbeef->pl_add_meta (it, "track", trk); + if (inf.length == -1) { + float songlength = deadbeef->conf_get_float ("gme.songlength", 3); + deadbeef->pl_set_item_duration (it, songlength * 60.f); + } + else { + deadbeef->pl_set_item_duration (it, (float)inf.length/1000.f); + } + const char *ext = fname + strlen (fname) - 1; + while (ext >= fname && *ext != '.') { + ext--; + } + it->filetype = NULL; + if (*ext == '.') { + ext++; + for (int i = 0; plugin.exts[i]; i++) { + if (!strcasecmp (ext, plugin.exts[i])) { + it->filetype = plugin.exts[i]; + } + } + } + after = deadbeef->pl_insert_item (after, it); + deadbeef->pl_item_unref (it); + } + else { + printf ("gme error: %s\n", ret); + } + } + if (emu) { + gme_delete (emu); + } + } + else { + printf ("error adding %s\n", fname); + } + return after; +} + +static const char * exts[]= +{ + "ay","gbs","gym","hes","kss","nsf","nsfe","sap","spc","vgm","vgz",NULL +}; + +#if 0 +static int +cgme_numvoices (void) { + if (!emu) { + return 0; + } + return gme_voice_count (emu); +} + +static void +cgme_mutevoice (int voice, int mute) { + cgme_voicemask &= ~ (1<